Tor support

pull/14/head
dvkt 4 years ago
parent f489f42bf7
commit 17cefcc67f

51
Cargo.lock generated

@ -10,6 +10,11 @@ name = "bitflags"
version = "1.2.1" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "c2-chacha" name = "c2-chacha"
version = "0.2.3" version = "0.2.3"
@ -142,6 +147,7 @@ dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tor-stream 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -240,6 +246,17 @@ dependencies = [
"core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "socks"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.1.0" version = "3.1.0"
@ -264,6 +281,15 @@ dependencies = [
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "tor-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"socks 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.8" version = "0.2.8"
@ -274,6 +300,11 @@ name = "wasi"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.8" version = "0.3.8"
@ -283,6 +314,11 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -293,9 +329,19 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" "checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
@ -324,10 +370,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021"
"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" "checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df"
"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" "checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895"
"checksum socks 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e6a64cfa9346d26e836a49fcc1ddfcb4d3df666b6787b6864db61d4918e1cbc2"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" "checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330"
"checksum tor-stream 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5865109fc90e0bc0f8c299f3794ca0fd5771df988aa6b962d4c9129c39674746"
"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"

@ -34,3 +34,4 @@ dev-version-ext = "dev"
termion = "1.5.3" termion = "1.5.3"
native-tls = "0.2" native-tls = "0.2"
libc = "0.2.66" libc = "0.2.66"
tor-stream = "0.2.0"

@ -31,6 +31,9 @@ the gophersphere.
Options: Options:
-t, --tls Try to open all pages w/ TLS -t, --tls Try to open all pages w/ TLS
-T, --tor Try to open all pages w/ Tor
Set the TOR_PROXY env variable to use
an address other than the default :9050
-r, --raw Print raw Gopher response only -r, --raw Print raw Gopher response only
-p, --print Print rendered Gopher response only -p, --print Print rendered Gopher response only
-l, --local Connect to 127.0.0.1:7070 -l, --local Connect to 127.0.0.1:7070

@ -18,6 +18,9 @@ start gopher://phetch/1/home
# Always use TLS mode. (--tls) # Always use TLS mode. (--tls)
tls no tls no
# Connect using local TOR proxy. (--tor)
tor no
# Always start in wide mode. (--wide) # Always start in wide mode. (--wide)
wide no wide no
"; ";
@ -26,6 +29,7 @@ wide no
pub struct Config { pub struct Config {
pub start: String, pub start: String,
pub tls: bool, pub tls: bool,
pub tor: bool,
pub wide: bool, pub wide: bool,
} }
@ -60,6 +64,7 @@ pub fn parse(text: &str) -> Result<Config> {
let mut cfg = Config { let mut cfg = Config {
start: String::new(), start: String::new(),
tls: false, tls: false,
tor: false,
wide: false, wide: false,
}; };
@ -89,6 +94,7 @@ pub fn parse(text: &str) -> Result<Config> {
match key { match key {
"start" => cfg.start.push_str(val), "start" => cfg.start.push_str(val),
"tls" => cfg.tls = to_bool(val)?, "tls" => cfg.tls = to_bool(val)?,
"tor" => cfg.tor = to_bool(val)?,
"wide" => cfg.wide = to_bool(val)?, "wide" => cfg.wide = to_bool(val)?,
_ => return Err(error!("Unknown key on line {}: {}", linenum, key)), _ => return Err(error!("Unknown key on line {}: {}", linenum, key)),
} }
@ -116,6 +122,7 @@ mod tests {
fn test_parse_default() { fn test_parse_default() {
let config = parse(DEFAULT_CONFIG).expect("Couldn't parse config"); let config = parse(DEFAULT_CONFIG).expect("Couldn't parse config");
assert_eq!(config.tls, false); assert_eq!(config.tls, false);
assert_eq!(config.tor, false);
assert_eq!(config.wide, false); assert_eq!(config.wide, false);
assert_eq!(config.start, "gopher://phetch/1/home"); assert_eq!(config.start, "gopher://phetch/1/home");
} }
@ -148,8 +155,9 @@ mod tests {
#[test] #[test]
fn test_no_or_false() { fn test_no_or_false() {
let cfg = parse("tls false\nwide no").unwrap(); let cfg = parse("tls false\nwide no\ntor n").unwrap();
assert_eq!(cfg.tls, false); assert_eq!(cfg.tls, false);
assert_eq!(cfg.tor, false);
assert_eq!(cfg.wide, false); assert_eq!(cfg.wide, false);
} }
#[test] #[test]

@ -1,4 +1,5 @@
use std::{ use std::{
env,
io::{Read, Result, Write}, io::{Read, Result, Write},
net::TcpStream, net::TcpStream,
net::ToSocketAddrs, net::ToSocketAddrs,
@ -6,6 +7,7 @@ use std::{
time::Duration, time::Duration,
}; };
use termion::input::TermRead; use termion::input::TermRead;
use tor_stream::TorStream;
#[cfg(not(feature = "disable-tls"))] #[cfg(not(feature = "disable-tls"))]
use native_tls::TlsConnector; use native_tls::TlsConnector;
@ -51,15 +53,21 @@ pub const TCP_TIMEOUT_DURATION: Duration = Duration::from_secs(TCP_TIMEOUT_IN_SE
/// Fetches a gopher URL and returns a tuple of: /// Fetches a gopher URL and returns a tuple of:
/// (did tls work?, raw Gopher response) /// (did tls work?, raw Gopher response)
pub fn fetch_url(url: &str, try_tls: bool) -> Result<(bool, String)> { pub fn fetch_url(url: &str, tls: bool, tor: bool) -> Result<(bool, String)> {
let (_, host, port, sel) = parse_url(url); let (_, host, port, sel) = parse_url(url);
fetch(host, port, sel, try_tls) fetch(host, port, sel, tls, tor)
} }
/// Fetches a gopher URL by its component parts and returns a tuple of: /// Fetches a gopher URL by its component parts and returns a tuple of:
/// (did tls work?, raw Gopher response) /// (did tls work?, raw Gopher response)
pub fn fetch(host: &str, port: &str, selector: &str, try_tls: bool) -> Result<(bool, String)> { pub fn fetch(
let mut stream = request(host, port, selector, try_tls)?; host: &str,
port: &str,
selector: &str,
tls: bool,
tor: bool,
) -> Result<(bool, String)> {
let mut stream = request(host, port, selector, tls, tor)?;
let mut body = Vec::new(); let mut body = Vec::new();
stream.read_to_end(&mut body)?; stream.read_to_end(&mut body)?;
let out = clean_response(&String::from_utf8_lossy(&body)); let out = clean_response(&String::from_utf8_lossy(&body));
@ -81,7 +89,7 @@ fn clean_response(res: &str) -> String {
/// Downloads a binary to disk. Allows canceling with Ctrl-c. /// Downloads a binary to disk. Allows canceling with Ctrl-c.
/// Returns a tuple of: /// Returns a tuple of:
/// (path it was saved to, the size in bytes) /// (path it was saved to, the size in bytes)
pub fn download_url(url: &str, try_tls: bool) -> Result<(String, usize)> { pub fn download_url(url: &str, tls: bool, tor: bool) -> Result<(String, usize)> {
let (_, host, port, sel) = parse_url(url); let (_, host, port, sel) = parse_url(url);
let filename = sel let filename = sel
.split_terminator('/') .split_terminator('/')
@ -93,7 +101,7 @@ pub fn download_url(url: &str, try_tls: bool) -> Result<(String, usize)> {
let stdin = termion::async_stdin(); let stdin = termion::async_stdin();
let mut keys = stdin.keys(); let mut keys = stdin.keys();
let mut stream = request(host, port, sel, try_tls)?; let mut stream = request(host, port, sel, tls, tor)?;
let mut file = std::fs::OpenOptions::new() let mut file = std::fs::OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
@ -119,36 +127,54 @@ pub fn download_url(url: &str, try_tls: bool) -> Result<(String, usize)> {
/// Make a Gopher request and return a TcpStream ready to be read()'d. /// Make a Gopher request and return a TcpStream ready to be read()'d.
/// Will attempt a TLS connection first, then retry a regular /// Will attempt a TLS connection first, then retry a regular
/// connection if it fails. /// connection if it fails.
pub fn request(host: &str, port: &str, selector: &str, try_tls: bool) -> Result<Stream> { pub fn request(host: &str, port: &str, selector: &str, tls: bool, tor: bool) -> Result<Stream> {
let selector = selector.replace('?', "\t"); // search queries let selector = selector.replace('?', "\t"); // search queries
let sock = format!("{}:{}", host, port) let sock = format!("{}:{}", host, port)
.to_socket_addrs() .to_socket_addrs()
.and_then(|mut socks| socks.next().ok_or_else(|| error!("Can't create socket")))?; .and_then(|mut socks| socks.next().ok_or_else(|| error!("Can't create socket")))?;
// attempt tls connection // attempt tls connection
#[cfg(not(feature = "disable-tls"))] if tls {
{ #[cfg(not(feature = "disable-tls"))]
if try_tls { {
if let Ok(connector) = TlsConnector::new() { {
let stream = TcpStream::connect_timeout(&sock, TCP_TIMEOUT_DURATION)?; if let Ok(connector) = TlsConnector::new() {
stream.set_read_timeout(Some(TCP_TIMEOUT_DURATION))?; let stream = TcpStream::connect_timeout(&sock, TCP_TIMEOUT_DURATION)?;
if let Ok(mut stream) = connector.connect(host, stream) { stream.set_read_timeout(Some(TCP_TIMEOUT_DURATION))?;
stream.write(format!("{}\r\n", selector).as_ref())?; if let Ok(mut stream) = connector.connect(host, stream) {
return Ok(Stream { stream.write(format!("{}\r\n", selector).as_ref())?;
io: Box::new(stream), return Ok(Stream {
tls: true, io: Box::new(stream),
}); tls: true,
});
}
} }
} }
} }
} }
let mut stream = TcpStream::connect_timeout(&sock, TCP_TIMEOUT_DURATION)?; // tls didn't work, try regular
stream.write(format!("{}\r\n", selector).as_ref())?; if tor {
Ok(Stream { let proxy = env::var("TOR_PROXY")
io: Box::new(stream), .unwrap_or("127.0.0.1:9050".into())
tls: false, .to_socket_addrs()?
}) .nth(0)
.unwrap();
let mut stream = TorStream::connect_with_address(proxy, sock)?;
stream.write(format!("{}\r\n", selector).as_ref())?;
Ok(Stream {
io: Box::new(stream),
tls: false,
})
} else {
let mut stream = TcpStream::connect_timeout(&sock, TCP_TIMEOUT_DURATION)?;
stream.set_read_timeout(Some(TCP_TIMEOUT_DURATION))?;
stream.write(format!("{}\r\n", selector).as_ref())?;
Ok(Stream {
io: Box::new(stream),
tls: false,
})
}
} }
/// Parses gopher URL into parts. /// Parses gopher URL into parts.

@ -58,6 +58,7 @@ fn run() -> i32 {
return 1; return 1;
} }
} }
"-T" | "--tor" | "-tor" => cfg.tor = true,
arg => { arg => {
if arg.starts_with('-') { if arg.starts_with('-') {
print_version(); print_version();
@ -75,13 +76,19 @@ fn run() -> i32 {
} }
} }
if cfg.tor && cfg.tls {
eprintln!("Can't set both --tor and --tls.");
return 1;
}
if mode == Mode::Raw { if mode == Mode::Raw {
print_raw(&cfg.start, cfg.tls); print_raw(&cfg.start, cfg.tls, cfg.tor);
return 0; return 0;
} }
let mut ui = UI::new(cfg.tls); let start = cfg.start.clone();
if let Err(e) = ui.open(&cfg.start, &cfg.start) { let mut ui = UI::new(cfg);
if let Err(e) = ui.open(&start, &start) {
eprintln!("{}", e); eprintln!("{}", e);
return 1; return 1;
} }
@ -127,6 +134,9 @@ Usage:
Options: Options:
-t, --tls Try to open all pages w/ TLS -t, --tls Try to open all pages w/ TLS
-T, --tor Try to open all pages w/ Tor
Set the TOR_PROXY env variable to use
an address other than the default :9050
-r, --raw Print raw Gopher response only -r, --raw Print raw Gopher response only
-p, --print Print rendered Gopher response only -p, --print Print rendered Gopher response only
-l, --local Connect to 127.0.0.1:7070 -l, --local Connect to 127.0.0.1:7070
@ -138,8 +148,8 @@ Once you've launched phetch, use `ctrl-h` to view the on-line help."
); );
} }
fn print_raw(url: &str, try_tls: bool) { fn print_raw(url: &str, tls: bool, tor: bool) {
match gopher::fetch_url(url, try_tls) { match gopher::fetch_url(url, tls, tor) {
Ok((_, response)) => println!("{}", response), Ok((_, response)) => println!("{}", response),
Err(e) => { Err(e) => {
eprintln!("{}", e); eprintln!("{}", e);

@ -14,6 +14,7 @@ pub struct Menu {
pub scroll: usize, // scrolling offset pub scroll: usize, // scrolling offset
pub searching: bool, // search mode? pub searching: bool, // search mode?
pub tls: bool, // retrieved via tls? pub tls: bool, // retrieved via tls?
pub tor: bool, // retrieved via tor?
pub size: (usize, usize), // cols, rows pub size: (usize, usize), // cols, rows
pub wide: bool, // in wide mode? pub wide: bool, // in wide mode?
} }
@ -44,6 +45,10 @@ impl View for Menu {
self.tls self.tls
} }
fn is_tor(&self) -> bool {
self.tor
}
fn raw(&self) -> String { fn raw(&self) -> String {
self.raw.to_string() self.raw.to_string()
} }
@ -66,10 +71,12 @@ impl View for Menu {
} }
impl Menu { impl Menu {
pub fn from(url: String, response: String, tls: bool) -> Menu { pub fn from(url: String, response: String, tls: bool, tor: bool) -> Menu {
let mut menu = Self::parse(url, response); Menu {
menu.tls = tls; tls,
menu tor,
..Self::parse(url, response)
}
} }
fn cols(&self) -> usize { fn cols(&self) -> usize {
@ -797,6 +804,7 @@ impl Menu {
searching: false, searching: false,
size: (0, 0), size: (0, 0),
tls: false, tls: false,
tor: false,
wide: false, wide: false,
} }
} }

@ -10,6 +10,7 @@ pub struct Text {
longest: usize, // longest line longest: usize, // longest line
size: (usize, usize), // cols, rows size: (usize, usize), // cols, rows
pub tls: bool, // retrieved via tls? pub tls: bool, // retrieved via tls?
pub tor: bool, // retrieved via tor?
pub wide: bool, // in wide mode? turns off margins pub wide: bool, // in wide mode? turns off margins
} }
@ -24,6 +25,10 @@ impl View for Text {
self.tls self.tls
} }
fn is_tor(&self) -> bool {
self.tor
}
fn url(&self) -> String { fn url(&self) -> String {
self.url.to_string() self.url.to_string()
} }
@ -135,7 +140,7 @@ impl View for Text {
} }
impl Text { impl Text {
pub fn from(url: String, response: String, tls: bool) -> Text { pub fn from(url: String, response: String, tls: bool, tor: bool) -> Text {
let mut lines = 0; let mut lines = 0;
let mut longest = 0; let mut longest = 0;
for line in response.split_terminator('\n') { for line in response.split_terminator('\n') {
@ -153,6 +158,7 @@ impl Text {
longest, longest,
size: (0, 0), size: (0, 0),
tls, tls,
tor,
wide: false, wide: false,
} }
} }

@ -5,6 +5,7 @@ pub use self::view::View;
use crate::{ use crate::{
bookmarks, color, bookmarks, color,
config::Config,
gopher::{self, Type}, gopher::{self, Type},
help, history, help, history,
menu::Menu, menu::Menu,
@ -39,12 +40,12 @@ pub struct UI {
running: bool, // main ui loop running? running: bool, // main ui loop running?
pub size: (usize, usize), // cols, rows pub size: (usize, usize), // cols, rows
status: String, // status message, if any status: String, // status message, if any
tls: bool, // tls mode? config: Config, // user config
out: RefCell<AlternateScreen<RawTerminal<Stdout>>>, out: RefCell<AlternateScreen<RawTerminal<Stdout>>>,
} }
impl UI { impl UI {
pub fn new(tls: bool) -> UI { pub fn new(config: Config) -> UI {
let mut size = (0, 0); let mut size = (0, 0);
if let Ok((cols, rows)) = terminal_size() { if let Ok((cols, rows)) = terminal_size() {
size = (cols as usize, rows as usize); size = (cols as usize, rows as usize);
@ -66,8 +67,8 @@ impl UI {
dirty: true, dirty: true,
running: true, running: true,
size, size,
config,
status: String::new(), status: String::new(),
tls,
out: RefCell::new(out), out: RefCell::new(out),
} }
} }
@ -163,9 +164,9 @@ impl UI {
fn download(&mut self, url: &str) -> Result<()> { fn download(&mut self, url: &str) -> Result<()> {
let url = url.to_string(); let url = url.to_string();
let tls = self.tls; let (tls, tor) = (self.config.tls, self.config.tor);
self.spinner(&format!("Downloading {}", url), move || { self.spinner(&format!("Downloading {}", url), move || {
gopher::download_url(&url, tls) gopher::download_url(&url, tls, tor)
}) })
.and_then(|res| res) .and_then(|res| res)
.and_then(|(path, bytes)| { .and_then(|(path, bytes)| {
@ -189,17 +190,17 @@ impl UI {
thread::spawn(move || history::save(&hname, &hurl)); thread::spawn(move || history::save(&hname, &hurl));
// request thread // request thread
let thread_url = url.to_string(); let thread_url = url.to_string();
let try_tls = self.tls; let (tls, tor) = (self.config.tls, self.config.tor);
// don't spin on first ever request // don't spin on first ever request
let (tls, res) = if self.views.is_empty() { let (tls, res) = if self.views.is_empty() {
gopher::fetch_url(&thread_url, try_tls)? gopher::fetch_url(&thread_url, tls, tor)?
} else { } else {
self.spinner("", move || gopher::fetch_url(&thread_url, try_tls))?? self.spinner("", move || gopher::fetch_url(&thread_url, tls, tor))??
}; };
let (typ, _, _, _) = gopher::parse_url(&url); let (typ, _, _, _) = gopher::parse_url(&url);
match typ { match typ {
Type::Menu | Type::Search => Ok(Box::new(Menu::from(url.to_string(), res, tls))), Type::Menu | Type::Search => Ok(Box::new(Menu::from(url.to_string(), res, tls, tor))),
Type::Text | Type::HTML => Ok(Box::new(Text::from(url.to_string(), res, tls))), Type::Text | Type::HTML => Ok(Box::new(Text::from(url.to_string(), res, tls, tor))),
_ => Err(error!("Unsupported Gopher Response: {:?}", typ)), _ => Err(error!("Unsupported Gopher Response: {:?}", typ)),
} }
} }
@ -210,7 +211,7 @@ impl UI {
&url.trim_start_matches("gopher://phetch/") &url.trim_start_matches("gopher://phetch/")
.trim_start_matches("1/"), .trim_start_matches("1/"),
) { ) {
Ok(Box::new(Menu::from(url.to_string(), source, false))) Ok(Box::new(Menu::from(url.to_string(), source, false, false)))
} else { } else {
Err(error!("phetch URL not found: {}", url)) Err(error!("phetch URL not found: {}", url))
} }
@ -296,12 +297,15 @@ impl UI {
let page = self.views.get(self.focused)?; let page = self.views.get(self.focused)?;
if page.is_tls() { if page.is_tls() {
return Some(format!( return Some(format!(
"{}{}{}{}{}", "{}{}",
termion::cursor::Goto(self.cols() - 3, self.rows()), termion::cursor::Goto(self.cols() - 3, self.rows()),
color::Black, color!("TLS", Black, GreenBG),
color::GreenBG, ));
"TLS", } else if page.is_tor() {
"\x1b[0m" return Some(format!(
"{}{}",
termion::cursor::Goto(self.cols() - 3, self.rows()),
color!("TOR", Bold, White, MagentaBG),
)); ));
} }
None None
@ -521,7 +525,7 @@ impl UI {
if let Some(page) = self.views.get(self.focused) { if let Some(page) = self.views.get(self.focused) {
let url = page.url(); let url = page.url();
let raw = page.raw(); let raw = page.raw();
let mut text = Text::from(url, raw, page.is_tls()); let mut text = Text::from(url, raw, page.is_tls(), page.is_tor());
text.wide = true; text.wide = true;
self.add_page(Box::new(text)); self.add_page(Box::new(text));
} }
@ -561,12 +565,6 @@ impl UI {
} }
} }
impl Default for UI {
fn default() -> Self {
UI::new(false)
}
}
impl Drop for UI { impl Drop for UI {
fn drop(&mut self) { fn drop(&mut self) {
let mut out = self.out.borrow_mut(); let mut out = self.out.borrow_mut();

@ -5,6 +5,7 @@ pub trait View: fmt::Display {
fn respond(&mut self, key: ui::Key) -> ui::Action; fn respond(&mut self, key: ui::Key) -> ui::Action;
fn render(&self) -> String; fn render(&self) -> String;
fn is_tls(&self) -> bool; fn is_tls(&self) -> bool;
fn is_tor(&self) -> bool;
fn url(&self) -> String; fn url(&self) -> String;
fn raw(&self) -> String; fn raw(&self) -> String;
fn term_size(&mut self, cols: usize, rows: usize); fn term_size(&mut self, cols: usize, rows: usize);

Loading…
Cancel
Save