From 9d3e1aed69c7503b53bb3a6c7e1f2bc0ba4c638f Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sat, 13 May 2023 17:00:23 -0300 Subject: [PATCH] feat(plugins): add allowlist for current core plugins --- Cargo.lock | 14 +- Cargo.toml | 4 +- examples/api/src-tauri/Cargo.toml | 10 +- examples/api/src-tauri/tauri.conf.json | 3 + plugins/cli/Cargo.toml | 3 + plugins/cli/guest-js/index.ts | 2 +- plugins/cli/src/lib.rs | 13 +- plugins/clipboard/Cargo.toml | 4 + plugins/clipboard/src/commands.rs | 4 + plugins/clipboard/src/lib.rs | 7 +- plugins/dialog/Cargo.toml | 7 + plugins/dialog/src/commands.rs | 7 + plugins/dialog/src/lib.rs | 5 + plugins/fs/Cargo.toml | 12 + plugins/fs/src/commands.rs | 349 ++++++++++++++----------- plugins/fs/src/lib.rs | 11 + plugins/global-shortcut/Cargo.toml | 5 + plugins/global-shortcut/src/lib.rs | 23 +- plugins/http/Cargo.toml | 1 + plugins/http/src/commands/mod.rs | 5 + plugins/http/src/lib.rs | 5 +- plugins/http/src/scope.rs | 2 + plugins/notification/Cargo.toml | 2 + plugins/notification/src/commands.rs | 6 +- plugins/notification/src/lib.rs | 2 + plugins/process/Cargo.toml | 4 + plugins/process/src/commands.rs | 4 + plugins/process/src/lib.rs | 7 +- plugins/shell/Cargo.toml | 8 +- plugins/shell/src/commands.rs | 305 +++++++++++---------- plugins/shell/src/lib.rs | 4 + plugins/shell/src/scope.rs | 3 + plugins/updater/Cargo.toml | 2 + plugins/updater/src/commands.rs | 4 + plugins/updater/src/lib.rs | 2 + plugins/window/Cargo.toml | 48 ++++ plugins/window/src/commands.rs | 54 ++++ plugins/window/src/lib.rs | 50 ++++ 38 files changed, 678 insertions(+), 323 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80d7bb36..1e256feb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4960,7 +4960,7 @@ dependencies = [ [[package]] name = "tauri" version = "2.0.0-alpha.8" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "anyhow", "bytes 1.4.0", @@ -5010,7 +5010,7 @@ dependencies = [ [[package]] name = "tauri-build" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "anyhow", "cargo_toml", @@ -5030,7 +5030,7 @@ dependencies = [ [[package]] name = "tauri-codegen" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "base64 0.21.0", "brotli", @@ -5055,7 +5055,7 @@ dependencies = [ [[package]] name = "tauri-macros" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -5449,7 +5449,7 @@ dependencies = [ [[package]] name = "tauri-runtime" version = "0.13.0-alpha.4" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "gtk", "http", @@ -5469,7 +5469,7 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" version = "0.13.0-alpha.4" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "cocoa", "gtk", @@ -5489,7 +5489,7 @@ dependencies = [ [[package]] name = "tauri-utils" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f" +source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346" dependencies = [ "aes-gcm 0.10.1", "brotli", diff --git a/Cargo.toml b/Cargo.toml index c70e53f1..2c1a8ad3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ resolver = "2" [workspace.dependencies] serde = { version = "1", features = ["derive"] } log = "0.4" -tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next" } -tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next" } +tauri = { git = "https://github.com/tauri-apps/tauri", branch = "feat/plugin-allowlist" } +tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "feat/plugin-allowlist" } serde_json = "1" thiserror = "1" diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 4bd7ce49..18e5827a 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -19,15 +19,15 @@ tiny_http = "0.11" log.workspace = true tauri-plugin-app = { path = "../../../plugins/app" } tauri-plugin-log = { path = "../../../plugins/log" } -tauri-plugin-fs = { path = "../../../plugins/fs" } +tauri-plugin-fs = { path = "../../../plugins/fs", features = [] } tauri-plugin-clipboard = { path = "../../../plugins/clipboard" } tauri-plugin-dialog = { path = "../../../plugins/dialog" } -tauri-plugin-http = { path = "../../../plugins/http", features = [ "http-multipart" ] } +tauri-plugin-http = { path = "../../../plugins/http", features = ["http-multipart"] } tauri-plugin-notification = { path = "../../../plugins/notification", features = [ "windows7-compat" ] } tauri-plugin-os = { path = "../../../plugins/os" } tauri-plugin-process = { path = "../../../plugins/process" } -tauri-plugin-shell = { path = "../../../plugins/shell" } -tauri-plugin-updater = { path = "../../../plugins/updater" } +tauri-plugin-shell = { path = "../../../plugins/shell", features = [] } +tauri-plugin-updater = { path = "../../../plugins/updater", features = [] } tauri-plugin-window = { path = "../../../plugins/window" } [dependencies.tauri] @@ -42,7 +42,7 @@ features = [ ] [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -tauri-plugin-cli = { path = "../../../plugins/cli" } +tauri-plugin-cli = { path = "../../../plugins/cli", features = ["allow-get-matches"] } tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut" } [target."cfg(target_os = \"windows\")".dependencies] diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index 8fd3fee4..3edcb21d 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -13,6 +13,9 @@ }, "plugins": { "cli": { + "allowlist": { + "getMatches": true + }, "description": "Tauri API example", "args": [ { diff --git a/plugins/cli/Cargo.toml b/plugins/cli/Cargo.toml index f6dc2cd3..60406838 100644 --- a/plugins/cli/Cargo.toml +++ b/plugins/cli/Cargo.toml @@ -12,3 +12,6 @@ tauri.workspace = true log.workspace = true thiserror.workspace = true clap = { version = "4", features = ["string"] } + +[features] +allow-get-matches = [] \ No newline at end of file diff --git a/plugins/cli/guest-js/index.ts b/plugins/cli/guest-js/index.ts index 81eaae86..de4f5b7f 100644 --- a/plugins/cli/guest-js/index.ts +++ b/plugins/cli/guest-js/index.ts @@ -64,7 +64,7 @@ interface CliMatches { * @since 1.0.0 */ async function getMatches(): Promise { - return await invoke("plugin:cli|cli_matches"); + return await invoke("plugin:cli|get_matches"); } export type { ArgMatch, SubcommandMatch, CliMatches }; diff --git a/plugins/cli/src/lib.rs b/plugins/cli/src/lib.rs index 25795c01..42a4fff7 100644 --- a/plugins/cli/src/lib.rs +++ b/plugins/cli/src/lib.rs @@ -1,6 +1,6 @@ use tauri::{ plugin::{Builder, PluginApi, TauriPlugin}, - AppHandle, Manager, Runtime, State, + Manager, Runtime, }; mod config; @@ -29,14 +29,21 @@ impl> CliExt for T { } } +#[cfg(feature = "allow-get-matches")] #[tauri::command] -fn cli_matches(_app: AppHandle, cli: State<'_, Cli>) -> Result { +fn get_matches( + _app: tauri::AppHandle, + cli: tauri::State<'_, Cli>, +) -> Result { cli.matches() } pub fn init() -> TauriPlugin { Builder::new("cli") - .invoke_handler(tauri::generate_handler![cli_matches]) + .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-get-matches")] + get_matches + ]) .setup(|app, api| { app.manage(Cli(api)); Ok(()) diff --git a/plugins/clipboard/Cargo.toml b/plugins/clipboard/Cargo.toml index 00864ff4..9e4133c3 100644 --- a/plugins/clipboard/Cargo.toml +++ b/plugins/clipboard/Cargo.toml @@ -17,3 +17,7 @@ thiserror.workspace = true [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] arboard = "3" + +[features] +allow-write = [] +allow-read = [] \ No newline at end of file diff --git a/plugins/clipboard/src/commands.rs b/plugins/clipboard/src/commands.rs index eec868a8..4d10160a 100644 --- a/plugins/clipboard/src/commands.rs +++ b/plugins/clipboard/src/commands.rs @@ -2,10 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use tauri::{command, AppHandle, Runtime, State}; use crate::{ClipKind, Clipboard, ClipboardContents, Result}; +#[cfg(feature = "allow-write")] #[command] pub(crate) async fn write( _app: AppHandle, @@ -15,6 +18,7 @@ pub(crate) async fn write( clipboard.write(data) } +#[cfg(feature = "allow-read")] #[command] pub(crate) async fn read( _app: AppHandle, diff --git a/plugins/clipboard/src/lib.rs b/plugins/clipboard/src/lib.rs index a655d93e..35cae757 100644 --- a/plugins/clipboard/src/lib.rs +++ b/plugins/clipboard/src/lib.rs @@ -39,7 +39,12 @@ impl> crate::ClipboardExt for T { /// Initializes the plugin. pub fn init() -> TauriPlugin { Builder::new("clipboard") - .invoke_handler(tauri::generate_handler![commands::write, commands::read]) + .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-write")] + commands::write, + #[cfg(feature = "allow-read")] + commands::read + ]) .setup(|app, api| { #[cfg(mobile)] let clipboard = mobile::init(app, api)?; diff --git a/plugins/dialog/Cargo.toml b/plugins/dialog/Cargo.toml index 1882da38..63910249 100644 --- a/plugins/dialog/Cargo.toml +++ b/plugins/dialog/Cargo.toml @@ -24,3 +24,10 @@ raw-window-handle = "0.5" [build-dependencies] tauri-build.workspace = true + +[features] +allow-open = [] +allow-save = [] +allow-message = [] +allow-ask = [] +allow-confirm = [] \ No newline at end of file diff --git a/plugins/dialog/src/commands.rs b/plugins/dialog/src/commands.rs index 905c01ca..1ef25d73 100644 --- a/plugins/dialog/src/commands.rs +++ b/plugins/dialog/src/commands.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use std::path::PathBuf; use serde::{Deserialize, Serialize}; @@ -86,6 +88,7 @@ fn set_default_path( } } +#[cfg(feature = "allow-open")] #[command] pub(crate) async fn open( window: Window, @@ -161,6 +164,7 @@ pub(crate) async fn open( Ok(res) } +#[cfg(feature = "allow-save")] #[allow(unused_variables)] #[command] pub(crate) async fn save( @@ -235,6 +239,7 @@ fn message_dialog( builder.blocking_show() } +#[cfg(feature = "allow-message")] #[command] pub(crate) async fn message( window: Window, @@ -255,6 +260,7 @@ pub(crate) async fn message( )) } +#[cfg(feature = "allow-ask")] #[command] pub(crate) async fn ask( window: Window, @@ -276,6 +282,7 @@ pub(crate) async fn ask( )) } +#[cfg(feature = "allow-confirm")] #[command] pub(crate) async fn confirm( window: Window, diff --git a/plugins/dialog/src/lib.rs b/plugins/dialog/src/lib.rs index 85518fef..6e162b9d 100644 --- a/plugins/dialog/src/lib.rs +++ b/plugins/dialog/src/lib.rs @@ -71,10 +71,15 @@ impl Dialog { pub fn init() -> TauriPlugin { Builder::new("dialog") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-open")] commands::open, + #[cfg(feature = "allow-save")] commands::save, + #[cfg(feature = "allow-message")] commands::message, + #[cfg(feature = "allow-ask")] commands::ask, + #[cfg(feature = "allow-confirm")] commands::confirm ]) .setup(|app, api| { diff --git a/plugins/fs/Cargo.toml b/plugins/fs/Cargo.toml index bb3980b6..6398bcc4 100644 --- a/plugins/fs/Cargo.toml +++ b/plugins/fs/Cargo.toml @@ -16,3 +16,15 @@ thiserror.workspace = true anyhow = "1" uuid = { version = "1", features = ["v4"] } glob = "0.3" + +[features] +allow-read-file = [] +allow-write-file = [] +allow-read-dir = [] +allow-copy-file = [] +allow-create-dir = [] +allow-remove-dir = [] +allow-remove-file = [] +allow-rename-file = [] +allow-exists = [] +allow-metadata = [] \ No newline at end of file diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs index 168f9629..1abc41d5 100644 --- a/plugins/fs/src/commands.rs +++ b/plugins/fs/src/commands.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports)] + use crate::Scope; use anyhow::Context; use serde::{Deserialize, Serialize, Serializer}; @@ -6,15 +8,10 @@ use tauri::{ Manager, Runtime, Window, }; -#[cfg(unix)] -use std::os::unix::fs::{MetadataExt, PermissionsExt}; -#[cfg(windows)] -use std::os::windows::fs::MetadataExt; use std::{ - fs::{self, symlink_metadata, File}, + fs::{self, File}, io::Write, path::{Path, PathBuf}, - time::{SystemTime, UNIX_EPOCH}, }; use crate::{Error, FsExt, Result}; @@ -36,6 +33,7 @@ impl Serialize for CommandError { } } +#[allow(dead_code)] type CommandResult = std::result::Result; /// The options for the directory functions on the file system API. @@ -57,6 +55,7 @@ pub struct FileOperationOptions { pub dir: Option, } +#[allow(dead_code)] fn resolve_path( window: &Window, path: SafePathBuf, @@ -77,6 +76,7 @@ fn resolve_path( } } +#[cfg(feature = "allow-read-file")] #[tauri::command] pub fn read_file( window: Window, @@ -89,6 +89,7 @@ pub fn read_file( .map_err(Into::into) } +#[cfg(feature = "allow-read-file")] #[tauri::command] pub fn read_text_file( window: Window, @@ -101,6 +102,7 @@ pub fn read_text_file( .map_err(Into::into) } +#[cfg(feature = "allow-write-file")] #[tauri::command] pub fn write_file( window: Window, @@ -119,84 +121,104 @@ pub fn write_file( }) } -#[derive(Clone, Copy)] -struct ReadDirOptions<'a> { - pub scope: Option<&'a Scope>, -} +#[cfg(feature = "allow-read-dir")] +pub use read_dir::*; -#[derive(Debug, Serialize)] -#[non_exhaustive] -pub struct DiskEntry { - /// The path to the entry. - pub path: PathBuf, - /// The name of the entry (file name with extension or directory name). - pub name: Option, - /// The children of this entry if it's a directory. - #[serde(skip_serializing_if = "Option::is_none")] - pub children: Option>, -} +#[cfg(feature = "allow-read-dir")] +mod read_dir { + use std::{ + fs::{self, symlink_metadata}, + path::{Path, PathBuf}, + }; + + use anyhow::Context; + use serde::Serialize; + use tauri::{path::SafePathBuf, Runtime, Window}; + + use crate::{FsExt, Result, Scope}; + + use super::{resolve_path, CommandResult, DirOperationOptions}; -fn read_dir_with_options>( - path: P, - recursive: bool, - options: ReadDirOptions<'_>, -) -> Result> { - let mut files_and_dirs: Vec = vec![]; - for entry in fs::read_dir(path)? { - let path = entry?.path(); - let path_as_string = path.display().to_string(); - - if let Ok(flag) = path.metadata().map(|m| m.is_dir()) { - let is_symlink = symlink_metadata(&path).map(|md| md.is_symlink())?; - files_and_dirs.push(DiskEntry { - path: path.clone(), - children: if flag { - Some( - if recursive - && (!is_symlink - || options.scope.map(|s| s.is_allowed(&path)).unwrap_or(true)) - { - read_dir_with_options(&path_as_string, true, options)? - } else { - vec![] - }, - ) - } else { - None - }, - name: path - .file_name() - .map(|name| name.to_string_lossy()) - .map(|name| name.to_string()), - }); + #[derive(Clone, Copy)] + struct ReadDirOptions<'a> { + pub scope: Option<&'a Scope>, + } + + #[derive(Debug, Serialize)] + #[non_exhaustive] + pub struct DiskEntry { + /// The path to the entry. + pub path: PathBuf, + /// The name of the entry (file name with extension or directory name). + pub name: Option, + /// The children of this entry if it's a directory. + #[serde(skip_serializing_if = "Option::is_none")] + pub children: Option>, + } + + fn read_dir_with_options>( + path: P, + recursive: bool, + options: ReadDirOptions<'_>, + ) -> Result> { + let mut files_and_dirs: Vec = vec![]; + for entry in fs::read_dir(path)? { + let path = entry?.path(); + let path_as_string = path.display().to_string(); + + if let Ok(flag) = path.metadata().map(|m| m.is_dir()) { + let is_symlink = symlink_metadata(&path).map(|md| md.is_symlink())?; + files_and_dirs.push(DiskEntry { + path: path.clone(), + children: if flag { + Some( + if recursive + && (!is_symlink + || options.scope.map(|s| s.is_allowed(&path)).unwrap_or(true)) + { + read_dir_with_options(&path_as_string, true, options)? + } else { + vec![] + }, + ) + } else { + None + }, + name: path + .file_name() + .map(|name| name.to_string_lossy()) + .map(|name| name.to_string()), + }); + } } + Result::Ok(files_and_dirs) } - Result::Ok(files_and_dirs) -} -#[tauri::command] -pub fn read_dir( - window: Window, - path: SafePathBuf, - options: Option, -) -> CommandResult> { - let (recursive, dir) = if let Some(options_value) = options { - (options_value.recursive, options_value.dir) - } else { - (false, None) - }; - let resolved_path = resolve_path(&window, path, dir)?; - read_dir_with_options( - &resolved_path, - recursive, - ReadDirOptions { - scope: Some(window.fs_scope()), - }, - ) - .with_context(|| format!("path: {}", resolved_path.display())) - .map_err(Into::into) + #[tauri::command] + pub fn read_dir( + window: Window, + path: SafePathBuf, + options: Option, + ) -> CommandResult> { + let (recursive, dir) = if let Some(options_value) = options { + (options_value.recursive, options_value.dir) + } else { + (false, None) + }; + let resolved_path = resolve_path(&window, path, dir)?; + read_dir_with_options( + &resolved_path, + recursive, + ReadDirOptions { + scope: Some(window.fs_scope()), + }, + ) + .with_context(|| format!("path: {}", resolved_path.display())) + .map_err(Into::into) + } } +#[cfg(feature = "allow-copy-file")] #[tauri::command] pub fn copy_file( window: Window, @@ -222,6 +244,7 @@ pub fn copy_file( Ok(()) } +#[cfg(feature = "allow-create-dir")] #[tauri::command] pub fn create_dir( window: Window, @@ -245,6 +268,7 @@ pub fn create_dir( Ok(()) } +#[cfg(feature = "allow-remove-dir")] #[tauri::command] pub fn remove_dir( window: Window, @@ -268,6 +292,7 @@ pub fn remove_dir( Ok(()) } +#[cfg(feature = "allow-remove-file")] #[tauri::command] pub fn remove_file( window: Window, @@ -280,6 +305,7 @@ pub fn remove_file( Ok(()) } +#[cfg(feature = "allow-rename-file")] #[tauri::command] pub fn rename_file( window: Window, @@ -300,6 +326,7 @@ pub fn rename_file( Ok(()) } +#[cfg(feature = "allow-exists")] #[tauri::command] pub fn exists( window: Window, @@ -310,86 +337,106 @@ pub fn exists( Ok(resolved_path.exists()) } -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Permissions { - readonly: bool, - #[cfg(unix)] - mode: u32, -} +#[cfg(feature = "allow-metadata")] +pub use metadata::*; -#[cfg(unix)] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UnixMetadata { - dev: u64, - ino: u64, - mode: u32, - nlink: u64, - uid: u32, - gid: u32, - rdev: u64, - blksize: u64, - blocks: u64, -} +#[cfg(feature = "allow-metadata")] +mod metadata { + use std::{ + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, + }; + + use serde::Serialize; -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Metadata { - accessed_at_ms: u64, - created_at_ms: u64, - modified_at_ms: u64, - is_dir: bool, - is_file: bool, - is_symlink: bool, - size: u64, - permissions: Permissions, #[cfg(unix)] - #[serde(flatten)] - unix: UnixMetadata, + use std::os::unix::fs::{MetadataExt, PermissionsExt}; #[cfg(windows)] - file_attributes: u32, -} + use std::os::windows::fs::MetadataExt; -fn system_time_to_ms(time: std::io::Result) -> u64 { - time.map(|t| { - let duration_since_epoch = t.duration_since(UNIX_EPOCH).unwrap(); - duration_since_epoch.as_millis() as u64 - }) - .unwrap_or_default() -} + use crate::Result; -#[tauri::command] -pub async fn metadata(path: PathBuf) -> Result { - let metadata = std::fs::metadata(path)?; - let file_type = metadata.file_type(); - let permissions = metadata.permissions(); - Ok(Metadata { - accessed_at_ms: system_time_to_ms(metadata.accessed()), - created_at_ms: system_time_to_ms(metadata.created()), - modified_at_ms: system_time_to_ms(metadata.modified()), - is_dir: file_type.is_dir(), - is_file: file_type.is_file(), - is_symlink: file_type.is_symlink(), - size: metadata.len(), - permissions: Permissions { - readonly: permissions.readonly(), - #[cfg(unix)] - mode: permissions.mode(), - }, + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + pub struct Permissions { + readonly: bool, + #[cfg(unix)] + mode: u32, + } + + #[cfg(unix)] + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + pub struct UnixMetadata { + dev: u64, + ino: u64, + mode: u32, + nlink: u64, + uid: u32, + gid: u32, + rdev: u64, + blksize: u64, + blocks: u64, + } + + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + pub struct Metadata { + accessed_at_ms: u64, + created_at_ms: u64, + modified_at_ms: u64, + is_dir: bool, + is_file: bool, + is_symlink: bool, + size: u64, + permissions: Permissions, #[cfg(unix)] - unix: UnixMetadata { - dev: metadata.dev(), - ino: metadata.ino(), - mode: metadata.mode(), - nlink: metadata.nlink(), - uid: metadata.uid(), - gid: metadata.gid(), - rdev: metadata.rdev(), - blksize: metadata.blksize(), - blocks: metadata.blocks(), - }, + #[serde(flatten)] + unix: UnixMetadata, #[cfg(windows)] - file_attributes: metadata.file_attributes(), - }) + file_attributes: u32, + } + + fn system_time_to_ms(time: std::io::Result) -> u64 { + time.map(|t| { + let duration_since_epoch = t.duration_since(UNIX_EPOCH).unwrap(); + duration_since_epoch.as_millis() as u64 + }) + .unwrap_or_default() + } + + #[tauri::command] + pub async fn metadata(path: PathBuf) -> Result { + let metadata = std::fs::metadata(path)?; + let file_type = metadata.file_type(); + let permissions = metadata.permissions(); + Ok(Metadata { + accessed_at_ms: system_time_to_ms(metadata.accessed()), + created_at_ms: system_time_to_ms(metadata.created()), + modified_at_ms: system_time_to_ms(metadata.modified()), + is_dir: file_type.is_dir(), + is_file: file_type.is_file(), + is_symlink: file_type.is_symlink(), + size: metadata.len(), + permissions: Permissions { + readonly: permissions.readonly(), + #[cfg(unix)] + mode: permissions.mode(), + }, + #[cfg(unix)] + unix: UnixMetadata { + dev: metadata.dev(), + ino: metadata.ino(), + mode: metadata.mode(), + nlink: metadata.nlink(), + uid: metadata.uid(), + gid: metadata.gid(), + rdev: metadata.rdev(), + blksize: metadata.blksize(), + blocks: metadata.blocks(), + }, + #[cfg(windows)] + file_attributes: metadata.file_attributes(), + }) + } } diff --git a/plugins/fs/src/lib.rs b/plugins/fs/src/lib.rs index 737189d5..24ea3dae 100644 --- a/plugins/fs/src/lib.rs +++ b/plugins/fs/src/lib.rs @@ -37,16 +37,27 @@ impl> FsExt for T { pub fn init() -> TauriPlugin> { PluginBuilder::>::new("fs") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-read-file")] commands::read_file, + #[cfg(feature = "allow-read-file")] commands::read_text_file, + #[cfg(feature = "allow-write-file")] commands::write_file, + #[cfg(feature = "allow-read-dir")] commands::read_dir, + #[cfg(feature = "allow-copy-file")] commands::copy_file, + #[cfg(feature = "allow-create-dir")] commands::create_dir, + #[cfg(feature = "allow-remove-dir")] commands::remove_dir, + #[cfg(feature = "allow-remove-file")] commands::remove_file, + #[cfg(feature = "allow-rename-file")] commands::rename_file, + #[cfg(feature = "allow-exists")] commands::exists, + #[cfg(feature = "allow-metadata")] commands::metadata ]) .setup(|app: &tauri::AppHandle, api| { diff --git a/plugins/global-shortcut/Cargo.toml b/plugins/global-shortcut/Cargo.toml index 19cd3c92..3f97f855 100644 --- a/plugins/global-shortcut/Cargo.toml +++ b/plugins/global-shortcut/Cargo.toml @@ -12,3 +12,8 @@ tauri.workspace = true log.workspace = true thiserror.workspace = true global-hotkey = "0.2.1" + +[features] +allow-register = [] +allow-unregister = [] +allow-is-registered = [] diff --git a/plugins/global-shortcut/src/lib.rs b/plugins/global-shortcut/src/lib.rs index 7c34fc21..9358b16e 100644 --- a/plugins/global-shortcut/src/lib.rs +++ b/plugins/global-shortcut/src/lib.rs @@ -9,7 +9,7 @@ use global_hotkey::{GlobalHotKeyEvent, GlobalHotKeyManager}; use tauri::{ api::ipc::CallbackFn, plugin::{Builder as PluginBuilder, TauriPlugin}, - AppHandle, Manager, Runtime, State, Window, + AppHandle, Manager, Runtime, Window, }; mod error; @@ -186,6 +186,7 @@ fn acquire_manager( .map_err(|e| Error::GlobalHotkey(e.to_string())) } +#[allow(dead_code)] fn parse_shortcut>(shortcut: S) -> Result { shortcut.as_ref().parse().map_err(Into::into) } @@ -200,10 +201,11 @@ where .map_err(|e| Error::GlobalHotkey(e.to_string())) } +#[cfg(feature = "allow-register")] #[tauri::command] fn register( window: Window, - global_shortcut: State<'_, GlobalShortcut>, + global_shortcut: tauri::State<'_, GlobalShortcut>, shortcut: String, handler: CallbackFn, ) -> Result<()> { @@ -213,10 +215,11 @@ fn register( ) } +#[cfg(feature = "allow-register")] #[tauri::command] fn register_all( window: Window, - global_shortcut: State<'_, GlobalShortcut>, + global_shortcut: tauri::State<'_, GlobalShortcut>, shortcuts: Vec, handler: CallbackFn, ) -> Result<()> { @@ -227,19 +230,21 @@ fn register_all( global_shortcut.register_all_internal(hotkeys, ShortcutSource::Ipc { window, handler }) } +#[cfg(feature = "allow-unregister")] #[tauri::command] fn unregister( _app: AppHandle, - global_shortcut: State<'_, GlobalShortcut>, + global_shortcut: tauri::State<'_, GlobalShortcut>, shortcut: String, ) -> Result<()> { global_shortcut.unregister(parse_shortcut(shortcut)?) } +#[cfg(feature = "allow-unregister")] #[tauri::command] fn unregister_all( _app: AppHandle, - global_shortcut: State<'_, GlobalShortcut>, + global_shortcut: tauri::State<'_, GlobalShortcut>, shortcuts: Vec, ) -> Result<()> { let mut hotkeys = Vec::new(); @@ -249,10 +254,11 @@ fn unregister_all( global_shortcut.unregister_all(hotkeys) } +#[cfg(feature = "allow-is-registered")] #[tauri::command] fn is_registered( _app: AppHandle, - global_shortcut: State<'_, GlobalShortcut>, + global_shortcut: tauri::State<'_, GlobalShortcut>, shortcut: String, ) -> Result { Ok(global_shortcut.is_registered(parse_shortcut(shortcut)?)) @@ -278,10 +284,15 @@ impl Builder { let handler = self.handler; PluginBuilder::new("globalShortcut") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-register")] register, + #[cfg(feature = "allow-register")] register_all, + #[cfg(feature = "allow-unregister")] unregister, + #[cfg(feature = "allow-unregister")] unregister_all, + #[cfg(feature = "allow-is-registered")] is_registered ]) .setup(move |app, _api| { diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml index b0e6ab32..74987191 100644 --- a/plugins/http/Cargo.toml +++ b/plugins/http/Cargo.toml @@ -23,3 +23,4 @@ http-multipart = [ "reqwest/multipart" ] native-tls = [ "reqwest/native-tls" ] native-tls-vendored = [ "reqwest/native-tls-vendored" ] rustls-tls = [ "reqwest/rustls-tls" ] +allow-request = [] \ No newline at end of file diff --git a/plugins/http/src/commands/mod.rs b/plugins/http/src/commands/mod.rs index 5e9d4c58..3aaee2f8 100644 --- a/plugins/http/src/commands/mod.rs +++ b/plugins/http/src/commands/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports, dead_code)] + use tauri::{path::SafePathBuf, AppHandle, Runtime, State}; use tauri_plugin_fs::FsExt; @@ -8,6 +10,7 @@ use client::{Body, ClientBuilder, FilePart, FormPart, HttpRequestBuilder, Respon pub use client::Client; +#[cfg(feature = "allow-request")] #[tauri::command] pub async fn create_client( _app: AppHandle, @@ -21,6 +24,7 @@ pub async fn create_client( Ok(id) } +#[cfg(feature = "allow-request")] #[tauri::command] pub async fn drop_client( _app: AppHandle, @@ -32,6 +36,7 @@ pub async fn drop_client( Ok(()) } +#[cfg(feature = "allow-request")] #[tauri::command] pub async fn request( app: AppHandle, diff --git a/plugins/http/src/lib.rs b/plugins/http/src/lib.rs index 348d26a2..7045e5bf 100644 --- a/plugins/http/src/lib.rs +++ b/plugins/http/src/lib.rs @@ -16,8 +16,8 @@ pub use error::Error; type Result = std::result::Result; type ClientId = u32; +#[allow(dead_code)] pub struct Http { - #[allow(dead_code)] app: AppHandle, pub(crate) clients: Mutex>, pub(crate) scope: scope::Scope, @@ -38,8 +38,11 @@ impl> HttpExt for T { pub fn init() -> TauriPlugin> { Builder::>::new("http") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-request")] commands::create_client, + #[cfg(feature = "allow-request")] commands::drop_client, + #[cfg(feature = "allow-request")] commands::request ]) .setup(|app, api| { diff --git a/plugins/http/src/scope.rs b/plugins/http/src/scope.rs index 0c620a9b..bf7c4071 100644 --- a/plugins/http/src/scope.rs +++ b/plugins/http/src/scope.rs @@ -9,6 +9,7 @@ use reqwest::Url; /// Scope for filesystem access. #[derive(Debug, Clone)] pub struct Scope { + #[allow(dead_code)] allowed_urls: Vec, } @@ -29,6 +30,7 @@ impl Scope { } /// Determines if the given URL is allowed on this scope. + #[allow(dead_code)] pub fn is_allowed(&self, url: &Url) -> bool { self.allowed_urls .iter() diff --git a/plugins/notification/Cargo.toml b/plugins/notification/Cargo.toml index f136b2e5..4c9653cc 100644 --- a/plugins/notification/Cargo.toml +++ b/plugins/notification/Cargo.toml @@ -29,3 +29,5 @@ win7-notifications = { version = "0.3.1", optional = true } [features] windows7-compat = [ "win7-notifications" ] +allow-request-permission = [] +allow-notify = [] \ No newline at end of file diff --git a/plugins/notification/src/commands.rs b/plugins/notification/src/commands.rs index 4af85585..bd51e222 100644 --- a/plugins/notification/src/commands.rs +++ b/plugins/notification/src/commands.rs @@ -4,7 +4,7 @@ use tauri::{command, AppHandle, Runtime, State}; -use crate::{Notification, NotificationData, PermissionState, Result}; +use crate::{Notification, PermissionState, Result}; #[command] pub(crate) async fn is_permission_granted( @@ -19,6 +19,7 @@ pub(crate) async fn is_permission_granted( } } +#[cfg(feature = "allow-request-permission")] #[command] pub(crate) async fn request_permission( _app: AppHandle, @@ -27,11 +28,12 @@ pub(crate) async fn request_permission( notification.request_permission() } +#[cfg(feature = "allow-notify")] #[command] pub(crate) async fn notify( _app: AppHandle, notification: State<'_, Notification>, - options: NotificationData, + options: crate::NotificationData, ) -> Result<()> { let mut builder = notification.builder(); builder.data = options; diff --git a/plugins/notification/src/lib.rs b/plugins/notification/src/lib.rs index 6e566fe2..03abdd30 100644 --- a/plugins/notification/src/lib.rs +++ b/plugins/notification/src/lib.rs @@ -214,7 +214,9 @@ impl> crate::NotificationExt for T { pub fn init() -> TauriPlugin { Builder::new("notification") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-notify")] commands::notify, + #[cfg(feature = "allow-request-permission")] commands::request_permission, commands::is_permission_granted ]) diff --git a/plugins/process/Cargo.toml b/plugins/process/Cargo.toml index f9025a7a..5e1b5fd6 100644 --- a/plugins/process/Cargo.toml +++ b/plugins/process/Cargo.toml @@ -7,3 +7,7 @@ license.workspace = true [dependencies] tauri.workspace = true + +[features] +allow-exit = [] +allow-restart = [] \ No newline at end of file diff --git a/plugins/process/src/commands.rs b/plugins/process/src/commands.rs index de2e7302..ca9e6050 100644 --- a/plugins/process/src/commands.rs +++ b/plugins/process/src/commands.rs @@ -1,10 +1,14 @@ +#![allow(unused_imports)] + use tauri::{AppHandle, Runtime}; +#[cfg(feature = "allow-exit")] #[tauri::command] pub fn exit(app: AppHandle, code: i32) { app.exit(code) } +#[cfg(feature = "allow-restart")] #[tauri::command] pub fn restart(app: AppHandle) { app.restart() diff --git a/plugins/process/src/lib.rs b/plugins/process/src/lib.rs index e38a4edc..566fc8bc 100644 --- a/plugins/process/src/lib.rs +++ b/plugins/process/src/lib.rs @@ -7,6 +7,11 @@ mod commands; pub fn init() -> TauriPlugin { Builder::new("process") - .invoke_handler(tauri::generate_handler![commands::exit, commands::restart]) + .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-exit")] + commands::exit, + #[cfg(feature = "allow-restart")] + commands::restart + ]) .build() } diff --git a/plugins/shell/Cargo.toml b/plugins/shell/Cargo.toml index fa517099..ae7ad75c 100644 --- a/plugins/shell/Cargo.toml +++ b/plugins/shell/Cargo.toml @@ -15,4 +15,10 @@ shared_child = "1" regex = "1" open = "4" encoding_rs = "0.8" -os_pipe = "1" \ No newline at end of file +os_pipe = "1" + +[features] +allow-execute = [] +allow-stdin-write = [] +allow-kill = [] +allow-open = [] diff --git a/plugins/shell/src/commands.rs b/plugins/shell/src/commands.rs index 226022d5..37590e68 100644 --- a/plugins/shell/src/commands.rs +++ b/plugins/shell/src/commands.rs @@ -1,177 +1,196 @@ -use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error}; +#![allow(unused_imports)] -use encoding_rs::Encoding; -use serde::{Deserialize, Serialize}; use tauri::{api::ipc::CallbackFn, Manager, Runtime, State, Window}; use crate::{ open::Program, process::{CommandEvent, TerminatedPayload}, - scope::ExecuteArgs, Shell, }; +#[allow(dead_code)] type ChildId = u32; -#[derive(Debug, Clone, Serialize)] -#[serde(tag = "event", content = "payload")] -#[non_exhaustive] -enum JSCommandEvent { - /// Stderr bytes until a newline (\n) or carriage return (\r) is found. - Stderr(Buffer), - /// Stdout bytes until a newline (\n) or carriage return (\r) is found. - Stdout(Buffer), - /// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string. - Error(String), - /// Command process terminated. - Terminated(TerminatedPayload), -} +#[cfg(feature = "allow-execute")] +pub use execute::*; + +#[cfg(feature = "allow-execute")] +mod execute { + use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error}; + + use encoding_rs::Encoding; + use serde::{Deserialize, Serialize}; + use tauri::{api::ipc::CallbackFn, Manager, Runtime, State, Window}; + + use crate::{ + process::{CommandEvent, TerminatedPayload}, + scope::ExecuteArgs, + Shell, + }; -fn get_event_buffer(line: Vec, encoding: EncodingWrapper) -> Result { - match encoding { - EncodingWrapper::Text(character_encoding) => match character_encoding { - Some(encoding) => Ok(Buffer::Text( - encoding.decode_with_bom_removal(&line).0.into(), - )), - None => String::from_utf8(line).map(Buffer::Text), - }, - EncodingWrapper::Raw => Ok(Buffer::Raw(line)), + use super::ChildId; + + #[derive(Debug, Clone, Serialize)] + #[serde(tag = "event", content = "payload")] + #[non_exhaustive] + enum JSCommandEvent { + /// Stderr bytes until a newline (\n) or carriage return (\r) is found. + Stderr(Buffer), + /// Stdout bytes until a newline (\n) or carriage return (\r) is found. + Stdout(Buffer), + /// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string. + Error(String), + /// Command process terminated. + Terminated(TerminatedPayload), } -} -impl JSCommandEvent { - pub fn new(event: CommandEvent, encoding: EncodingWrapper) -> Self { - match event { - CommandEvent::Terminated(payload) => JSCommandEvent::Terminated(payload), - CommandEvent::Error(error) => JSCommandEvent::Error(error), - CommandEvent::Stderr(line) => get_event_buffer(line, encoding) - .map(JSCommandEvent::Stderr) - .unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())), - CommandEvent::Stdout(line) => get_event_buffer(line, encoding) - .map(JSCommandEvent::Stdout) - .unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())), + fn get_event_buffer(line: Vec, encoding: EncodingWrapper) -> Result { + match encoding { + EncodingWrapper::Text(character_encoding) => match character_encoding { + Some(encoding) => Ok(Buffer::Text( + encoding.decode_with_bom_removal(&line).0.into(), + )), + None => String::from_utf8(line).map(Buffer::Text), + }, + EncodingWrapper::Raw => Ok(Buffer::Raw(line)), } } -} -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -#[allow(missing_docs)] -pub enum Buffer { - Text(String), - Raw(Vec), -} + impl JSCommandEvent { + pub fn new(event: CommandEvent, encoding: EncodingWrapper) -> Self { + match event { + CommandEvent::Terminated(payload) => JSCommandEvent::Terminated(payload), + CommandEvent::Error(error) => JSCommandEvent::Error(error), + CommandEvent::Stderr(line) => get_event_buffer(line, encoding) + .map(JSCommandEvent::Stderr) + .unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())), + CommandEvent::Stdout(line) => get_event_buffer(line, encoding) + .map(JSCommandEvent::Stdout) + .unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())), + } + } + } -#[derive(Debug, Copy, Clone)] -pub enum EncodingWrapper { - Raw, - Text(Option<&'static Encoding>), -} + #[derive(Debug, Clone, Deserialize, Serialize)] + #[serde(untagged)] + #[allow(missing_docs)] + pub enum Buffer { + Text(String), + Raw(Vec), + } -#[derive(Debug, Clone, Default, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CommandOptions { - #[serde(default)] - sidecar: bool, - cwd: Option, - // by default we don't add any env variables to the spawned process - // but the env is an `Option` so when it's `None` we clear the env. - #[serde(default = "default_env")] - env: Option>, - // Character encoding for stdout/stderr - encoding: Option, -} + #[derive(Debug, Copy, Clone)] + pub enum EncodingWrapper { + Raw, + Text(Option<&'static Encoding>), + } -#[allow(clippy::unnecessary_wraps)] -fn default_env() -> Option> { - Some(HashMap::default()) -} + #[derive(Debug, Clone, Default, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct CommandOptions { + #[serde(default)] + sidecar: bool, + cwd: Option, + // by default we don't add any env variables to the spawned process + // but the env is an `Option` so when it's `None` we clear the env. + #[serde(default = "default_env")] + env: Option>, + // Character encoding for stdout/stderr + encoding: Option, + } -#[tauri::command] -pub fn execute( - window: Window, - shell: State<'_, Shell>, - program: String, - args: ExecuteArgs, - on_event_fn: CallbackFn, - options: CommandOptions, -) -> crate::Result { - let mut command = if options.sidecar { - let program = PathBuf::from(program); - let program_as_string = program.display().to_string(); - let program_no_ext_as_string = program.with_extension("").display().to_string(); - let configured_sidecar = window - .config() - .tauri - .bundle - .external_bin - .as_ref() - .and_then(|bins| { - bins.iter() - .find(|b| b == &&program_as_string || b == &&program_no_ext_as_string) - }) - .cloned(); - if let Some(sidecar) = configured_sidecar { - shell - .scope - .prepare_sidecar(&program.to_string_lossy(), &sidecar, args)? + #[allow(clippy::unnecessary_wraps)] + fn default_env() -> Option> { + Some(HashMap::default()) + } + + #[tauri::command] + pub fn execute( + window: Window, + shell: State<'_, Shell>, + program: String, + args: ExecuteArgs, + on_event_fn: CallbackFn, + options: CommandOptions, + ) -> crate::Result { + let mut command = if options.sidecar { + let program = PathBuf::from(program); + let program_as_string = program.display().to_string(); + let program_no_ext_as_string = program.with_extension("").display().to_string(); + let configured_sidecar = window + .config() + .tauri + .bundle + .external_bin + .as_ref() + .and_then(|bins| { + bins.iter() + .find(|b| b == &&program_as_string || b == &&program_no_ext_as_string) + }) + .cloned(); + if let Some(sidecar) = configured_sidecar { + shell + .scope + .prepare_sidecar(&program.to_string_lossy(), &sidecar, args)? + } else { + return Err(crate::Error::SidecarNotAllowed(program)); + } } else { - return Err(crate::Error::SidecarNotAllowed(program)); - } - } else { - match shell.scope.prepare(&program, args) { - Ok(cmd) => cmd, - Err(e) => { - #[cfg(debug_assertions)] - eprintln!("{e}"); - return Err(crate::Error::ProgramNotAllowed(PathBuf::from(program))); + match shell.scope.prepare(&program, args) { + Ok(cmd) => cmd, + Err(e) => { + #[cfg(debug_assertions)] + eprintln!("{e}"); + return Err(crate::Error::ProgramNotAllowed(PathBuf::from(program))); + } } + }; + if let Some(cwd) = options.cwd { + command = command.current_dir(cwd); } - }; - if let Some(cwd) = options.cwd { - command = command.current_dir(cwd); - } - if let Some(env) = options.env { - command = command.envs(env); - } else { - command = command.env_clear(); - } - let encoding = match options.encoding { - Option::None => EncodingWrapper::Text(None), - Some(encoding) => match encoding.as_str() { - "raw" => EncodingWrapper::Raw, - _ => { - if let Some(text_encoding) = Encoding::for_label(encoding.as_bytes()) { - EncodingWrapper::Text(Some(text_encoding)) - } else { - return Err(crate::Error::UnknownEncoding(encoding)); + if let Some(env) = options.env { + command = command.envs(env); + } else { + command = command.env_clear(); + } + let encoding = match options.encoding { + Option::None => EncodingWrapper::Text(None), + Some(encoding) => match encoding.as_str() { + "raw" => EncodingWrapper::Raw, + _ => { + if let Some(text_encoding) = Encoding::for_label(encoding.as_bytes()) { + EncodingWrapper::Text(Some(text_encoding)) + } else { + return Err(crate::Error::UnknownEncoding(encoding)); + } } - } - }, - }; + }, + }; - let (mut rx, child) = command.spawn()?; + let (mut rx, child) = command.spawn()?; - let pid = child.pid(); - shell.children.lock().unwrap().insert(pid, child); - let children = shell.children.clone(); + let pid = child.pid(); + shell.children.lock().unwrap().insert(pid, child); + let children = shell.children.clone(); - tauri::async_runtime::spawn(async move { - while let Some(event) = rx.recv().await { - if matches!(event, crate::process::CommandEvent::Terminated(_)) { - children.lock().unwrap().remove(&pid); - }; - let js_event = JSCommandEvent::new(event, encoding); - let js = tauri::api::ipc::format_callback(on_event_fn, &js_event) - .expect("unable to serialize CommandEvent"); + tauri::async_runtime::spawn(async move { + while let Some(event) = rx.recv().await { + if matches!(event, crate::process::CommandEvent::Terminated(_)) { + children.lock().unwrap().remove(&pid); + }; + let js_event = JSCommandEvent::new(event, encoding); + let js = tauri::api::ipc::format_callback(on_event_fn, &js_event) + .expect("unable to serialize CommandEvent"); - let _ = window.eval(js.as_str()); - } - }); + let _ = window.eval(js.as_str()); + } + }); - Ok(pid) + Ok(pid) + } } +#[cfg(feature = "allow-stdin-write")] #[tauri::command] pub fn stdin_write( _window: Window, @@ -188,6 +207,7 @@ pub fn stdin_write( Ok(()) } +#[cfg(feature = "allow-kill")] #[tauri::command] pub fn kill( _window: Window, @@ -200,6 +220,7 @@ pub fn kill( Ok(()) } +#[cfg(feature = "allow-open")] #[tauri::command] pub fn open( _window: Window, diff --git a/plugins/shell/src/lib.rs b/plugins/shell/src/lib.rs index f9ffd1e7..46e8f325 100644 --- a/plugins/shell/src/lib.rs +++ b/plugins/shell/src/lib.rs @@ -65,9 +65,13 @@ impl> ShellExt for T { pub fn init() -> TauriPlugin> { Builder::>::new("shell") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-execute")] commands::execute, + #[cfg(feature = "allow-stdin-write")] commands::stdin_write, + #[cfg(feature = "allow-kill")] commands::kill, + #[cfg(feature = "allow-open")] commands::open ]) .setup(|app, api| { diff --git a/plugins/shell/src/scope.rs b/plugins/shell/src/scope.rs index 871f3940..481e4000 100644 --- a/plugins/shell/src/scope.rs +++ b/plugins/shell/src/scope.rs @@ -159,6 +159,7 @@ impl Scope { } /// Validates argument inputs and creates a Tauri sidecar [`Command`]. + #[allow(dead_code)] pub fn prepare_sidecar( &self, command_name: &str, @@ -169,11 +170,13 @@ impl Scope { } /// Validates argument inputs and creates a Tauri [`Command`]. + #[allow(dead_code)] pub fn prepare(&self, command_name: &str, args: ExecuteArgs) -> Result { self._prepare(command_name, args, None) } /// Validates argument inputs and creates a Tauri [`Command`]. + #[allow(dead_code)] pub fn _prepare( &self, command_name: &str, diff --git a/plugins/updater/Cargo.toml b/plugins/updater/Cargo.toml index 33e726ac..62191897 100644 --- a/plugins/updater/Cargo.toml +++ b/plugins/updater/Cargo.toml @@ -36,3 +36,5 @@ tokio-test = "0.4.2" native-tls = [ "reqwest/native-tls" ] native-tls-vendored = [ "reqwest/native-tls-vendored" ] rustls-tls = [ "reqwest/rustls-tls" ] +allow-check = [] +allow-download-and-install = [] \ No newline at end of file diff --git a/plugins/updater/src/commands.rs b/plugins/updater/src/commands.rs index 8de387dc..349a6679 100644 --- a/plugins/updater/src/commands.rs +++ b/plugins/updater/src/commands.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports, dead_code)] + use crate::{PendingUpdate, Result, UpdaterExt}; use http::header; @@ -42,6 +44,7 @@ impl<'de> Deserialize<'de> for HeaderMap { } } +#[cfg(feature = "allow-check")] #[tauri::command] pub(crate) async fn check( app: AppHandle, @@ -85,6 +88,7 @@ pub(crate) struct DownloadProgress { content_length: Option, } +#[cfg(feature = "allow-download-and-install")] #[tauri::command] pub(crate) async fn download_and_install( _app: AppHandle, diff --git a/plugins/updater/src/lib.rs b/plugins/updater/src/lib.rs index 7d4818b3..d89752c5 100644 --- a/plugins/updater/src/lib.rs +++ b/plugins/updater/src/lib.rs @@ -88,7 +88,9 @@ impl Builder { Ok(()) }) .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-check")] commands::check, + #[cfg(feature = "allow-download-and-install")] commands::download_and_install ]) .build() diff --git a/plugins/window/Cargo.toml b/plugins/window/Cargo.toml index 6fda6b38..e3b84176 100644 --- a/plugins/window/Cargo.toml +++ b/plugins/window/Cargo.toml @@ -13,3 +13,51 @@ thiserror.workspace = true [features] icon-png = ["tauri/icon-png"] icon-ico = ["tauri/icon-ico"] +allow-create = [] +allow-scale-factor = [] +allow-inner-position = [] +allow-outer-position = [] +allow-inner-size = [] +allow-outer-size = [] +allow-is-fullscreen = [] +allow-is-minimized = [] +allow-is-maximized = [] +allow-is-decorated = [] +allow-is-resizable = [] +allow-is-visible = [] +allow-title = [] +allow-current-monitor = [] +allow-primary-monitor = [] +allow-available-monitors = [] +allow-theme = [] +allow-center = [] +allow-request-user-attention = [] +allow-set-resizable = [] +allow-set-title = [] +allow-maximize = [] +allow-unmaximize = [] +allow-minimize = [] +allow-unminimize = [] +allow-show = [] +allow-hide = [] +allow-close = [] +allow-set-decorations = [] +allow-set-shadow = [] +allow-set-always-on-top = [] +allow-set-content-protected = [] +allow-set-size = [] +allow-set-min-size = [] +allow-set-max-size = [] +allow-set-position = [] +allow-set-fullscreen = [] +allow-set-focus = [] +allow-set-skip-taskbar = [] +allow-set-cursor-grab = [] +allow-set-cursor-visible = [] +allow-set-cursor-icon = [] +allow-set-cursor-position = [] +allow-set-ignore-cursor-events = [] +allow-start-dragging = [] +allow-print = [] +allow-set-icon = [] +allow-toggle-maximize = [] \ No newline at end of file diff --git a/plugins/window/src/commands.rs b/plugins/window/src/commands.rs index 453c1f98..232098fd 100644 --- a/plugins/window/src/commands.rs +++ b/plugins/window/src/commands.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports, dead_code)] + use serde::{Deserialize, Serialize, Serializer}; use tauri::{ utils::config::WindowConfig, AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition, @@ -57,6 +59,7 @@ impl From for Icon { } } +#[cfg(feature = "allow-create")] #[tauri::command] pub fn create(app: AppHandle, options: WindowConfig) -> Result<()> { tauri::window::WindowBuilder::from_config(&app, options).build()?; @@ -70,6 +73,7 @@ fn get_window(window: Window, label: Option) -> Result { #[tauri::command] @@ -79,6 +83,7 @@ macro_rules! getter { }; } +#[allow(unused_macros)] macro_rules! setter { ($cmd: ident) => { #[tauri::command] @@ -99,53 +104,99 @@ macro_rules! setter { }; } +#[cfg(feature = "allow-scale-factor")] getter!(scale_factor, f64); +#[cfg(feature = "allow-inner-position")] getter!(inner_position, PhysicalPosition); +#[cfg(feature = "allow-outer-position")] getter!(outer_position, PhysicalPosition); +#[cfg(feature = "allow-inner-size")] getter!(inner_size, PhysicalSize); +#[cfg(feature = "allow-outer-size")] getter!(outer_size, PhysicalSize); +#[cfg(feature = "allow-is-fullscreen")] getter!(is_fullscreen, bool); +#[cfg(feature = "allow-is-minimized")] getter!(is_minimized, bool); +#[cfg(feature = "allow-is-maximized")] getter!(is_maximized, bool); +#[cfg(feature = "allow-is-decorated")] getter!(is_decorated, bool); +#[cfg(feature = "allow-is-resizable")] getter!(is_resizable, bool); +#[cfg(feature = "allow-is-visible")] getter!(is_visible, bool); +#[cfg(feature = "allow-title")] getter!(title, String); +#[cfg(feature = "allow-current-monitor")] getter!(current_monitor, Option); +#[cfg(feature = "allow-primary-monitor")] getter!(primary_monitor, Option); +#[cfg(feature = "allow-available-monitors")] getter!(available_monitors, Vec); +#[cfg(feature = "allow-theme")] getter!(theme, Theme); +#[cfg(feature = "allow-center")] setter!(center); +#[cfg(feature = "allow-request-user-attention")] setter!(request_user_attention, Option); +#[cfg(feature = "allow-set-resizable")] setter!(set_resizable, bool); +#[cfg(feature = "allow-set-title")] setter!(set_title, &str); +#[cfg(feature = "allow-maximize")] setter!(maximize); +#[cfg(feature = "allow-unmaximize")] setter!(unmaximize); +#[cfg(feature = "allow-minimize")] setter!(minimize); +#[cfg(feature = "allow-unminimize")] setter!(unminimize); +#[cfg(feature = "allow-show")] setter!(show); +#[cfg(feature = "allow-hide")] setter!(hide); +#[cfg(feature = "allow-close")] setter!(close); +#[cfg(feature = "allow-set-decorations")] setter!(set_decorations, bool); +#[cfg(feature = "allow-set-shadow")] setter!(set_shadow, bool); +#[cfg(feature = "allow-set-always-on-top")] setter!(set_always_on_top, bool); +#[cfg(feature = "allow-set-content-protected")] setter!(set_content_protected, bool); +#[cfg(feature = "allow-set-size")] setter!(set_size, Size); +#[cfg(feature = "allow-set-min-size")] setter!(set_min_size, Option); +#[cfg(feature = "allow-set-max-size")] setter!(set_max_size, Option); +#[cfg(feature = "allow-set-position")] setter!(set_position, Position); +#[cfg(feature = "allow-set-fullscreen")] setter!(set_fullscreen, bool); +#[cfg(feature = "allow-set-focus")] setter!(set_focus); +#[cfg(feature = "allow-set-skip-taskbar")] setter!(set_skip_taskbar, bool); +#[cfg(feature = "allow-set-cursor-grab")] setter!(set_cursor_grab, bool); +#[cfg(feature = "allow-set-cursor-visible")] setter!(set_cursor_visible, bool); +#[cfg(feature = "allow-set-cursor-icon")] setter!(set_cursor_icon, CursorIcon); +#[cfg(feature = "allow-set-cursor-position")] setter!(set_cursor_position, Position); +#[cfg(feature = "allow-set-ignore-cursor-events")] setter!(set_ignore_cursor_events, bool); +#[cfg(feature = "allow-start-dragging")] setter!(start_dragging); +#[cfg(feature = "allow-print")] setter!(print); +#[cfg(feature = "allow-set-icon")] #[tauri::command] pub fn set_icon( window: Window, @@ -157,6 +208,7 @@ pub fn set_icon( .map_err(Into::into) } +#[cfg(feature = "allow-toggle-maximize")] #[tauri::command] pub fn toggle_maximize(window: Window, label: Option) -> Result<()> { let window = get_window(window, label)?; @@ -167,6 +219,7 @@ pub fn toggle_maximize(window: Window, label: Option) -> Ok(()) } +#[cfg(feature = "allow-toggle-maximize")] #[tauri::command] pub fn internal_toggle_maximize( window: Window, @@ -182,6 +235,7 @@ pub fn internal_toggle_maximize( Ok(()) } +#[cfg(debug_assertions)] #[tauri::command] pub fn internal_toggle_devtools( window: Window, diff --git a/plugins/window/src/lib.rs b/plugins/window/src/lib.rs index 513a5d32..f517f190 100644 --- a/plugins/window/src/lib.rs +++ b/plugins/window/src/lib.rs @@ -8,57 +8,107 @@ mod commands; pub fn init() -> TauriPlugin { Builder::new("window") .invoke_handler(tauri::generate_handler![ + #[cfg(feature = "allow-create")] commands::create, // getters + #[cfg(feature = "allow-scale-factor")] commands::scale_factor, + #[cfg(feature = "allow-inner-position")] commands::inner_position, + #[cfg(feature = "allow-outer-position")] commands::outer_position, + #[cfg(feature = "allow-inner-size")] commands::inner_size, + #[cfg(feature = "allow-outer-size")] commands::outer_size, + #[cfg(feature = "allow-is-fullscreen")] commands::is_fullscreen, + #[cfg(feature = "allow-is-minimized")] commands::is_minimized, + #[cfg(feature = "allow-is-maximized")] commands::is_maximized, + #[cfg(feature = "allow-is-decorated")] commands::is_decorated, + #[cfg(feature = "allow-is-resizable")] commands::is_resizable, + #[cfg(feature = "allow-is-visible")] commands::is_visible, + #[cfg(feature = "allow-title")] commands::title, + #[cfg(feature = "allow-current-monitor")] commands::current_monitor, + #[cfg(feature = "allow-primary-monitor")] commands::primary_monitor, + #[cfg(feature = "allow-available-monitors")] commands::available_monitors, + #[cfg(feature = "allow-theme")] commands::theme, // setters + #[cfg(feature = "allow-center")] commands::center, + #[cfg(feature = "allow-request-user-attention")] commands::request_user_attention, + #[cfg(feature = "allow-set-resizable")] commands::set_resizable, + #[cfg(feature = "allow-set-title")] commands::set_title, + #[cfg(feature = "allow-maximize")] commands::maximize, + #[cfg(feature = "allow-unmaximize")] commands::unmaximize, + #[cfg(feature = "allow-minimize")] commands::minimize, + #[cfg(feature = "allow-unminimize")] commands::unminimize, + #[cfg(feature = "allow-show")] commands::show, + #[cfg(feature = "allow-hide")] commands::hide, + #[cfg(feature = "allow-close")] commands::close, + #[cfg(feature = "allow-set-decorations")] commands::set_decorations, + #[cfg(feature = "allow-set-shadow")] commands::set_shadow, + #[cfg(feature = "allow-set-always-on-top")] commands::set_always_on_top, + #[cfg(feature = "allow-set-content-protected")] commands::set_content_protected, + #[cfg(feature = "allow-set-size")] commands::set_size, + #[cfg(feature = "allow-set-min-size")] commands::set_min_size, + #[cfg(feature = "allow-set-max-size")] commands::set_max_size, + #[cfg(feature = "allow-set-position")] commands::set_position, + #[cfg(feature = "allow-set-fullscreen")] commands::set_fullscreen, + #[cfg(feature = "allow-set-focus")] commands::set_focus, + #[cfg(feature = "allow-set-skip-taskbar")] commands::set_skip_taskbar, + #[cfg(feature = "allow-set-cursor-grab")] commands::set_cursor_grab, + #[cfg(feature = "allow-set-cursor-visible")] commands::set_cursor_visible, + #[cfg(feature = "allow-set-cursor-icon")] commands::set_cursor_icon, + #[cfg(feature = "allow-set-cursor-position")] commands::set_cursor_position, + #[cfg(feature = "allow-set-ignore-cursor-events")] commands::set_ignore_cursor_events, + #[cfg(feature = "allow-start-dragging")] commands::start_dragging, + #[cfg(feature = "allow-print")] commands::print, + #[cfg(feature = "allow-set-icon")] commands::set_icon, + #[cfg(feature = "allow-toggle-maximize")] commands::toggle_maximize, + #[cfg(feature = "allow-toggle-maximize")] commands::internal_toggle_maximize, + #[cfg(debug_assertions)] commands::internal_toggle_devtools, ]) .build()