From a9ee18dcbbf837225f05d4dd0186fc989a319c63 Mon Sep 17 00:00:00 2001 From: Johannes May Date: Sat, 12 Mar 2022 22:12:35 +0100 Subject: [PATCH 1/3] Allow to set the accept input attribute to arbitrary values --- src/args.rs | 20 ++++++++++++++++++++ src/config.rs | 23 ++++++++++++++++++++++- src/renderer.rs | 5 ++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/args.rs b/src/args.rs index 841db3ade..7667bee62 100644 --- a/src/args.rs +++ b/src/args.rs @@ -8,6 +8,13 @@ use crate::auth; use crate::errors::ContextualError; use crate::renderer; +#[derive(clap::ArgEnum, Clone)] +pub enum MediaType { + Image, + Audio, + Video, +} + #[derive(Parser)] #[clap(name = "miniserve", author, about, version)] pub struct CliArgs { @@ -104,6 +111,19 @@ pub struct CliArgs { #[clap(short = 'u', long = "upload-files")] pub file_upload: bool, + /// Specify uploadable media types + #[clap(arg_enum, short = 'm', long = "media-type", requires = "file-upload")] + pub media_type: Option>, + + /// Directly specify the uploadable media type expression + #[clap( + short = 'M', + long = "raw-media-type", + requires = "file-upload", + conflicts_with = "media-type" + )] + pub media_type_raw: Option, + /// Enable overriding existing files during file upload #[clap(short = 'o', long = "overwrite-files")] pub overwrite_files: bool, diff --git a/src/config.rs b/src/config.rs index 73fcec2cb..1bb8e08ab 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,7 +13,10 @@ use http::HeaderMap; #[cfg(feature = "tls")] use rustls_pemfile as pemfile; -use crate::{args::CliArgs, auth::RequiredAuth}; +use crate::{ + args::{CliArgs, MediaType}, + auth::RequiredAuth, +}; /// Possible characters for random routes const ROUTE_ALPHABET: [char; 16] = [ @@ -81,6 +84,9 @@ pub struct MiniserveConfig { /// Enable file upload pub file_upload: bool, + /// HTML accept attribute value + pub uploadable_media_type: Option, + /// Enable upload to override existing files pub overwrite_files: bool, @@ -188,6 +194,20 @@ impl MiniserveConfig { #[cfg(not(feature = "tls"))] let tls_rustls_server_config = None; + let uploadable_media_type = args.media_type_raw.or_else(|| { + args.media_type.map(|types| { + types + .into_iter() + .map(|t| match t { + MediaType::Audio => "audio/*", + MediaType::Image => "image/*", + MediaType::Video => "video/*", + }) + .collect::>() + .join(",") + }) + }); + Ok(MiniserveConfig { verbose: args.verbose, path: args.path.unwrap_or_else(|| PathBuf::from(".")), @@ -207,6 +227,7 @@ impl MiniserveConfig { overwrite_files: args.overwrite_files, show_qrcode: args.qrcode, file_upload: args.file_upload, + uploadable_media_type, tar_enabled: args.enable_tar, tar_gz_enabled: args.enable_tar_gz, zip_enabled: args.enable_zip, diff --git a/src/renderer.rs b/src/renderer.rs index ef7285b68..c64d45910 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -107,7 +107,10 @@ pub fn page( form id="file_submit" action=(upload_action) method="POST" enctype="multipart/form-data" { p { "Select a file to upload or drag it anywhere into the window" } div { - input #file-input type="file" name="file_to_upload" required="" multiple {} + @match &conf.uploadable_media_type { + Some(accept) => {input #file-input accept=(accept) type="file" name="file_to_upload" required="" multiple {}}, + None => {input #file-input type="file" name="file_to_upload" required="" multiple {}} + } button type="submit" { "Upload file" } } } From 6a6baec9ab16e004b4163af3012a949412df1148 Mon Sep 17 00:00:00 2001 From: Johannes May Date: Sun, 13 Mar 2022 18:54:42 +0100 Subject: [PATCH 2/3] Add test cases for the media-type parameters --- tests/upload_files.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/upload_files.rs b/tests/upload_files.rs index 5e764ba60..71fcbc4a0 100644 --- a/tests/upload_files.rs +++ b/tests/upload_files.rs @@ -167,3 +167,23 @@ fn upload_to_symlink_directory( Ok(()) } + +/// Test setting the HTML accept attribute using -m and -M. +#[rstest] +#[case(server(&["-u"]), None)] +#[case(server(&["-u", "-m", "image"]), Some("image/*"))] +#[case(server(&["-u", "-m", "image", "-m", "audio", "-m", "video"]), Some("image/*,audio/*,video/*"))] +#[case(server(&["-u", "-m", "audio", "-m", "image", "-m", "video"]), Some("audio/*,image/*,video/*"))] +#[case(server(&["-u", "-M", "test_value"]), Some("test_value"))] +fn set_media_type( + #[case] server: TestServer, + #[case] expected_accept_value: Option<&str>, +) -> Result<(), Error> { + let body = reqwest::blocking::get(server.url())?.error_for_status()?; + let parsed = Document::from_read(body)?; + + let input = parsed.find(Attr("id", "file-input")).next().unwrap(); + assert_eq!(input.attr("accept"), expected_accept_value); + + Ok(()) +} From 8e37888aa04a29bc0019f7546cc435420aeaa458 Mon Sep 17 00:00:00 2001 From: Johannes May Date: Sun, 13 Mar 2022 18:57:29 +0100 Subject: [PATCH 3/3] Add an example for the media-type feature --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index d6bf32f0f..d613662f9 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,13 @@ Sometimes this is just a more practical and quick way than doing things properly (where `$FILE` is the path to the file. This uses miniserve's default port of 8080) +### Take pictures and upload them from smartphones: + + miniserve -u -m image -q + +This uses the `--media-type` option, which sends a hint for the expected media type to the browser. +Some mobile browsers like Firefox on Android will offer to open the camera app when seeing this. + ## Features - Easy to use