diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs index 29d1104f..1d457e4b 100644 --- a/plugins/fs/src/commands.rs +++ b/plugins/fs/src/commands.rs @@ -85,6 +85,7 @@ pub fn create( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "create", &webview, &global_scope, &command_scope, @@ -119,6 +120,7 @@ pub fn open( options: Option, ) -> CommandResult { let (file, _path) = resolve_file( + "open", &webview, &global_scope, &command_scope, @@ -172,6 +174,7 @@ pub async fn copy_file( options: Option, ) -> CommandResult<()> { let resolved_from_path = resolve_path( + "copy-file", &webview, &global_scope, &command_scope, @@ -179,6 +182,7 @@ pub async fn copy_file( options.as_ref().and_then(|o| o.from_path_base_dir), )?; let resolved_to_path = resolve_path( + "copy-file", &webview, &global_scope, &command_scope, @@ -213,6 +217,7 @@ pub fn mkdir( options: Option, ) -> CommandResult<()> { let resolved_path = resolve_path( + "mkdir", &webview, &global_scope, &command_scope, @@ -260,6 +265,7 @@ pub async fn read_dir( options: Option, ) -> CommandResult> { let resolved_path = resolve_path( + "read-dir", &webview, &global_scope, &command_scope, @@ -336,8 +342,8 @@ pub async fn read( Ok(tauri::ipc::Response::new(data)) } -#[tauri::command] -pub async fn read_file( +async fn read_file_inner( + permission: &str, webview: Webview, global_scope: GlobalScope, command_scope: CommandScope, @@ -345,6 +351,7 @@ pub async fn read_file( options: Option, ) -> CommandResult { let (mut file, path) = resolve_file( + permission, &webview, &global_scope, &command_scope, @@ -372,6 +379,25 @@ pub async fn read_file( Ok(tauri::ipc::Response::new(contents)) } +#[tauri::command] +pub async fn read_file( + webview: Webview, + global_scope: GlobalScope, + command_scope: CommandScope, + path: SafeFilePath, + options: Option, +) -> CommandResult { + read_file_inner( + "read-file", + webview, + global_scope, + command_scope, + path, + options, + ) + .await +} + // TODO, remove in v3, rely on `read_file` command instead #[tauri::command] pub async fn read_text_file( @@ -381,7 +407,15 @@ pub async fn read_text_file( path: SafeFilePath, options: Option, ) -> CommandResult { - read_file(webview, global_scope, command_scope, path, options).await + read_file_inner( + "read-text-file", + webview, + global_scope, + command_scope, + path, + options, + ) + .await } #[tauri::command] @@ -393,6 +427,7 @@ pub fn read_text_file_lines( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "read-text-file-lines", &webview, &global_scope, &command_scope, @@ -457,6 +492,7 @@ pub fn remove( options: Option, ) -> CommandResult<()> { let resolved_path = resolve_path( + "remove", &webview, &global_scope, &command_scope, @@ -526,6 +562,7 @@ pub fn rename( options: Option, ) -> CommandResult<()> { let resolved_old_path = resolve_path( + "rename", &webview, &global_scope, &command_scope, @@ -533,6 +570,7 @@ pub fn rename( options.as_ref().and_then(|o| o.old_path_base_dir), )?; let resolved_new_path = resolve_path( + "rename", &webview, &global_scope, &command_scope, @@ -580,6 +618,7 @@ pub async fn seek( #[cfg(target_os = "android")] fn get_metadata std::io::Result>( + permission: &str, metadata_fn: F, webview: &Webview, global_scope: &GlobalScope, @@ -590,6 +629,7 @@ fn get_metadata std::io::Result { let (file, path) = resolve_file( + permission, webview, global_scope, command_scope, @@ -611,6 +651,7 @@ fn get_metadata std::io::Result get_fs_metadata( + permission, metadata_fn, webview, global_scope, @@ -623,6 +664,7 @@ fn get_metadata std::io::Result std::io::Result>( + permission: &str, metadata_fn: F, webview: &Webview, global_scope: &GlobalScope, @@ -631,6 +673,7 @@ fn get_metadata std::io::Result, ) -> CommandResult { get_fs_metadata( + permission, metadata_fn, webview, global_scope, @@ -641,6 +684,7 @@ fn get_metadata std::io::Result std::io::Result>( + permission: &str, metadata_fn: F, webview: &Webview, global_scope: &GlobalScope, @@ -649,6 +693,7 @@ fn get_fs_metadata std::io::Result, ) -> CommandResult { let resolved_path = resolve_path( + permission, webview, global_scope, command_scope, @@ -673,6 +718,7 @@ pub fn stat( options: Option, ) -> CommandResult { let metadata = get_metadata( + "stat", |p| std::fs::metadata(p), &webview, &global_scope, @@ -693,6 +739,7 @@ pub fn lstat( options: Option, ) -> CommandResult { let metadata = get_metadata( + "lstat", |p| std::fs::symlink_metadata(p), &webview, &global_scope, @@ -721,6 +768,7 @@ pub async fn truncate( options: Option, ) -> CommandResult<()> { let resolved_path = resolve_path( + "truncate", &webview, &global_scope, &command_scope, @@ -789,23 +837,13 @@ fn default_create_value() -> bool { true } -#[tauri::command] -pub async fn write_file( +async fn write_file_inner( + permission: &str, webview: Webview, global_scope: GlobalScope, command_scope: CommandScope, request: tauri::ipc::Request<'_>, ) -> CommandResult<()> { - let data = match request.body() { - tauri::ipc::InvokeBody::Raw(data) => Cow::Borrowed(data), - tauri::ipc::InvokeBody::Json(serde_json::Value::Array(data)) => Cow::Owned( - data.iter() - .flat_map(|v| v.as_number().and_then(|v| v.as_u64().map(|v| v as u8))) - .collect(), - ), - _ => return Err(anyhow::anyhow!("unexpected invoke body").into()), - }; - let path = request .headers() .get("path") @@ -816,6 +854,7 @@ pub async fn write_file( .map_err(|_| anyhow::anyhow!("path is not a valid UTF-8").into()) }) .and_then(|p| SafeFilePath::from_str(&p).map_err(CommandError::from))?; + let options: Option = request .headers() .get("options") @@ -823,6 +862,7 @@ pub async fn write_file( .and_then(|opts| serde_json::from_str(opts).ok()); let (mut file, path) = resolve_file( + permission, &webview, &global_scope, &command_scope, @@ -858,6 +898,16 @@ pub async fn write_file( }, )?; + let data = match request.body() { + tauri::ipc::InvokeBody::Raw(data) => Cow::Borrowed(data), + tauri::ipc::InvokeBody::Json(serde_json::Value::Array(data)) => Cow::Owned( + data.iter() + .flat_map(|v| v.as_number().and_then(|v| v.as_u64().map(|v| v as u8))) + .collect(), + ), + _ => return Err(anyhow::anyhow!("unexpected invoke body").into()), + }; + file.write_all(&data) .map_err(|e| { format!( @@ -868,6 +918,16 @@ pub async fn write_file( .map_err(Into::into) } +#[tauri::command] +pub async fn write_file( + webview: Webview, + global_scope: GlobalScope, + command_scope: CommandScope, + request: tauri::ipc::Request<'_>, +) -> CommandResult<()> { + write_file_inner("write-file", webview, global_scope, command_scope, request).await +} + // TODO, remove in v3, rely on `write_file` command instead #[tauri::command] pub async fn write_text_file( @@ -876,7 +936,14 @@ pub async fn write_text_file( command_scope: CommandScope, request: tauri::ipc::Request<'_>, ) -> CommandResult<()> { - write_file(webview, global_scope, command_scope, request).await + write_file_inner( + "write-text-file", + webview, + global_scope, + command_scope, + request, + ) + .await } #[tauri::command] @@ -888,6 +955,7 @@ pub fn exists( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "exists", &webview, &global_scope, &command_scope, @@ -906,6 +974,7 @@ pub async fn size( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "size", &webview, &global_scope, &command_scope, @@ -948,16 +1017,25 @@ fn get_dir_size(path: &PathBuf) -> CommandResult { #[cfg(not(target_os = "android"))] pub fn resolve_file( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, path: SafeFilePath, open_options: OpenOptions, ) -> CommandResult<(File, PathBuf)> { - resolve_file_in_fs(webview, global_scope, command_scope, path, open_options) + resolve_file_in_fs( + permission, + webview, + global_scope, + command_scope, + path, + open_options, + ) } fn resolve_file_in_fs( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, @@ -965,6 +1043,7 @@ fn resolve_file_in_fs( open_options: OpenOptions, ) -> CommandResult<(File, PathBuf)> { let path = resolve_path( + permission, webview, global_scope, command_scope, @@ -985,6 +1064,7 @@ fn resolve_file_in_fs( #[cfg(target_os = "android")] pub fn resolve_file( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, @@ -1002,6 +1082,7 @@ pub fn resolve_file( Ok((file, path)) } SafeFilePath::Path(path) => resolve_file_in_fs( + permission, webview, global_scope, command_scope, @@ -1012,6 +1093,7 @@ pub fn resolve_file( } pub fn resolve_path( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, @@ -1057,7 +1139,17 @@ pub fn resolve_path( if fs_scope.scope.is_allowed(&path) || scope.is_allowed(&path) { Ok(path) } else { - Err(CommandError::Plugin(Error::PathForbidden(path))) + #[cfg(not(debug_assertions))] + return Err(CommandError::Plugin(Error::PathForbidden(path))); + + #[cfg(debug_assertions)] + Err( + anyhow::anyhow!( + "forbidden path: {}, maybe it is not allowed on the scope for `allow-{permission}` permission in your capability file", + path.display() + ) + ) + .map_err(Into::into) } } diff --git a/plugins/fs/src/mobile.rs b/plugins/fs/src/mobile.rs index 06422be6..472f2c8a 100644 --- a/plugins/fs/src/mobile.rs +++ b/plugins/fs/src/mobile.rs @@ -89,7 +89,7 @@ impl Fs { std::fs::File::from_raw_fd(fd) }) } else { - todo!() + unimplemented!() } } } diff --git a/plugins/fs/src/watcher.rs b/plugins/fs/src/watcher.rs index 7d851822..5a8c08bd 100644 --- a/plugins/fs/src/watcher.rs +++ b/plugins/fs/src/watcher.rs @@ -93,6 +93,7 @@ pub async fn watch( let mut resolved_paths = Vec::with_capacity(paths.capacity()); for path in paths { resolved_paths.push(resolve_path( + "watch", &webview, &global_scope, &command_scope,