-- Leo's gemini proxy

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

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

commit 058b69e7383cffafcecf0121046c4d83114cf2cb

Author: Julien Blanchard <julien@sideburns.eu>

Date: Tue Dec 31 18:41:24 2019 +0100


Color support


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

index 7a98f60..302fa4a 100644

--- a/Cargo.lock

+++ b/Cargo.lock

@@ -9,6 +9,14 @@ dependencies = [

]


[[package]]

+name = "ansi-parser"

+version = "0.6.5"

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

+dependencies = [

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

+]

+

+[[package]]

name = "arrayref"

version = "0.3.5"

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

@@ -130,6 +138,7 @@ dependencies = [

name = "castor"

version = "0.1.0"

dependencies = [

+ "ansi-parser 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",

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

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

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

@@ -141,6 +150,7 @@ dependencies = [

"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)",

+ "percent-encoding 2.1.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)",

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

@@ -542,6 +552,15 @@ dependencies = [

]


[[package]]

+name = "nom"

+version = "4.2.3"

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

+dependencies = [

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

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

+]

+

+[[package]]

name = "open"

version = "1.3.2"

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

@@ -898,6 +917,11 @@ version = "0.2.8"

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


[[package]]

+name = "version_check"

+version = "0.1.5"

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

+

+[[package]]

name = "wasi"

version = "0.7.0"

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

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


[metadata]

"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"

+"checksum ansi-parser 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "761ac675f1638a6a49e26f6ac3a4067ca3fefa8029816ae4ef8d3fa3d06a5194"

"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"

"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"

"checksum atk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "444daefa55f229af145ea58d77efd23725024ee1f6f3102743709aa6b18c663e"

@@ -977,6 +1002,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 nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"

"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"

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

"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"

"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"

"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"

+"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"

"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"

"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"

"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

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

index 0ba960a..7f4c5eb 100644

--- a/Cargo.toml

+++ b/Cargo.toml

@@ -20,6 +20,8 @@ url = "*"

tempfile = "*"

dirs = "*"

lazy_static = "*"

+ansi-parser = "0.6.5"

+percent-encoding="*"


[dependencies.gtk]

version = "0.8.0"

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

index 8d5ed0d..7ab2537 100644

--- a/src/client.rs

+++ b/src/client.rs

@@ -1,90 +1,11 @@

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

+use std::io::{Write};

use tempfile::NamedTempFile;


-use native_tls::TlsConnector;

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

-use std::time::Duration;

-

-use crate::Gemini;

-use crate::Gopher;

-use crate::Protocol;

-


pub trait Client {

fn get_data(&self) -> Result<(Option<Vec<u8>>, Vec<u8>), String>;

}


-impl Client for Gemini {

- fn get_data(&self) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

- let url = self.get_source_url();

- let host = url.host_str().unwrap();

- let urlf = format!("{}:1965", host);

-

- let mut builder = TlsConnector::builder();

- builder.danger_accept_invalid_hostnames(true);

- builder.danger_accept_invalid_certs(true);

- let connector = builder.build().unwrap();

-

- match urlf.to_socket_addrs() {

- Ok(mut addrs_iter) => match addrs_iter.next() {

- Some(socket_addr) => {

- let stream = TcpStream::connect_timeout(&socket_addr, Duration::new(5, 0));

-

- match stream {

- Ok(stream) => {

- let mstream = connector.connect(&host, stream);

-

- match mstream {

- Ok(mut stream) => {

- let url = format!("{}\r\n", url);

- stream.write_all(url.as_bytes()).unwrap();

- let mut res = vec![];

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

-

- let clrf_idx = find_clrf(&res);

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

-

- Ok((Some(res), content))

- }

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

- }

- }

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

- }

- }

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

- },

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

- }

- }

-}

-

-impl Client for Gopher {

- fn get_data(&self) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

- let url = self.get_source_url();

- let host = url.host_str().unwrap();

- let urlf = format!("{}:70", host);

- println!("{:?}", url.path());

-

- match TcpStream::connect(&urlf) {

- Ok(mut stream) => {

- let mut url = format!("{}\r\n", url.path());

- let url = if url.starts_with("/0/") || url.starts_with("/1/") {

- url.split_off(2)

- } else {

- url

- };

- stream.write_all(url.as_bytes()).unwrap();

- let mut res = vec![];

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

-

- Ok((None, res))

- }

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

- }

- }

-}

-

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

let path = write_tmp_file(content);

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

@@ -96,8 +17,3 @@ fn write_tmp_file(content: Vec<u8>) -> std::path::PathBuf {

let (_file, path) = tmp_file.keep().unwrap();

path

}

-

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

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

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

-}

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

new file mode 100644

index 0000000..c4cc60e

--- /dev/null

+++ b/src/colors.rs

@@ -0,0 +1,294 @@

+pub mod colors {

+ use ansi_parser::AnsiSequence;

+ use ansi_parser::{AnsiParser, Output};

+

+ extern crate regex;

+ use regex::Regex;

+

+ const COLOR_CODE_REGEX: &str = r"(?-u)\u{1b}\[[\d|;]+m";

+

+ pub fn cleanup(line: &str) -> String {

+ let color_code_regexp = Regex::new(COLOR_CODE_REGEX).unwrap();

+ let line = color_code_regexp.replace_all(line, "");

+ String::from(line)

+ }

+

+ pub fn colorize(line: &str) -> String {

+ let parsed: Vec<Output> = line.ansi_parse().collect();

+

+ let mut s = String::new();

+ for p in parsed {

+ match p {

+ Output::Escape(AnsiSequence::SetGraphicsMode(colors)) => {

+ if colors.len() == 1 {

+ s.push_str("");

+ } else {

+ let color = colors.last().unwrap();

+ s.push_str(&format!("<span foreground={:?}>", ansi_color_to_hex(*color)))

+ }

+ }

+ Output::TextBlock(text) => {

+ if s.ends_with('>') {

+ s.push_str(&format!("{}</span>", text))

+ } else {

+ s.push_str(text)

+ }

+ }

+ _ => s.push_str("something else"),

+ }

+ }

+ s

+ }

+

+ fn ansi_color_to_hex(color: u32) -> &'static str {

+ match color {

+ 10 => "#00ff00",

+ 11 => "#ffff00",

+ 12 => "#0000ff",

+ 13 => "#ff00ff",

+ 14 => "#00ffff",

+ 15 => "#ffffff",

+ 16 => "#000000",

+ 17 => "#00005f",

+ 18 => "#000087",

+ 19 => "#0000af",

+ 20 => "#0000d7",

+ 21 => "#0000ff",

+ 22 => "#005f00",

+ 23 => "#005f5f",

+ 24 => "#005f87",

+ 25 => "#005faf",

+ 26 => "#005fd7",

+ 27 => "#005fff",

+ 28 => "#008700",

+ 29 => "#00875f",

+ 30 => "#008787",

+ 31 => "#0087af",

+ 32 => "#0087d7",

+ 33 => "#0087ff",

+ 34 => "#00af00",

+ 35 => "#00af5f",

+ 36 => "#00af87",

+ 37 => "#00afaf",

+ 38 => "#00afd7",

+ 39 => "#00afff",

+ 40 => "#00d700",

+ 41 => "#00d75f",

+ 42 => "#00d787",

+ 43 => "#00d7af",

+ 44 => "#00d7d7",

+ 45 => "#00d7ff",

+ 46 => "#00ff00",

+ 47 => "#00ff5f",

+ 48 => "#00ff87",

+ 49 => "#00ffaf",

+ 50 => "#00ffd7",

+ 51 => "#00ffff",

+ 52 => "#5f0000",

+ 53 => "#5f005f",

+ 54 => "#5f0087",

+ 55 => "#5f00af",

+ 56 => "#5f00d7",

+ 57 => "#5f00ff",

+ 58 => "#5f5f00",

+ 59 => "#5f5f5f",

+ 60 => "#5f5f87",

+ 61 => "#5f5faf",

+ 62 => "#5f5fd7",

+ 63 => "#5f5fff",

+ 64 => "#5f8700",

+ 65 => "#5f875f",

+ 66 => "#5f8787",

+ 67 => "#5f87af",

+ 68 => "#5f87d7",

+ 69 => "#5f87ff",

+ 70 => "#5faf00",

+ 71 => "#5faf5f",

+ 72 => "#5faf87",

+ 73 => "#5fafaf",

+ 74 => "#5fafd7",

+ 75 => "#5fafff",

+ 76 => "#5fd700",

+ 77 => "#5fd75f",

+ 78 => "#5fd787",

+ 79 => "#5fd7af",

+ 80 => "#5fd7d7",

+ 81 => "#5fd7ff",

+ 82 => "#5fff00",

+ 83 => "#5fff5f",

+ 84 => "#5fff87",

+ 85 => "#5fffaf",

+ 86 => "#5fffd7",

+ 87 => "#5fffff",

+ 88 => "#870000",

+ 89 => "#87005f",

+ 90 => "#870087",

+ 91 => "#8700af",

+ 92 => "#8700d7",

+ 93 => "#8700ff",

+ 94 => "#875f00",

+ 95 => "#875f5f",

+ 96 => "#875f87",

+ 97 => "#875faf",

+ 98 => "#875fd7",

+ 99 => "#875fff",

+ 100 => "#878700",

+ 101 => "#87875f",

+ 102 => "#878787",

+ 103 => "#8787af",

+ 104 => "#8787d7",

+ 105 => "#8787ff",

+ 106 => "#87af00",

+ 107 => "#87af5f",

+ 108 => "#87af87",

+ 109 => "#87afaf",

+ 110 => "#87afd7",

+ 111 => "#87afff",

+ 112 => "#87d700",

+ 113 => "#87d75f",

+ 114 => "#87d787",

+ 115 => "#87d7af",

+ 116 => "#87d7d7",

+ 117 => "#87d7ff",

+ 118 => "#87ff00",

+ 119 => "#87ff5f",

+ 120 => "#87ff87",

+ 121 => "#87ffaf",

+ 122 => "#87ffd7",

+ 123 => "#87ffff",

+ 124 => "#af0000",

+ 125 => "#af005f",

+ 126 => "#af0087",

+ 127 => "#af00af",

+ 128 => "#af00d7",

+ 129 => "#af00ff",

+ 130 => "#af5f00",

+ 131 => "#af5f5f",

+ 132 => "#af5f87",

+ 133 => "#af5faf",

+ 134 => "#af5fd7",

+ 135 => "#af5fff",

+ 136 => "#af8700",

+ 137 => "#af875f",

+ 138 => "#af8787",

+ 139 => "#af87af",

+ 140 => "#af87d7",

+ 141 => "#af87ff",

+ 142 => "#afaf00",

+ 143 => "#afaf5f",

+ 144 => "#afaf87",

+ 145 => "#afafaf",

+ 146 => "#afafd7",

+ 147 => "#afafff",

+ 148 => "#afd700",

+ 149 => "#afd75f",

+ 150 => "#afd787",

+ 151 => "#afd7af",

+ 152 => "#afd7d7",

+ 153 => "#afd7ff",

+ 154 => "#afff00",

+ 155 => "#afff5f",

+ 156 => "#afff87",

+ 157 => "#afffaf",

+ 158 => "#afffd7",

+ 159 => "#afffff",

+ 160 => "#d70000",

+ 161 => "#d7005f",

+ 162 => "#d70087",

+ 163 => "#d700af",

+ 164 => "#d700d7",

+ 165 => "#d700ff",

+ 166 => "#d75f00",

+ 167 => "#d75f5f",

+ 168 => "#d75f87",

+ 169 => "#d75faf",

+ 170 => "#d75fd7",

+ 171 => "#d75fff",

+ 172 => "#d78700",

+ 173 => "#d7875f",

+ 174 => "#d78787",

+ 175 => "#d787af",

+ 176 => "#d787d7",

+ 177 => "#d787ff",

+ 178 => "#d7af00",

+ 179 => "#d7af5f",

+ 180 => "#d7af87",

+ 181 => "#d7afaf",

+ 182 => "#d7afd7",

+ 183 => "#d7afff",

+ 184 => "#d7d700",

+ 185 => "#d7d75f",

+ 186 => "#d7d787",

+ 187 => "#d7d7af",

+ 188 => "#d7d7d7",

+ 189 => "#d7d7ff",

+ 190 => "#d7ff00",

+ 191 => "#d7ff5f",

+ 192 => "#d7ff87",

+ 193 => "#d7ffaf",

+ 194 => "#d7ffd7",

+ 195 => "#d7ffff",

+ 196 => "#ff0000",

+ 197 => "#ff005f",

+ 198 => "#ff0087",

+ 199 => "#ff00af",

+ 200 => "#ff00d7",

+ 201 => "#ff00ff",

+ 202 => "#ff5f00",

+ 203 => "#ff5f5f",

+ 204 => "#ff5f87",

+ 205 => "#ff5faf",

+ 206 => "#ff5fd7",

+ 207 => "#ff5fff",

+ 208 => "#ff8700",

+ 209 => "#ff875f",

+ 210 => "#ff8787",

+ 211 => "#ff87af",

+ 212 => "#ff87d7",

+ 213 => "#ff87ff",

+ 214 => "#ffaf00",

+ 215 => "#ffaf5f",

+ 216 => "#ffaf87",

+ 217 => "#ffafaf",

+ 218 => "#ffafd7",

+ 219 => "#ffafff",

+ 220 => "#ffd700",

+ 221 => "#ffd75f",

+ 222 => "#ffd787",

+ 223 => "#ffd7af",

+ 224 => "#ffd7d7",

+ 225 => "#ffd7ff",

+ 226 => "#ffff00",

+ 227 => "#ffff5f",

+ 228 => "#ffff87",

+ 229 => "#ffffaf",

+ 230 => "#ffffd7",

+ 231 => "#ffffff",

+ 232 => "#080808",

+ 233 => "#121212",

+ 234 => "#1c1c1c",

+ 235 => "#262626",

+ 236 => "#303030",

+ 237 => "#3a3a3a",

+ 238 => "#444444",

+ 239 => "#4e4e4e",

+ 240 => "#585858",

+ 241 => "#626262",

+ 242 => "#6c6c6c",

+ 243 => "#767676",

+ 244 => "#808080",

+ 245 => "#8a8a8a",

+ 246 => "#949494",

+ 247 => "#9e9e9e",

+ 248 => "#a8a8a8",

+ 249 => "#b2b2b2",

+ 250 => "#bcbcbc",

+ 251 => "#c6c6c6",

+ 252 => "#d0d0d0",

+ 253 => "#dadada",

+ 254 => "#e4e4e4",

+ 255 => "#eeeeee",

+ _ => "#00ff00",

+ }

+ }

+}

diff --git a/src/gemini/client.rs b/src/gemini/client.rs

index 5b44690..0658ff0 100644

--- a/src/gemini/client.rs

+++ b/src/gemini/client.rs

@@ -1,12 +1,12 @@

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

-use tempfile::NamedTempFile;

-

use native_tls::TlsConnector;

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

use std::time::Duration;


+use crate::protocols::*;


-pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

+pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

+ let url = url.get_source_url();

let host = url.host_str().unwrap();

let urlf = format!("{}:1965", host);


@@ -48,18 +48,6 @@ pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

}

}


-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();

- tmp_file.write_all(&content).unwrap();

- let (_file, path) = tmp_file.keep().unwrap();

- path

-}

-

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

let clrf = b"\r\n";

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

diff --git a/src/gemini/mod.rs b/src/gemini/mod.rs

index 9d35acf..b6c0233 100644

--- a/src/gemini/mod.rs

+++ b/src/gemini/mod.rs

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

pub mod client;

pub mod link;

pub mod parser;

-

-struct GeminiUrl(&'static str);

diff --git a/src/gemini/parser.rs b/src/gemini/parser.rs

index 87f8c8f..af20d20 100644

--- a/src/gemini/parser.rs

+++ b/src/gemini/parser.rs

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

extern crate regex;

use regex::Regex;


+use crate::colors::*;

+

use std::str::FromStr;


@@ -19,7 +21,7 @@ pub struct ParseError;

const H1_REGEX: &str = r"^#\s+(.*)$";

const H2_REGEX: &str = r"^##\s+(.*)$";

const H3_REGEX: &str = r"^###\s+(.*)$";

-const LIST_ITEM_REGEX: &str = r"^\s*\*\s+(.*)$";

+const LIST_ITEM_REGEX: &str = r"^\s*\*\s+([^*]*)$";

const LINK_ITEM_REGEX: &str = r"^=>\s*(\S*)\s*(.*)?$";


impl FromStr for TextElement {

@@ -52,8 +54,7 @@ impl FromStr for TextElement {

} else if link_item_regexp.is_match(&line) {

Ok(TextElement::LinkItem(String::from(line)))

} else {

- let text_line = String::from(line);

- Ok(TextElement::Text(text_line))

+ Ok(TextElement::Text(colors::colorize(line)))

}

}

}

diff --git a/src/gopher/client.rs b/src/gopher/client.rs

index 724a892..5d2c12c 100644

--- a/src/gopher/client.rs

+++ b/src/gopher/client.rs

@@ -1,12 +1,13 @@

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

-use tempfile::NamedTempFile;

-use std::net::{TcpStream};

+use std::net::TcpStream;

+use percent_encoding::percent_decode;


+use crate::Protocol;


-pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

- let host = url.host_str().unwrap();

+pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

+ let url = url.get_source_url();

+ let host = url.host_str().unwrap().to_string();

let urlf = format!("{}:70", host);

- println!("{:?}", url.path());


match TcpStream::connect(&urlf) {

Ok(mut stream) => {

@@ -16,6 +17,7 @@ pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

} else {

url

};

+ let url = percent_decode(url.as_bytes()).decode_utf8().unwrap();

stream.write_all(url.as_bytes()).unwrap();

let mut res = vec![];

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

@@ -25,20 +27,3 @@ pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

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

}

}

-

-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();

- tmp_file.write_all(&content).unwrap();

- let (_file, path) = tmp_file.keep().unwrap();

- path

-}

-

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

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

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

-}

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

index f9fa012..7785575 100644

--- a/src/gopher/link.rs

+++ b/src/gopher/link.rs

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

-extern crate regex;

-use regex::Regex;

use std::str::FromStr;

use url::Url;


@@ -20,23 +18,19 @@ impl FromStr for Link {


// Parses a &str into an instance of 'Link'

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

- let mut els = line.split("\t");

+ let mut els = line.split('\t');


if line.starts_with('0') || line.starts_with('1') {

let label = els.next().expect("no label");

- println!("{:?}", label);

let path = els.next();

- println!("{:?}", path);

let host = els.next();

- println!("{:?}", host);

- let port = els.next();

- println!("{:?}", port);


if let Some(host) = host {

if let Some(path) = path {

let mut text = String::from(label);

text.remove(0);

- Ok(Link::Gopher(Url::parse(&format!("gopher://{}{}", host, path)).unwrap(), String::from(text)))

+ let url = Url::parse(&format!("gopher://{}{}", host, path)).unwrap();

+ Ok(Link::Gopher(url, text))

} else {

Err(ParseError)

}

@@ -45,22 +39,16 @@ impl FromStr for Link {

}

} else if line.starts_with('h') {

let label = els.next();

- println!("{:?}", label);

let url = els.next();

- println!("{:?}", url);


if let Some(label) = label {

if let Some(url) = url {

- let mut text = String::from(label);

- text.remove(0);

- let mut url = String::from(url);

- let url = url.split_off(4);

- let url = Url::parse(&url).unwrap();

- match url.scheme() {

- "gemini" => Ok(Link::Gemini(url, String::from(text))),

- "http" => Ok(Link::Http(url, String::from(text))),

- "https" => Ok(Link::Http(url, String::from(text))),

- _ => Ok(Link::Unknown(url, String::from(text))),

+ let mut label = String::from(label);

+ label.remove(0);

+ let url = String::from(url);

+ match make_link(url, label) {

+ Some(link) => Ok(link),

+ None => Err(ParseError)

}

} else {

Err(ParseError)

@@ -69,54 +57,22 @@ impl FromStr for Link {

Err(ParseError)

}

} else if line.starts_with('[') {

- let label = line;

- println!("{:?}", label);

let mut url = String::from(line);

let url = url.split_off(4);

- println!("{:?}", url);

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

+ let label = String::from(line);


- if let Ok(url) = url {

- println!("SCHEME {}", url.scheme());

- match url.scheme() {

- "gemini" => Ok(Link::Gemini(url, String::from(line))),

- "gopher" => Ok(Link::Gopher(url, String::from(line))),

- "http" => Ok(Link::Http(url, String::from(line))),

- "https" => Ok(Link::Http(url, String::from(line))),

- _ => Ok(Link::Unknown(url, String::from(line))),

- }

- } else {

- Err(ParseError)

+ match make_link(url, label) {

+ Some(link) => Ok(link),

+ None => Err(ParseError)

}

} else {

Err(ParseError)

}

-

- // match link_regexp.captures(&line) {

- // Some(caps) => {

- // let url_str = caps.get(1).map_or("", |m| m.as_str());

- // let label_str = caps.get(2).map_or("", |m| m.as_str());

-

- // let url = url_str.to_string();

- // let label = if label_str.is_empty() {

- // url_str.to_string()

- // } else {

- // label_str.to_string()

- // };

-

- // match make_link(url, label) {

- // Some(link) => Ok(link),

- // None => Err(ParseError),

- // }

- // }

- // None => Err(ParseError),

- // }

}

}


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

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

- match urlp {

+ match Url::parse(&url) {

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

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

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

diff --git a/src/gopher/parser.rs b/src/gopher/parser.rs

index 99e8d98..107deba 100644

--- a/src/gopher/parser.rs

+++ b/src/gopher/parser.rs

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

- use std::str::FromStr;

-

+use std::str::FromStr;

+use crate::colors::*;


pub enum TextElement {

@@ -17,26 +17,32 @@ impl FromStr for TextElement {


// Parses a &str into an instance of 'TextElement'

fn from_str(line: &str) -> Result<TextElement, ParseError> {

- if line.starts_with('0') {

- Ok(TextElement::LinkItem(String::from(line)))

- } else if line.starts_with('1') {

- Ok(TextElement::LinkItem(String::from(line)))

+ if line.starts_with('0') || line.starts_with('1') {

+ Ok(TextElement::LinkItem(colors::cleanup(line)))

} else if line.starts_with('i') {

- let mut els = line.split("\t");

+ let mut els = line.split('\t');

let text = els.next().expect("");

let mut text = String::from(text);

text.remove(0);

- Ok(TextElement::Text(String::from(text)))

+ Ok(TextElement::Text(colors::colorize(&text)))

} else if line.starts_with('h') {

- Ok(TextElement::ExternalLinkItem(String::from(line)))

+ Ok(TextElement::ExternalLinkItem(colors::cleanup(line)))

} else if line.starts_with('I') {

- let mut els = line.split("\t");

+ let mut els = line.split('\t');

let text = els.next().expect("");

let mut text = String::from(text);

text.remove(0);

- Ok(TextElement::Image(String::from(text)))

+ Ok(TextElement::Image(colors::cleanup(&text)))

+ } else if line.contains("://") {

+ if line.contains("gopher://") {

+ Ok(TextElement::LinkItem(String::from(line)))

+ } else if line.contains("http://") || line.contains("https://") {

+ Ok(TextElement::ExternalLinkItem(String::from(line)))

+ } else {

+ Ok(TextElement::Text(colors::colorize(line)))

+ }

} else {

- Ok(TextElement::Text(String::from(line)))

+ Ok(TextElement::Text(colors::colorize(line)))

}

}

}

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

index fb217d1..ab56d35 100644

--- a/src/main.rs

+++ b/src/main.rs

@@ -19,7 +19,7 @@ mod absolute_url;

use absolute_url::AbsoluteUrl;

mod bookmarks;

mod client;

-use client::Client;

+mod colors;

mod gemini;

mod gopher;

mod history;

@@ -129,7 +129,7 @@ fn show_bookmarks(gui: &Arc<Gui>) {

content_view.show_all();

}


-fn visit_url<T: AbsoluteUrl + Client + Protocol>(gui: &Arc<Gui>, url: T) {

+fn visit_url<T: AbsoluteUrl + Protocol>(gui: &Arc<Gui>, url: T) {

{

if url.get_source_str() == "gemini://::bookmarks" {

show_bookmarks(&gui);

@@ -142,7 +142,7 @@ fn visit_url<T: AbsoluteUrl + Client + Protocol>(gui: &Arc<Gui>, url: T) {

let absolute_url = url.to_absolute_url();


match absolute_url {

- Ok(url2) => match url.get_data() {

+ Ok(absolute_url) => match gemini::client::get_data(url) {

Ok((meta, new_content)) => {

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

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

@@ -150,8 +150,8 @@ fn visit_url<T: AbsoluteUrl + Client + Protocol>(gui: &Arc<Gui>, url: T) {

Status::Success(meta) => {

if meta.starts_with("text/") {

// display text files.

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

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

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

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

let content_str =

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


@@ -162,7 +162,7 @@ fn visit_url<T: AbsoluteUrl + Client + Protocol>(gui: &Arc<Gui>, url: T) {

content_view.show_all();

} else {

// download and try to open the rest.

- gemini::client::download(new_content);

+ client::download(new_content);

}

}

Status::Gone(_meta) => {

@@ -180,7 +180,7 @@ fn visit_url<T: AbsoluteUrl + Client + Protocol>(gui: &Arc<Gui>, url: T) {

);

}

Status::Input(message) => {

- input_dialog(&gui, url2, &message);

+ input_dialog(&gui, absolute_url, &message);

}

_ => (),

}

@@ -197,10 +197,10 @@ fn visit_url<T: AbsoluteUrl + Client + Protocol>(gui: &Arc<Gui>, url: T) {

} else {

let absolute_url = url.to_absolute_url();

match absolute_url {

- Ok(url) => match gopher::client::get(&url) {

+ Ok(abs_url) => match gopher::client::get_data(url) {

Ok((_meta, new_content)) => {

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

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

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

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

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


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

@@ -269,7 +269,11 @@ fn draw_gemini_content(

}

Ok(gemini::parser::TextElement::Text(text)) => {

let mut end_iter = buffer.get_end_iter();

- buffer.insert(&mut end_iter, &format!("{}\n", text));

+ buffer.insert_markup(

+ &mut end_iter,

+ &format!("{}\n", text),

+ );

+

}

Ok(gemini::parser::TextElement::LinkItem(link_item)) => {

draw_gemini_link(&gui, link_item);

@@ -290,12 +294,11 @@ fn draw_gopher_content(

for el in content {

match el {

Ok(gopher::parser::TextElement::Text(text)) => {

- if text.contains("://") {

- draw_gopher_link(&gui, text);

- } else {

- let mut end_iter = buffer.get_end_iter();

- buffer.insert(&mut end_iter, &format!("{}\n", text));

- }

+ let mut end_iter = buffer.get_end_iter();

+ buffer.insert_markup(

+ &mut end_iter,

+ &format!("{}\n", text),

+ );

}

Ok(gopher::parser::TextElement::LinkItem(link_item)) => {

draw_gopher_link(&gui, link_item);



---

Served by Pollux Gemini Server.

-- Response ended

-- Page fetched on Sun May 19 07:23:17 2024