darknet content should "just work" now. needs better UI support!
This commit is contained in:
parent
734971b883
commit
2d6a417987
4 changed files with 93 additions and 87 deletions
|
@ -17,6 +17,7 @@ chrono = "0.4.41"
|
||||||
rss_content = { git = "https://code.gabe.rocks/gabriel/rss_content", version = "0.1.1" }
|
rss_content = { git = "https://code.gabe.rocks/gabriel/rss_content", version = "0.1.1" }
|
||||||
bytes = "1.10.1"
|
bytes = "1.10.1"
|
||||||
scraper = "0.23.1"
|
scraper = "0.23.1"
|
||||||
|
url = "2.5.4"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
debug=true
|
debug=true
|
||||||
|
|
155
src/net.rs
155
src/net.rs
|
@ -1,86 +1,107 @@
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use iced::widget::image::Handle;
|
use iced::widget::image::Handle;
|
||||||
use rss::{Channel};
|
|
||||||
use reqwest::{self, blocking::Client, header::USER_AGENT, Error, Proxy};
|
use reqwest::{self, blocking::Client, header::USER_AGENT, Error, Proxy};
|
||||||
|
use rss::Channel;
|
||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
const DEFAULT_TOR_PROXY: &str = "socks5h://127.0.0.1:9050";
|
const DEFAULT_TOR_PROXY: &str = "socks5h://127.0.0.1:9050";
|
||||||
const DEFAULT_I2P_PROXY: &str = "socks5h://127.0.0.1:4447";
|
const DEFAULT_I2P_PROXY: &str = "socks5h://127.0.0.1:4447";
|
||||||
|
|
||||||
enum Network{
|
#[derive(PartialEq)]
|
||||||
|
enum Network {
|
||||||
Clearnet,
|
Clearnet,
|
||||||
Tor,
|
Tor,
|
||||||
I2P
|
I2P,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_client(network: Network) -> Result<Client,Error>{
|
fn url_network(url: &str) -> Network {
|
||||||
|
match Url::parse(url) {
|
||||||
|
Ok(u) => match u.host() {
|
||||||
|
Some(host) => {
|
||||||
|
let h = host.to_string();
|
||||||
|
if h.find(".onion").is_some() {
|
||||||
|
return Network::Tor;
|
||||||
|
} else if h.find(".i2p").is_some() {
|
||||||
|
return Network::I2P;
|
||||||
|
} else {
|
||||||
|
return Network::Clearnet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {return Network::Unknown;}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
return Network::Clearnet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Network::Clearnet
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn network_testing(){
|
||||||
|
assert!(url_network("http://gabe.onion/rss") == Network::Tor);
|
||||||
|
assert!(url_network("http://gabe.i2p/rss") == Network::I2P);
|
||||||
|
assert!(url_network("https://gabe.rocks/rss") == Network::Clearnet);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client(network: Network) -> Result<Client, Error> {
|
||||||
//in the long run, support for fancy things like arti & user-configurable proxies will be important, nevermind other networks like reticulum!
|
//in the long run, support for fancy things like arti & user-configurable proxies will be important, nevermind other networks like reticulum!
|
||||||
match network {
|
match network {
|
||||||
Network::Clearnet => {
|
Network::Clearnet => {
|
||||||
return Ok(Client::new());
|
return Ok(Client::new());
|
||||||
}
|
}
|
||||||
Network::Tor => {
|
Network::Tor => {
|
||||||
match Client::builder().proxy(Proxy::http(DEFAULT_TOR_PROXY).unwrap()).build() {
|
match Client::builder()
|
||||||
Ok(client) => {return Ok(client);}
|
.proxy(Proxy::http(DEFAULT_TOR_PROXY).unwrap())
|
||||||
Err(e) => {return Err(e);}
|
.build()
|
||||||
|
{
|
||||||
|
Ok(client) => {
|
||||||
|
return Ok(client);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Network::I2P => {
|
Network::I2P => {
|
||||||
match Client::builder().proxy(Proxy::http(DEFAULT_I2P_PROXY).unwrap()).build() {
|
match Client::builder()
|
||||||
Ok(client) => {return Ok(client);}
|
.proxy(Proxy::http(DEFAULT_I2P_PROXY).unwrap())
|
||||||
Err(e) => {return Err(e);}
|
.build()
|
||||||
}
|
{
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_onion_content(url: &str) -> Option<String> {
|
|
||||||
match Client::builder().proxy(
|
|
||||||
Proxy::http(DEFAULT_TOR_PROXY).unwrap()
|
|
||||||
).build() {
|
|
||||||
Ok(client) => {
|
Ok(client) => {
|
||||||
let res = client.get(url)
|
return Ok(client);
|
||||||
.header(USER_AGENT,"RSS Reader")
|
|
||||||
.send();
|
|
||||||
match res {
|
|
||||||
Ok(resp) => {
|
|
||||||
match resp.text() {
|
|
||||||
Ok(body) => {return Some(body);}
|
|
||||||
Err(_) => {return None;}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error loading content via Tor\nError:{}",e);
|
return Err(e);
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Network::Unknown => {
|
||||||
println!("Failed to get Tor client\nError:{}",e);
|
println!("Fetching client for unknown network...");
|
||||||
return None;
|
return Ok(Client::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_content(url: &str) -> Option<String> {
|
fn get_content(url: &str) -> Option<String> {
|
||||||
let client = Client::new();
|
let client = get_client(url_network(url)).unwrap();
|
||||||
let res = client.get(url)
|
let res = client.get(url).header(USER_AGENT, "RSS Reader").send();
|
||||||
.header(USER_AGENT,"RSS Reader")
|
|
||||||
.send();
|
|
||||||
match res {
|
match res {
|
||||||
Ok(resp) => {
|
Ok(resp) => match resp.text() {
|
||||||
match resp.text() {
|
Ok(body) => return Some(body),
|
||||||
Ok(body) => {return Some(body)}
|
Err(_) => return None,
|
||||||
Err(_) => {return None}
|
},
|
||||||
}
|
Err(_) => return None,
|
||||||
}
|
|
||||||
Err(_) => {return None}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_feeds(url: &str) -> Option<Vec<Channel>>{
|
#[test]
|
||||||
|
fn get_onion_content() {
|
||||||
|
get_content("http://gabriel262me3lgv3w7xohtesg3laoojmtye644pwirhdm73qmedmsqd.onion/rss");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_feeds(url: &str) -> Option<Vec<Channel>> {
|
||||||
let page = get_content(url).unwrap();
|
let page = get_content(url).unwrap();
|
||||||
let doc = Html::parse_document(&page);
|
let doc = Html::parse_document(&page);
|
||||||
let link_selector = Selector::parse("head link").unwrap();
|
let link_selector = Selector::parse("head link").unwrap();
|
||||||
|
@ -90,7 +111,7 @@ pub fn get_feeds(url: &str) -> Option<Vec<Channel>>{
|
||||||
if rel == "alternate" {
|
if rel == "alternate" {
|
||||||
if let Some(href) = e.value().attr("href") {
|
if let Some(href) = e.value().attr("href") {
|
||||||
match load_rss(href) {
|
match load_rss(href) {
|
||||||
Some(c) => {feeds.push(c)}
|
Some(c) => feeds.push(c),
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,37 +120,35 @@ pub fn get_feeds(url: &str) -> Option<Vec<Channel>>{
|
||||||
}
|
}
|
||||||
return match feeds.len() {
|
return match feeds.len() {
|
||||||
0 => None,
|
0 => None,
|
||||||
_ => {Some(feeds)}
|
_ => Some(feeds),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_feed(url: &str) -> bool {
|
pub fn is_feed(url: &str) -> bool {
|
||||||
match get_feeds(url) {
|
match get_feeds(url) {
|
||||||
Some(_) => true,
|
Some(_) => true,
|
||||||
None => false
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_rss(url: &str) -> Option<Channel>{
|
pub fn load_rss(url: &str) -> Option<Channel> {
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let res = client.get(url)
|
let res = client.get(url).header(USER_AGENT, "RSS Reader").send();
|
||||||
.header(USER_AGENT,"RSS Reader")
|
|
||||||
.send();
|
|
||||||
match res {
|
match res {
|
||||||
Ok(resp) => {
|
Ok(resp) => match resp.bytes() {
|
||||||
match resp.bytes() {
|
Ok(body) => match Channel::read_from(&*body) {
|
||||||
Ok(body) => {
|
Ok(channel) => Some(channel),
|
||||||
match Channel::read_from(&*body) {
|
Err(e) => {
|
||||||
Ok(channel) => {Some(channel)}
|
panic!("Error parsing feed:\n{}", e);
|
||||||
Err(e) => {panic!("Error parsing feed:\n{}",e);}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
Err(_) => { panic!("Empty response")}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {panic!("Error loading feed.:{}",err)}
|
Err(_) => {
|
||||||
|
panic!("Empty response")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
panic!("Error loading feed.:{}", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_item_image(item: &rss::Item) -> Option<&str> {
|
pub fn get_item_image(item: &rss::Item) -> Option<&str> {
|
||||||
|
@ -152,7 +171,7 @@ pub fn get_item_image(item: &rss::Item) -> Option<&str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn download_image(url: &str) -> Option<iced::widget::image::Handle>{
|
pub fn download_image(url: &str) -> Option<iced::widget::image::Handle> {
|
||||||
match reqwest::blocking::get(url) {
|
match reqwest::blocking::get(url) {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
let img: Handle = Handle::from_bytes(r.bytes().unwrap());
|
let img: Handle = Handle::from_bytes(r.bytes().unwrap());
|
||||||
|
|
13
src/tests.rs
13
src/tests.rs
|
@ -7,19 +7,6 @@ fn get_feed_test(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn tor_gabe() {
|
|
||||||
let onion_gabe = net::get_onion_content("http://gabriel262me3lgv3w7xohtesg3laoojmtye644pwirhdm73qmedmsqd.onion/rss");
|
|
||||||
match onion_gabe {
|
|
||||||
Some(b) => {
|
|
||||||
println!("Successfully loaded over Tor:\n{}",b);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
panic!("Failed to load via Tor")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_feeds() {
|
fn load_feeds() {
|
||||||
let url = "https://gabe.rocks";
|
let url = "https://gabe.rocks";
|
||||||
|
|
|
@ -145,7 +145,6 @@ fn view(state: &State) -> Element<'_, Message> {
|
||||||
Page::AllItems => item_list(&state),
|
Page::AllItems => item_list(&state),
|
||||||
Page::ItemView => item_view(&state),
|
Page::ItemView => item_view(&state),
|
||||||
Page::Testing => testing(&state),
|
Page::Testing => testing(&state),
|
||||||
_ => {home(&state)}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue