From f856f64e2b9f10f3b32e79862a6773f491f7cfc1 Mon Sep 17 00:00:00 2001 From: Petr Gadorek Date: Tue, 28 Jan 2025 09:00:25 +0100 Subject: [PATCH 1/2] EIM-111 path validation added --- src-tauri/src/lib.rs | 22 +++++++- .../wizard_steps/InstallationPathSelect.vue | 51 ++++++++++++++++--- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 8023cb2..96524a0 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -503,6 +503,25 @@ fn set_installation_path(app_handle: AppHandle, path: String) -> Result<(), Stri ); Ok(()) } +#[tauri::command] +fn is_path_empty_or_nonexistent(path: String) -> bool { + let path = Path::new(&path); + + // If path doesn't exist, return true + if !path.exists() { + return true; + } + + // If path exists, check if it's a directory and if it's empty + if path.is_dir() { + match fs::read_dir(path) { + Ok(mut entries) => entries.next().is_none(), // true if directory is empty + Err(_) => false, // return false if we can't read the directory + } + } else { + false // return false if it's not a directory + } +} #[tauri::command] fn load_settings(app_handle: AppHandle, path: &str) { @@ -1246,7 +1265,8 @@ pub fn run() { quit_app, save_config, get_logs_folder, - show_in_folder + show_in_folder, + is_path_empty_or_nonexistent, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/components/wizard_steps/InstallationPathSelect.vue b/src/components/wizard_steps/InstallationPathSelect.vue index 60c0c37..a8db792 100644 --- a/src/components/wizard_steps/InstallationPathSelect.vue +++ b/src/components/wizard_steps/InstallationPathSelect.vue @@ -21,7 +21,7 @@
-

{{ pathError }}

+

{{ pathError }}

Instalation path updated successfully!

@@ -54,19 +54,36 @@ export default { return { installPath: '', pathError: '', + pathIsValid: false, pathSelected: false }; }, + watch: { + async installPath(newValue, oldValue) { + console.log("installPath changed from", oldValue, "to", newValue); + // This function will run every time installPath changes + let result = await this.validatePath(newValue); + if (!result) { + this.pathError = `Path ${newValue} is not valid. Please choose a empty or non-existent directory.`; + this.pathIsValid = false; + } else { + this.pathError = `Path ${newValue} is valid.`; + this.pathIsValid = true; + } + } + }, computed: { - isValidPath() { - return true; // TODO: add some validation logic here + async isValidPath() { + console.log("Validating path:", path); + return this.installPath.length > 0 && this.validatePath(this.installPath); // return this.installPath.length > 0 && !this.pathError; } }, methods: { - validatePath(path) { - // Add path validation logic here if needed - return true; + async validatePath(path) { + let result = await invoke("is_path_empty_or_nonexistent", { path: path }); + console.log("Validating path:", path, "result:", result); + return result; }, async openFolderDialog() { const selected = await open({ @@ -79,6 +96,10 @@ export default { } }, async processInstallPath() { + if (!this.isValidPath) { + this.pathError = "Invalid path. Please choose a valid directory."; + return; + } console.log("Selected installation path:", this.installPath); await invoke("set_installation_path", { path: this.installPath }); this.nextstep(); @@ -200,4 +221,22 @@ export default { border-top: 1px solid #e5e7eb; } + +.error-message { + margin-left: 20%; + margin-right: 20%; + margin-bottom: 10px; + padding: 10px; +} + +.error-message-false { + background-color: #fdeae8; + border-left: 4px solid #E8362D; +} + +.error-message-true { + background-color: #eaf3fb; + border-left: 4px solid #5AC8FA; + color: #374151 +} \ No newline at end of file From 621f6edb1beef14e3266cda27460926cca728b06 Mon Sep 17 00:00:00 2001 From: Petr Gadorek Date: Wed, 29 Jan 2025 13:56:00 +0100 Subject: [PATCH 2/2] modified path checking for verifiing direct conflicts --- src-tauri/src/lib.rs | 165 ++++++++++-------- .../wizard_steps/InstallationPathSelect.vue | 7 +- 2 files changed, 98 insertions(+), 74 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 96524a0..27f0741 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,3 +1,5 @@ +#[cfg(target_os = "linux")] +use fork::{daemon, Fork}; use idf_im_lib::{ self, add_path_to_path, download_file, ensure_path, expand_tilde, idf_tools::get_tools_export_paths, python_utils::run_idf_tools_py, settings::Settings, @@ -6,6 +8,8 @@ use idf_im_lib::{ use log::{debug, error, info}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use std::fs::metadata; +use std::process::Command; use std::{ fs::{self, File}, io::Read, @@ -13,13 +17,7 @@ use std::{ sync::{mpsc, Mutex}, thread, }; -use tauri::{AppHandle, Manager}; -use std::process::Command; -use std::fs::metadata; -#[cfg(target_os = "linux")] -use fork::{daemon, Fork}; // dep: fork = "0.1" - - +use tauri::{AppHandle, Manager}; // dep: fork = "0.1" // Types and structs #[derive(Default, Serialize, Deserialize)] @@ -237,19 +235,19 @@ fn python_sanity_check(app_handle: AppHandle, python: Option<&str>) -> bool { #[tauri::command] fn get_logs_folder(app_handle: AppHandle) -> PathBuf { - match idf_im_lib::get_log_directory() { - Some(folder) => folder, - None => { - send_message( - &app_handle, - format!("Error getting log folder"), - "error".to_string(), - ); - log::error!("Error getting log folder"); //TODO: emit message - PathBuf::new() - } -}} - + match idf_im_lib::get_log_directory() { + Some(folder) => folder, + None => { + send_message( + &app_handle, + format!("Error getting log folder"), + "error".to_string(), + ); + log::error!("Error getting log folder"); //TODO: emit message + PathBuf::new() + } + } +} #[tauri::command] fn python_install(app_handle: AppHandle) -> bool { @@ -504,23 +502,50 @@ fn set_installation_path(app_handle: AppHandle, path: String) -> Result<(), Stri Ok(()) } #[tauri::command] -fn is_path_empty_or_nonexistent(path: String) -> bool { - let path = Path::new(&path); - - // If path doesn't exist, return true - if !path.exists() { - return true; - } - - // If path exists, check if it's a directory and if it's empty - if path.is_dir() { - match fs::read_dir(path) { - Ok(mut entries) => entries.next().is_none(), // true if directory is empty - Err(_) => false, // return false if we can't read the directory - } - } else { - false // return false if it's not a directory - } +async fn is_path_empty_or_nonexistent(app_handle: AppHandle, path: String) -> bool { + let path = Path::new(&path); + + // If path doesn't exist, return true + if !path.exists() { + return true; + } + + // If path exists, check if it's a directory and if it's empty + if path.is_dir() { + match fs::read_dir(path) { + Ok(mut entries) => { + if entries.next().is_none() { + //it's empty + return true; + } + let settings = get_locked_settings(&app_handle).unwrap(); + + let vers = match &settings.idf_versions { + Some(v) => v, + None => { + send_message( + &app_handle, + "No IDF versions selected. Please select at least one version to continue." + .to_string(), + "error".to_string(), + ); + return false; // something is broken we don't have any versions selected + } + }; + for v in vers { + let new_path = path.join(v); + if new_path.exists() { + return false; + } + } + return true; + } // true if directory is empty + Err(_) => false, // return false if we can't read the directory + } + } else { + //path is file which is conflicting with the directory + false // return false if it's not a directory + } } #[tauri::command] @@ -1150,20 +1175,20 @@ async fn start_simple_setup(app_handle: tauri::AppHandle) { #[tauri::command] fn show_in_folder(path: String) { - #[cfg(target_os = "windows")] - { - match Command::new("explorer") - .args(["/select,", &path]) // The comma after select is not a typo - .spawn() { - Ok(_) => {}, - Err(e) => { - error!("Failed to open folder with explorer: {}", e); - } + #[cfg(target_os = "windows")] + { + match Command::new("explorer") + .args(["/select,", &path]) // The comma after select is not a typo + .spawn() + { + Ok(_) => {} + Err(e) => { + error!("Failed to open folder with explorer: {}", e); + } } - - } + } - #[cfg(target_os = "linux")] + #[cfg(target_os = "linux")] { let path = if path.contains(",") { // see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76 @@ -1180,38 +1205,38 @@ fn show_in_folder(path: String) { }; // Try using xdg-open first - if Command::new("xdg-open") - .arg(&path) - .spawn() - .is_err() - { + if Command::new("xdg-open").arg(&path).spawn().is_err() { // Fallback to dbus-send if xdg-open fails let uri = format!("file://{}", path); match Command::new("dbus-send") - .args(["--session", "--dest=org.freedesktop.FileManager1", "--type=method_call", - "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1.ShowItems", - format!("array:string:\"{}\"", uri).as_str(), "string:\"\""]) - .spawn() { - Ok(_) => {}, - Err(e) => { - error!("Failed to open file with dbus-send: {}", e); - } + .args([ + "--session", + "--dest=org.freedesktop.FileManager1", + "--type=method_call", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1.ShowItems", + format!("array:string:\"{}\"", uri).as_str(), + "string:\"\"", + ]) + .spawn() + { + Ok(_) => {} + Err(e) => { + error!("Failed to open file with dbus-send: {}", e); } + } } } - #[cfg(target_os = "macos")] - { - match Command::new("open") - .args(["-R", &path]) - .spawn() { - Ok(_) => {}, + #[cfg(target_os = "macos")] + { + match Command::new("open").args(["-R", &path]).spawn() { + Ok(_) => {} Err(e) => { error!("Failed to open file with open: {}", e); } } - - } + } } use tauri::Emitter; diff --git a/src/components/wizard_steps/InstallationPathSelect.vue b/src/components/wizard_steps/InstallationPathSelect.vue index a8db792..f07f354 100644 --- a/src/components/wizard_steps/InstallationPathSelect.vue +++ b/src/components/wizard_steps/InstallationPathSelect.vue @@ -29,7 +29,8 @@
@@ -64,7 +65,7 @@ export default { // This function will run every time installPath changes let result = await this.validatePath(newValue); if (!result) { - this.pathError = `Path ${newValue} is not valid. Please choose a empty or non-existent directory.`; + this.pathError = `Path ${newValue} is not valid because it contains conflicting files or directories. Please choose a empty or non-existent directory.`; this.pathIsValid = false; } else { this.pathError = `Path ${newValue} is valid.`; @@ -76,13 +77,11 @@ export default { async isValidPath() { console.log("Validating path:", path); return this.installPath.length > 0 && this.validatePath(this.installPath); - // return this.installPath.length > 0 && !this.pathError; } }, methods: { async validatePath(path) { let result = await invoke("is_path_empty_or_nonexistent", { path: path }); - console.log("Validating path:", path, "result:", result); return result; }, async openFolderDialog() {