use std::fs::Metadata;
use std::io::SeekFrom;
use std::net::SocketAddr;
-use std::path::{Path, PathBuf};
+use std::path::{Component, Path, PathBuf};
use std::sync::atomic::{self, AtomicBool};
use std::sync::Arc;
use std::time::SystemTime;
impl Server {
pub fn init(args: Args, running: Arc<AtomicBool>) -> Result<Self> {
- let assets_prefix = format!("{}__dufs_v{}_/", args.uri_prefix, env!("CARGO_PKG_VERSION"));
+ let assets_prefix = format!("__dufs_v{}__/", env!("CARGO_PKG_VERSION"));
let single_file_req_paths = if args.path_is_file {
vec![
args.uri_prefix.to_string(),
let headers = req.headers();
let method = req.method().clone();
- if guard_path(req_path, &mut res) {
- return Ok(res);
- }
-
- if method == Method::GET && self.handle_assets(req_path, headers, &mut res).await? {
- return Ok(res);
- }
-
- let authorization = headers.get(AUTHORIZATION);
let relative_path = match self.resolve_path(req_path) {
Some(v) => v,
None => {
- status_forbid(&mut res);
+ status_bad_request(&mut res, "Invalid Path");
return Ok(res);
}
};
+ if method == Method::GET
+ && self
+ .handle_assets(&relative_path, headers, &mut res)
+ .await?
+ {
+ return Ok(res);
+ }
+
+ let authorization = headers.get(AUTHORIZATION);
let guard = self.args.auth.guard(&relative_path, &method, authorization);
let (user, access_paths) = match guard {
}
} else if is_file {
if query_params.contains_key("edit") {
- self.handle_deal_file(path, DataKind::Edit, head_only, user, &mut res)
+ self.handle_edit_file(path, DataKind::Edit, head_only, user, &mut res)
.await?;
} else if query_params.contains_key("view") {
- self.handle_deal_file(path, DataKind::View, head_only, user, &mut res)
+ self.handle_edit_file(path, DataKind::View, head_only, user, &mut res)
.await?;
} else {
self.handle_send_file(path, headers, head_only, &mut res)
Ok(())
}
- async fn handle_deal_file(
+ async fn handle_edit_file(
&self,
path: &Path,
kind: DataKind,
.typed_insert(ContentType::from(mime_guess::mime::TEXT_HTML_UTF_8));
let output = self
.html
- .replace("__ASSETS_PREFIX__", &self.assets_prefix)
+ .replace(
+ "__ASSETS_PREFIX__",
+ &format!("{}{}", self.args.uri_prefix, self.assets_prefix),
+ )
.replace("__INDEX_DATA__", &serde_json::to_string(&data)?);
res.headers_mut()
.typed_insert(ContentLength(output.as_bytes().len() as u64));
res.headers_mut()
.typed_insert(ContentType::from(mime_guess::mime::TEXT_HTML_UTF_8));
self.html
- .replace("__ASSETS_PREFIX__", &self.assets_prefix)
+ .replace(
+ "__ASSETS_PREFIX__",
+ &format!("{}{}", self.args.uri_prefix, self.assets_prefix),
+ )
.replace("__INDEX_DATA__", &serde_json::to_string(&data)?)
};
res.headers_mut()
{
Some(dest) => dest,
None => {
- *res.status_mut() = StatusCode::BAD_REQUEST;
+ status_bad_request(res, "Invalid Destination");
return None;
}
};
- if guard_path(&dest_path, res) {
- return None;
- }
-
let authorization = headers.get(AUTHORIZATION);
let guard = self
.args
}
fn resolve_path(&self, path: &str) -> Option<String> {
- let path = path.trim_matches('/');
let path = decode_uri(path)?;
- let prefix = self.args.path_prefix.as_str();
- if prefix == "/" {
- return Some(path.to_string());
+ let path = path.trim_matches('/');
+ let mut parts = vec![];
+ for comp in Path::new(path).components() {
+ if let Component::Normal(v) = comp {
+ let v = v.to_string_lossy();
+ if cfg!(windows) {
+ let chars: Vec<char> = v.chars().collect();
+ if chars.len() == 2 && chars[1] == ':' && chars[0].is_ascii_alphabetic() {
+ return None;
+ }
+ }
+ parts.push(v);
+ } else {
+ return None;
+ }
+ }
+ let new_path = parts.join("/");
+ let path_prefix = self.args.path_prefix.as_str();
+ if path_prefix.is_empty() {
+ return Some(new_path);
}
- path.strip_prefix(prefix.trim_start_matches('/'))
+ new_path
+ .strip_prefix(path_prefix.trim_start_matches('/'))
.map(|v| v.trim_matches('/').to_string())
}
Some(v) => v,
None => return Ok(None),
};
- let err = || anyhow!("Invalid X-Update-Range header");
+ let err = || anyhow!("Invalid X-Update-Range Header");
let value = value.to_str().map_err(|_| err())?;
if value == "append" {
return Ok(Some(size));
let (start, _) = parse_range(value, size).ok_or_else(err)?;
Ok(Some(start))
}
-
-fn guard_path(path: &str, res: &mut Response) -> bool {
- let path = Path::new(path);
- if path.components().any(|v| v.as_os_str() == "..") {
- status_bad_request(res, "");
- return true;
- }
- false
-}
fn assets(server: TestServer) -> Result<(), Error> {
let ver = env!("CARGO_PKG_VERSION");
let resp = reqwest::blocking::get(server.url())?;
- let index_js = format!("/__dufs_v{ver}_/index.js");
- let index_css = format!("/__dufs_v{ver}_/index.css");
- let favicon_ico = format!("/__dufs_v{ver}_/favicon.ico");
+ let index_js = format!("/__dufs_v{ver}__/index.js");
+ let index_css = format!("/__dufs_v{ver}__/index.css");
+ let favicon_ico = format!("/__dufs_v{ver}__/favicon.ico");
let text = resp.text()?;
println!("{text}");
assert!(text.contains(&format!(r#"href="{index_css}""#)));
#[rstest]
fn asset_js(server: TestServer) -> Result<(), Error> {
let url = format!(
- "{}__dufs_v{}_/index.js",
+ "{}__dufs_v{}__/index.js",
server.url(),
env!("CARGO_PKG_VERSION")
);
#[rstest]
fn asset_css(server: TestServer) -> Result<(), Error> {
let url = format!(
- "{}__dufs_v{}_/index.css",
+ "{}__dufs_v{}__/index.css",
server.url(),
env!("CARGO_PKG_VERSION")
);
#[rstest]
fn asset_ico(server: TestServer) -> Result<(), Error> {
let url = format!(
- "{}__dufs_v{}_/favicon.ico",
+ "{}__dufs_v{}__/favicon.ico",
server.url(),
env!("CARGO_PKG_VERSION")
);
fn assets_with_prefix(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> {
let ver = env!("CARGO_PKG_VERSION");
let resp = reqwest::blocking::get(format!("{}xyz/", server.url()))?;
- let index_js = format!("/xyz/__dufs_v{ver}_/index.js");
- let index_css = format!("/xyz/__dufs_v{ver}_/index.css");
- let favicon_ico = format!("/xyz/__dufs_v{ver}_/favicon.ico");
+ let index_js = format!("/xyz/__dufs_v{ver}__/index.js");
+ let index_css = format!("/xyz/__dufs_v{ver}__/index.css");
+ let favicon_ico = format!("/xyz/__dufs_v{ver}__/favicon.ico");
let text = resp.text()?;
assert!(text.contains(&format!(r#"href="{index_css}""#)));
assert!(text.contains(&format!(r#"href="{favicon_ico}""#)));
#[with(&["--path-prefix", "xyz"])] server: TestServer,
) -> Result<(), Error> {
let url = format!(
- "{}xyz/__dufs_v{}_/index.js",
+ "{}xyz/__dufs_v{}__/index.js",
server.url(),
env!("CARGO_PKG_VERSION")
);
let url = format!("http://localhost:{port}");
let resp = reqwest::blocking::get(&url)?;
assert!(resp.text()?.starts_with(&format!(
- "/__dufs_v{}_/index.js;DATA",
+ "/__dufs_v{}__/index.js;DATA",
env!("CARGO_PKG_VERSION")
)));
let resp = reqwest::blocking::get(&url)?;