aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-04-17 23:59:20 -0700
committerFuwn <[email protected]>2023-04-17 23:59:20 -0700
commit51deeab2db38e65713b07c7c72e6151c93d1e201 (patch)
tree391b2c348011d999ac8c60a6af3ca72339b49011 /src
parentstyle: rewrite formatter (diff)
downloadlocus-51deeab2db38e65713b07c7c72e6151c93d1e201.tar.xz
locus-51deeab2db38e65713b07c7c72e6151c93d1e201.zip
feat(web): a cool lil web-to-gemini proxy
Diffstat (limited to 'src')
-rw-r--r--src/main.rs2
-rw-r--r--src/modules.rs3
-rw-r--r--src/modules/web.rs187
3 files changed, 190 insertions, 2 deletions
diff --git a/src/main.rs b/src/main.rs
index 90a083b..8d45dbd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,7 +16,7 @@
// Copyright (C) 2022-2022 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-#![feature(const_extern_fn)]
+#![feature(const_extern_fn, async_closure)]
#![deny(
warnings,
nonstandard_style,
diff --git a/src/modules.rs b/src/modules.rs
index 33afc88..3254e7a 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -30,5 +30,6 @@ amenadiel::modules!(
interests,
api,
stocks,
- cryptocurrency
+ cryptocurrency,
+ web
);
diff --git a/src/modules/web.rs b/src/modules/web.rs
new file mode 100644
index 0000000..09c6ac9
--- /dev/null
+++ b/src/modules/web.rs
@@ -0,0 +1,187 @@
+// This file is part of Locus <https://github.com/gemrest/locus>.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2023 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+use crate::{response::success, route::track_mount};
+
+fn error(
+ message: &str,
+ context: &windmark::context::RouteContext,
+) -> windmark::Response {
+ success(
+ &format!(
+ "# World Wide Web to Gemini Gateway\n\n{message}\n\n=> /web Go back"
+ ),
+ context,
+ )
+}
+
+pub fn module(router: &mut windmark::Router) {
+ track_mount(
+ router,
+ "/web",
+ "World Wide Web to Gemini Gateway",
+ |context| {
+ success(
+ &r#"# World Wide Web to Gemini Gateway
+
+To use this gateway, simply append the address of your target resource to the end of the current route, minus the protocol.
+
+To visit the web version of this exact page, <https://fuwn.me/web>, you would visit <gemini://fuwn.me/web/fuwn.me/web>.
+
+=> /web/fuwn.me/web Try it!"#,
+ &context,
+ )
+ },
+ );
+ track_mount(
+ router,
+ "/web/*url",
+ "World Wide Web to Gemini Gateway Visitor",
+ async move |context| {
+ windmark::Response::success({
+ let Ok(url) = url::Url::parse(&format!(
+ "https://{}",
+ if let Some(url_choice) = context.parameters.get("url") {
+ url_choice
+ } else {
+ return error(
+ "Looks like you didn't provide a URL!",
+ &context,
+ );
+ }
+ )) else {
+ return error(
+ "The URL you provided is invalid.",
+ &context,
+ );
+ };
+ let mut contents = vec![];
+ let website = if let Ok(website) = reqwest::get(url.clone()).await {
+ if let Ok(text) = website.text().await {
+ text
+ } else {
+ return error(
+ "The website you provided could not be reached.",
+ &context,
+ );
+ }
+ } else {
+ return error(
+ "The website you provided could not be reached.",
+ &context,
+ );
+ };
+ let Ok(dom) = tl::parse(&website, tl::ParserOptions::default()) else {
+ return error(
+ "The website you provided could not be properly parsed.",
+ &context,
+ );
+ };
+ let parser = dom.parser();
+ let mut nodes = dom.nodes().iter().peekable();
+
+ while let Some(element) = nodes.next() {
+ let mut text = String::new();
+
+ element.as_tag().map_or((), |tag| {
+ match tag.name().as_utf8_str().to_string().as_str() {
+ "p" => {
+ contents.push(format!(
+ "{}\n\n",
+ tag
+ .inner_text(parser)
+ .lines()
+ .collect::<Vec<_>>()
+ .first()
+ .unwrap_or(&"A parse error occurred in this location.")
+ ));
+ }
+ "a" => {
+ contents.pop();
+ contents.push(format!(
+ "=> {} {}\n{}",
+ tag.attributes().get("href").flatten().map_or(
+ "A parse error occurred in this location.".to_string(),
+ |href| href.as_utf8_str().to_string()
+ ),
+ tag.inner_text(parser),
+ nodes.peek().map_or("", |next_node| {
+ next_node.as_tag().map_or("", |next_tag| {
+ if next_tag.name().as_utf8_str().to_string().as_str()
+ == "a"
+ {
+ ""
+ } else {
+ "\n"
+ }
+ })
+ })
+ ));
+ }
+ "h1" => {
+ contents.push(format!("# {}\n\n", tag.inner_text(parser)));
+ }
+ "h2" => {
+ contents.push(format!("## {}\n\n", tag.inner_text(parser)));
+ }
+ "h3" => {
+ contents.push(format!("### {}\n\n", tag.inner_text(parser)));
+ }
+ "pre" => {
+ contents.push(format!("```\n{}```\n", tag.inner_text(parser)));
+ }
+ "blockquote" => {
+ contents.push(format!("> {}\n\n", tag.inner_text(parser)));
+ }
+ "li" => {
+ contents.push(format!("* {}\n", tag.inner_text(parser)));
+ }
+ "html" | "head" | "script" | "link" | "title" | "body" | "ul" => {
+ }
+ _ => {
+ text = tag.inner_text(parser).to_string();
+
+ if text.contains("Proxy information")
+ || text.contains("Proxied content from")
+ {
+ return;
+ }
+
+ println!(
+ "{}: {}",
+ tag.name().as_utf8_str().to_string(),
+ tag.inner_text(parser)
+ );
+
+ contents.push(format!("{}\n\n", tag.inner_text(parser)));
+ }
+ }
+ });
+
+ // Covers September and Kineto
+ if text == "Proxy information"
+ || text.contains("Proxied content from")
+ {
+ break;
+ }
+ }
+
+ contents.join("")
+ })
+ },
+ );
+}