commit 49813d0c68137b24b70380068cc826742ef3b920
parent bd9ed3255a8c43d51a6d71edc912d17319f43443
Author: Johann150 <johann.galle@protonmail.com>
Date: Thu, 11 Feb 2021 18:46:20 +0100
serve hidden files if there is a .meta config for them
Diffstat:
4 files changed, 55 insertions(+), 42 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
@@ -19,6 +19,7 @@ Thank you to @gegeweb for contributing to this release.
* The configuration files are now parsed as YAML. The syntax only changes in that a space is now required behind the colon.
* The changelog is now also kept in this file in addition to the GitHub releases.
* Certificate chain and key file are now only loaded once at startup, certificate changes need a restart to take effect.
+* Hidden files are now served if there is an explicit setting in a `.meta` file for them, regardless of the `--serve-secret` flag.
### Fixed
* The Syntax for the IPv6 address in the README has been corrected.
diff --git a/README.md b/README.md
@@ -48,7 +48,7 @@ agate --content path/to/content/ \
All of the command-line arguments are optional. Run `agate --help` to see the default values used when arguments are omitted.
-When a client requests the URL `gemini://example.com/foo/bar`, Agate will respond with the file at `path/to/content/foo/bar`. If any segment of the requested path starts with a dot, agate will respond with a status code 52, whether the file exists or not (this behaviour can be disabled with `--serve-secret`). If there is a directory at that path, Agate will look for a file named `index.gmi` inside that directory.
+When a client requests the URL `gemini://example.com/foo/bar`, Agate will respond with the file at `path/to/content/foo/bar`. If any segment of the requested path starts with a dot, agate will respond with a status code 52, whether the file exists or not. This behaviour can be disabled with `--serve-secret` or by an entry for the specific file in the `.meta` configuration file (see Meta-Presets). If there is a directory at that path, Agate will look for a file named `index.gmi` inside that directory.
## Configuration
diff --git a/src/main.rs b/src/main.rs
@@ -331,15 +331,24 @@ impl RequestHandle {
path.push(url.host_str().expect("no hostname"));
}
- if let Some(segments) = url.path_segments() {
- for segment in segments {
- if !ARGS.serve_secret && segment.starts_with('.') {
- // Do not serve anything that looks like a hidden file.
- return self
- .send_header(52, "If I told you, it would not be a secret.")
- .await;
- }
- path.push(&*percent_decode_str(segment).decode_utf8()?);
+ if let Some(mut segments) = url.path_segments() {
+ // append percent-decoded path segments
+ path.extend(
+ segments
+ .clone()
+ .map(|segment| Ok(percent_decode_str(segment).decode_utf8()?.into_owned()))
+ .collect::<Result<Vec<_>>>()?,
+ );
+ // check if hiding files is disabled
+ if !ARGS.serve_secret
+ // there is a configuration for this file, assume it should be served
+ && !self.metadata.lock().await.exists(&path)
+ // check if file or directory is hidden
+ && segments.any(|segment| segment.starts_with('.'))
+ {
+ return self
+ .send_header(52, "If I told you, it would not be a secret.")
+ .await;
}
}
diff --git a/src/metadata.rs b/src/metadata.rs
@@ -1,5 +1,5 @@
use std::collections::BTreeMap;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::time::SystemTime;
use yaml_rust::YamlLoader;
@@ -67,21 +67,22 @@ impl FileOptions {
}
}
- /// Checks wether the database for the respective directory is still
- /// up to date.
- /// Will only return true if the database should be (re)read, i.e. it will
- /// return false if there is no database file in the specified directory.
- fn check_outdated(&self, db_dir: &PathBuf) -> bool {
- let mut db = db_dir.clone();
- db.push(SIDECAR_FILENAME);
- let db = db.as_path();
+ /// Checks wether the database for the directory of the specified file is
+ /// still up to date and re-reads it if outdated or not yet read.
+ fn update(&mut self, dir: &Path) {
+ let mut dir = if super::ARGS.central_config {
+ super::ARGS.content_dir.clone()
+ } else {
+ dir.parent().expect("no parent directory").to_path_buf()
+ };
+ dir.push(SIDECAR_FILENAME);
- if let Ok(metadata) = db.metadata() {
+ let should_read = if let Ok(metadata) = dir.as_path().metadata() {
if !metadata.is_file() {
// it exists, but it is a directory
false
} else if let (Ok(modified), Some(last_read)) =
- (metadata.modified(), self.databases_read.get(db_dir))
+ (metadata.modified(), self.databases_read.get(&dir))
{
// check that it was last modified before the read
// if the times are the same, we might have read the old file
@@ -95,18 +96,17 @@ impl FileOptions {
} else {
// the file probably does not exist
false
+ };
+
+ if should_read {
+ self.read_database(&dir);
}
}
- /// (Re)reads a specific sidecar file that resides in the specified
- /// directory. The function takes a directory to minimize path
- /// alterations "on the fly".
+ /// (Re)reads a specified sidecar file.
/// This function will allways try to read the file, even if it is current.
- fn read_database(&mut self, db_dir: &PathBuf) {
- log::trace!("reading database for {:?}", db_dir);
- let mut db = db_dir.clone();
- db.push(SIDECAR_FILENAME);
- let db = db.as_path();
+ fn read_database(&mut self, db: &PathBuf) {
+ log::trace!("reading database {:?}", db);
if let Ok(contents) = std::fs::read_to_string(db) {
let docs = match YamlLoader::load_from_str(&contents) {
@@ -137,7 +137,8 @@ impl FileOptions {
};
// generate workspace-unique path
- let mut path = db_dir.clone();
+ let mut path = db.clone();
+ path.pop();
path.push(rel_path);
// parse the preset
@@ -180,8 +181,10 @@ impl FileOptions {
log::error!("no YAML document {:?}", db);
return;
};
- self.databases_read
- .insert(db_dir.clone(), SystemTime::now());
+ self.databases_read.insert(
+ db.as_path().parent().unwrap().to_path_buf(),
+ SystemTime::now(),
+ );
} else {
log::error!("could not read configuration file {:?}", db);
}
@@ -192,17 +195,17 @@ impl FileOptions {
/// The file path should consistenly be either absolute or relative to the
/// working/content directory. If inconsisten file paths are used, this can
/// lead to loading and storing sidecar files multiple times.
- pub fn get(&mut self, file: &PathBuf) -> PresetMeta {
- let dir = if super::ARGS.central_config {
- super::ARGS.content_dir.clone()
- } else {
- file.parent().expect("no parent directory").to_path_buf()
- };
-
- if self.check_outdated(&dir) {
- self.read_database(&dir);
- }
+ pub fn get(&mut self, file: &Path) -> PresetMeta {
+ self.update(file);
self.file_meta.get(file).unwrap_or(&self.default).clone()
}
+
+ /// Returns true if a configuration exists in a configuration file.
+ /// Returns false if no or only the default value exists.
+ pub fn exists(&mut self, file: &Path) -> bool {
+ self.update(file);
+
+ self.file_meta.contains_key(file)
+ }
}