aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Configuration.md22
-rw-r--r--docker-compose.yaml2
-rw-r--r--src/html.rs51
-rw-r--r--src/response.rs49
-rw-r--r--src/url.rs42
5 files changed, 79 insertions, 87 deletions
diff --git a/Configuration.md b/Configuration.md
index dfacbe9..a1c1930 100644
--- a/Configuration.md
+++ b/Configuration.md
@@ -37,14 +37,17 @@ If no `CSS_EXTERNAL` value is provided, a default stylesheet of
CSS_EXTERNAL=https://cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.1/mini-default.min.css
```
-## `KEEP_GEMINI_EXACT`
+## `KEEP_GEMINI`
-A comma-separated list of Gemini URIs to keep as is when proxying.
+A comma-separated list of Gemini URL fragments to keep as is when proxying
+
+Wildcards are supported using the `*` character, and exceptions can be made
+using the `!` character
```dotenv
-# These two URIs will be kept pointing to their original Gemini URIs when
-# proxied instead of being replaced with their proxied equivalents.
-KEEP_GEMINI_EXACT=gemini://fuwn.me/something,gemini://fuwn.me/another
+# These rules ensure that all Gemini URLs will be left untouched in the proxied
+# HTML response except for URLs under the "fuwn.me" domain
+KEEP_GEMINI=!*fuwn.me/*,gemini://*
```
## `HEAD`
@@ -55,15 +58,6 @@ Insert any string at the end of the HTMl `<head>`
HEAD=<script>/* September */</script><style>/* September */</style>
```
-## `KEEP_GEMINI_DOMAIN`
-
-Similar to `KEEP_GEMINI_EXACT`, except matches based on entire domain or domains
-instead of exact URIs
-
-```dotenv
-KEEP_GEMINI_DOMAIN=fuwn.me,example.com
-```
-
## `PROXY_BY_DEFAULT`
Control whether or not all Gemini URLs will be proxied
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 896f490..7e978bc 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -5,7 +5,5 @@ services:
environment:
- "ROOT=gemini://fuwn.me"
- "CSS_EXTERNAL=https://example.com/style.css"
- - "KEEP_GEMINI_EXACT=gemini://fuwn.me/skills"
- # - "KEEP_GEMINI_DOMAIN=fuwn.me"
- "PROXY_BY_DEFAULT=true"
image: "fuwn/september:latest"
diff --git a/src/html.rs b/src/html.rs
index c0425df..341286c 100644
--- a/src/html.rs
+++ b/src/html.rs
@@ -1,16 +1,21 @@
use {
+ crate::url::matches_pattern,
germ::ast::Node,
std::{env::var, fmt::Write},
url::Url,
};
fn link_from_host_href(url: &Url, href: &str) -> Option<String> {
- Some(format!(
- "gemini://{}{}{}",
- url.domain()?,
- { if href.starts_with('/') { "" } else { "/" } },
- href
- ))
+ if href.starts_with("/proxy/") {
+ Some(format!("gemini://{}", href.replace("/proxy/", "")))
+ } else {
+ Some(format!(
+ "gemini://{}{}{}",
+ url.domain()?,
+ { if href.starts_with('/') { "" } else { "/" } },
+ href
+ ))
+ }
}
fn safe(text: &str) -> String {
@@ -181,30 +186,26 @@ pub fn from_gemini(
}
}
- if let Ok(keeps) = var("KEEP_GEMINI_EXACT") {
- let mut keeps = keeps.split(',');
+ if let Ok(keeps) = var("KEEP_GEMINI") {
+ let patterns = keeps.split(',').collect::<Vec<_>>();
if (href.starts_with('/') || !href.contains("://")) && !surface {
let temporary_href = link_from_host_href(url, &href)?;
+ let should_exclude = patterns
+ .iter()
+ .filter(|p| p.starts_with('!'))
+ .any(|p| matches_pattern(&p[1..], &temporary_href));
- if keeps.any(|k| k == &*temporary_href) {
- href = temporary_href;
- }
- }
- }
-
- if let Ok(keeps) = var("KEEP_GEMINI_DOMAIN") {
- let host = if let Some(host) = url.host() {
- host.to_string()
- } else {
- return None;
- };
+ if !should_exclude {
+ let should_include = patterns
+ .iter()
+ .filter(|p| !p.starts_with('!'))
+ .any(|p| matches_pattern(p, &temporary_href));
- if (href.starts_with('/')
- || !href.contains("://") && keeps.split(',').any(|k| k == &*host))
- && !surface
- {
- href = link_from_host_href(url, &href)?;
+ if should_include {
+ href = temporary_href;
+ }
+ }
}
}
diff --git a/src/response.rs b/src/response.rs
index 1a68b08..68c1690 100644
--- a/src/response.rs
+++ b/src/response.rs
@@ -1,7 +1,7 @@
pub mod configuration;
use {
- crate::url::from_path as url_from_path,
+ crate::url::{from_path as url_from_path, matches_pattern},
actix_web::{Error, HttpResponse},
std::{env::var, fmt::Write, time::Instant},
};
@@ -262,8 +262,8 @@ pub async fn default(
if let Ok(plain_texts) = var("PLAIN_TEXT_ROUTE") {
if plain_texts.split(',').any(|r| {
- path_matches_pattern(r, http_request.path())
- || path_matches_pattern(r, http_request.path().trim_end_matches('/'))
+ matches_pattern(r, http_request.path())
+ || matches_pattern(r, http_request.path().trim_end_matches('/'))
}) {
return Ok(HttpResponse::Ok().body(
response.content().as_ref().map_or_else(String::default, String::clone),
@@ -277,46 +277,3 @@ pub async fn default(
.body(html_context),
)
}
-
-fn path_matches_pattern(pattern: &str, path: &str) -> bool {
- if !pattern.contains('*') {
- return path == pattern;
- }
-
- let parts: Vec<&str> = pattern.split('*').collect();
- let mut position = if pattern.starts_with('*') {
- 0
- } else {
- let first = parts.first().unwrap();
-
- if !path.starts_with(first) {
- return false;
- }
-
- first.len()
- };
-
- let mid_end = parts.len().saturating_sub(1);
-
- for part in &parts[1..mid_end] {
- if part.is_empty() {
- continue;
- }
-
- if let Some(found) = path[position..].find(part) {
- position += found + part.len();
- } else {
- return false;
- }
- }
-
- if !pattern.ends_with('*') {
- let last = parts.last().unwrap();
-
- if !path[position..].ends_with(last) {
- return false;
- }
- }
-
- true
-}
diff --git a/src/url.rs b/src/url.rs
index 289c5cb..5289d28 100644
--- a/src/url.rs
+++ b/src/url.rs
@@ -57,3 +57,45 @@ pub fn from_path(
)
})
}
+
+pub fn matches_pattern(pattern: &str, path: &str) -> bool {
+ if !pattern.contains('*') {
+ return path == pattern;
+ }
+
+ let parts: Vec<&str> = pattern.split('*').collect();
+ let mut position = if pattern.starts_with('*') {
+ 0
+ } else {
+ let first = parts.first().unwrap();
+
+ if !path.starts_with(first) {
+ return false;
+ }
+
+ first.len()
+ };
+ let before_last = parts.len().saturating_sub(1);
+
+ for part in &parts[1..before_last] {
+ if part.is_empty() {
+ continue;
+ }
+
+ if let Some(found) = path[position..].find(part) {
+ position += found + part.len();
+ } else {
+ return false;
+ }
+ }
+
+ if !pattern.ends_with('*') {
+ let last = parts.last().unwrap();
+
+ if !path[position..].ends_with(last) {
+ return false;
+ }
+ }
+
+ true
+}