Fix 26 bugs, edge cases, and consistency issues from fifth audit pass
Critical: undo toast now trashes only batch output files (not entire dir), JPEG scanline write errors propagated, selective metadata write result returned. High: zero-dimension guards in ResizeConfig/fit_within, negative aspect ratio rejection, FM integration toggle infinite recursion guard, saturating counter arithmetic in executor. Medium: PNG compression level passed to oxipng, pct mode updates job_config, external file loading updates step indicator, CLI undo removes history entries, watch config write failures reported, fast-copy path reads image dimensions for rename templates, discovery excludes unprocessable formats (heic/svg/ico/jxl), CLI warns on invalid algorithm/overwrite values, resolve_collision trailing dot fix, generation guards on all preview threads to cancel stale results, default DPI aligned to 0, watermark text width uses char count not byte length. Low: binary path escaped in Nautilus extension, file dialog filter aligned with discovery, reset_wizard clears preset_mode and output_dir.
This commit is contained in:
@@ -76,6 +76,14 @@ pub fn regenerate_all() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sanitize a string for safe use in shell Exec= lines and XML command elements.
|
||||
/// Removes or replaces characters that could cause shell injection.
|
||||
fn shell_safe(s: &str) -> String {
|
||||
s.chars()
|
||||
.filter(|c| c.is_alphanumeric() || matches!(c, ' ' | '-' | '_' | '.' | '(' | ')' | ','))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn pixstrip_bin() -> String {
|
||||
// Try to find the pixstrip binary path
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
@@ -116,7 +124,7 @@ fn get_preset_names() -> Vec<String> {
|
||||
fn nautilus_extension_dir() -> PathBuf {
|
||||
let data = std::env::var("XDG_DATA_HOME")
|
||||
.unwrap_or_else(|_| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
|
||||
format!("{}/.local/share", home)
|
||||
});
|
||||
PathBuf::from(data).join("nautilus-python").join("extensions")
|
||||
@@ -143,11 +151,12 @@ fn install_nautilus() -> Result<()> {
|
||||
\x20 item.connect('activate', self._on_preset, '{}', files)\n\
|
||||
\x20 submenu.append_item(item)\n\n",
|
||||
name.replace(' ', "_"),
|
||||
name,
|
||||
name,
|
||||
name.replace('\'', "\\'"),
|
||||
name.replace('\'', "\\'"),
|
||||
));
|
||||
}
|
||||
|
||||
let escaped_bin = bin.replace('\\', "\\\\").replace('\'', "\\'");
|
||||
let script = format!(
|
||||
r#"import subprocess
|
||||
from gi.repository import Nautilus, GObject
|
||||
@@ -202,7 +211,7 @@ class PixstripExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||
subprocess.Popen(['{bin}', '--preset', preset_name, '--files'] + paths)
|
||||
"#,
|
||||
preset_items = preset_items,
|
||||
bin = bin,
|
||||
bin = escaped_bin,
|
||||
);
|
||||
|
||||
std::fs::write(nautilus_extension_path(), script)?;
|
||||
@@ -222,7 +231,7 @@ fn uninstall_nautilus() -> Result<()> {
|
||||
fn nemo_action_dir() -> PathBuf {
|
||||
let data = std::env::var("XDG_DATA_HOME")
|
||||
.unwrap_or_else(|_| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
|
||||
format!("{}/.local/share", home)
|
||||
});
|
||||
PathBuf::from(data).join("nemo").join("actions")
|
||||
@@ -261,12 +270,13 @@ fn install_nemo() -> Result<()> {
|
||||
"[Nemo Action]\n\
|
||||
Name=Pixstrip: {name}\n\
|
||||
Comment=Process with {name} preset\n\
|
||||
Exec={bin} --preset \"{name}\" --files %F\n\
|
||||
Exec={bin} --preset \"{safe_label}\" --files %F\n\
|
||||
Icon-Name=applications-graphics-symbolic\n\
|
||||
Selection=Any\n\
|
||||
Extensions=jpg;jpeg;png;webp;gif;tiff;tif;avif;bmp;\n\
|
||||
Mimetypes=image/*;\n",
|
||||
name = name,
|
||||
safe_label = shell_safe(name),
|
||||
bin = bin,
|
||||
);
|
||||
std::fs::write(action_path, action)?;
|
||||
@@ -300,7 +310,7 @@ fn uninstall_nemo() -> Result<()> {
|
||||
fn thunar_action_dir() -> PathBuf {
|
||||
let config = std::env::var("XDG_CONFIG_HOME")
|
||||
.unwrap_or_else(|_| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
|
||||
format!("{}/.config", home)
|
||||
});
|
||||
PathBuf::from(config).join("Thunar")
|
||||
@@ -337,14 +347,15 @@ fn install_thunar() -> Result<()> {
|
||||
actions.push_str(&format!(
|
||||
" <action>\n\
|
||||
\x20 <icon>applications-graphics-symbolic</icon>\n\
|
||||
\x20 <name>Pixstrip: {name}</name>\n\
|
||||
\x20 <command>{bin} --preset \"{name}\" --files %F</command>\n\
|
||||
\x20 <description>Process with {name} preset</description>\n\
|
||||
\x20 <name>Pixstrip: {xml_name}</name>\n\
|
||||
\x20 <command>{bin} --preset \"{safe_label}\" --files %F</command>\n\
|
||||
\x20 <description>Process with {xml_name} preset</description>\n\
|
||||
\x20 <patterns>*.jpg;*.jpeg;*.png;*.webp;*.gif;*.tiff;*.tif;*.avif;*.bmp</patterns>\n\
|
||||
\x20 <image-files/>\n\
|
||||
\x20 <directories/>\n\
|
||||
</action>\n",
|
||||
name = name,
|
||||
xml_name = name.replace('&', "&").replace('<', "<").replace('>', ">").replace('"', """),
|
||||
safe_label = shell_safe(name),
|
||||
bin = bin,
|
||||
));
|
||||
}
|
||||
@@ -367,7 +378,7 @@ fn uninstall_thunar() -> Result<()> {
|
||||
fn dolphin_service_dir() -> PathBuf {
|
||||
let data = std::env::var("XDG_DATA_HOME")
|
||||
.unwrap_or_else(|_| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
|
||||
format!("{}/.local/share", home)
|
||||
});
|
||||
PathBuf::from(data).join("kio").join("servicemenus")
|
||||
@@ -410,9 +421,10 @@ fn install_dolphin() -> Result<()> {
|
||||
"[Desktop Action Preset{i}]\n\
|
||||
Name={name}\n\
|
||||
Icon=applications-graphics-symbolic\n\
|
||||
Exec={bin} --preset \"{name}\" --files %F\n\n",
|
||||
Exec={bin} --preset \"{safe_label}\" --files %F\n\n",
|
||||
i = i,
|
||||
name = name,
|
||||
safe_label = shell_safe(name),
|
||||
bin = bin,
|
||||
));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user