--allow-delete Allow delete files/folders
--allow-search Allow search files/folders
--allow-symlink Allow symlink to files/folders outside root directory
+ --allow-archive Allow zip archive generation
--enable-cors Enable CORS, sets `Access-Control-Allow-Origin: *`
--render-index Serve index.html when requesting a directory, returns 404 if not found index.html
--render-try-index Serve index.html when requesting a directory, returns directory listing if not found index.html
<div class="breadcrumb"></div>
<div class="toolbox">
<div>
- <a href="?zip" title="Download folder as a .zip file">
+ <a href="?zip" class="zip-root hidden" title="Download folder as a .zip file">
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
</a>
</div>
* @property {boolean} allow_upload
* @property {boolean} allow_delete
* @property {boolean} allow_search
+ * @property {boolean} allow_archive
* @property {boolean} dir_exists
*/
let actionMove = "";
if (file.path_type.endsWith("Dir")) {
url += "/";
- actionDownload = `
- <div class="action-btn">
- <a href="${url}?zip" title="Download folder as a .zip file">
- <svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
- </a>
- </div>`;
+ if (DATA.allow_archive) {
+ actionDownload = `
+ <div class="action-btn">
+ <a href="${url}?zip" title="Download folder as a .zip file">
+ <svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
+ </a>
+ </div>`;
+ }
} else {
actionDownload = `
<div class="action-btn" >
}
}
+ if (DATA.allow_archive) {
+ document.querySelector(".zip-root").classList.remove("hidden");
+ }
+
addBreadcrumb(DATA.href, DATA.uri_prefix);
renderPathsTableHead();
renderPathsTableBody();
.action(ArgAction::SetTrue)
.help("Allow symlink to files/folders outside root directory"),
)
+ .arg(
+ Arg::new("allow-archive")
+ .long("allow-archive")
+ .action(ArgAction::SetTrue)
+ .help("Allow zip archive generation"),
+ )
.arg(
Arg::new("enable-cors")
.long("enable-cors")
pub allow_delete: bool,
pub allow_search: bool,
pub allow_symlink: bool,
+ pub allow_archive: bool,
pub render_index: bool,
pub render_spa: bool,
pub render_try_index: bool,
let allow_delete = matches.get_flag("allow-all") || matches.get_flag("allow-delete");
let allow_search = matches.get_flag("allow-all") || matches.get_flag("allow-search");
let allow_symlink = matches.get_flag("allow-all") || matches.get_flag("allow-symlink");
+ let allow_archive = matches.get_flag("allow-all") || matches.get_flag("allow-archive");
let render_index = matches.get_flag("render-index");
let render_try_index = matches.get_flag("render-try-index");
let render_spa = matches.get_flag("render-spa");
allow_upload,
allow_search,
allow_symlink,
+ allow_archive,
render_index,
render_try_index,
render_spa,
let allow_upload = self.args.allow_upload;
let allow_delete = self.args.allow_delete;
let allow_search = self.args.allow_search;
+ let allow_archive = self.args.allow_archive;
let render_index = self.args.render_index;
let render_spa = self.args.render_spa;
let render_try_index = self.args.render_try_index;
Method::GET | Method::HEAD => {
if is_dir {
if render_try_index {
- if query_params.contains_key("zip") {
+ if allow_archive && query_params.contains_key("zip") {
+ if !allow_archive {
+ status_not_found(&mut res);
+ return Ok(res);
+ }
self.handle_zip_dir(path, head_only, &mut res).await?;
} else if allow_search && query_params.contains_key("q") {
self.handle_search_dir(path, &query_params, head_only, &mut res)
self.handle_render_index(path, &query_params, headers, head_only, &mut res)
.await?;
} else if query_params.contains_key("zip") {
+ if !allow_archive {
+ status_not_found(&mut res);
+ return Ok(res);
+ }
self.handle_zip_dir(path, head_only, &mut res).await?;
} else if allow_search && query_params.contains_key("q") {
self.handle_search_dir(path, &query_params, head_only, &mut res)
allow_upload: self.args.allow_upload,
allow_delete: self.args.allow_delete,
allow_search: self.args.allow_search,
+ allow_archive: self.args.allow_archive,
dir_exists: exist,
};
let data = serde_json::to_string(&data).unwrap();
allow_upload: bool,
allow_delete: bool,
allow_search: bool,
+ allow_archive: bool,
dir_exists: bool,
}
Ok(())
}
+#[rstest]
+fn default_not_allow_archive(server: TestServer) -> Result<(), Error> {
+ let resp = reqwest::blocking::get(format!("{}?zip", server.url()))?;
+ assert_eq!(resp.status(), 404);
+ Ok(())
+}
+
#[rstest]
fn default_not_exist_dir(server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}404/", server.url()))?;
}
Ok(())
}
+
+#[rstest]
+fn allow_archive(#[with(&["--allow-archive"])] server: TestServer) -> Result<(), Error> {
+ let resp = reqwest::blocking::get(format!("{}?zip", server.url()))?;
+ assert_eq!(resp.status(), 200);
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "application/zip"
+ );
+ assert!(resp.headers().contains_key("content-disposition"));
+ Ok(())
+}
}
#[rstest]
-fn get_dir_zip(server: TestServer) -> Result<(), Error> {
+fn get_dir_zip(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}?zip", server.url()))?;
assert_eq!(resp.status(), 200);
assert_eq!(
}
#[rstest]
-fn head_dir_zip(server: TestServer) -> Result<(), Error> {
+fn head_dir_zip(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
let resp = fetch!(b"HEAD", format!("{}?zip", server.url())).send()?;
assert_eq!(resp.status(), 200);
assert_eq!(
}
#[rstest]
-fn render_try_index3(#[with(&["--render-try-index"])] server: TestServer) -> Result<(), Error> {
+fn render_try_index3(
+ #[with(&["--render-try-index", "--allow-archive"])] server: TestServer,
+) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}{}?zip", server.url(), DIR_NO_INDEX))?;
assert_eq!(resp.status(), 200);
assert_eq!(