diff --git a/src-tauri/src/printers.rs b/src-tauri/src/printers.rs index b5118f8..5763578 100644 --- a/src-tauri/src/printers.rs +++ b/src-tauri/src/printers.rs @@ -99,6 +99,75 @@ finally: pass "#; +#[cfg(target_os = "linux")] +const FIELDLENS_CUPS_REPAIR_HELPER: &str = r#"#!/usr/bin/python3 +import os +import subprocess +import sys +import tempfile + +target_printer = sys.argv[1] +target_port = sys.argv[2] +temp_filter = sys.argv[3] +filter_path = sys.argv[4] + +source_ppd = os.path.join("/etc/cups/ppd", f"{target_printer}.ppd") +spool_dir = "/var/cache/cups/fieldlens-print" + +with open(source_ppd, "r", encoding="utf-8", errors="replace") as source: + original = source.read() + +cleaned = "\n".join( + line for line in original.splitlines() + if "fieldlens-capture" not in line +) + "\n" + +prefilter = ( + "*% FieldLens original-document capture before cups-filters universal conversion\n" + "*cupsPreFilter: \"application/vnd.universal-input 0 fieldlens-capture\"\n" +) + +index = cleaned.rfind("*% End") +if index >= 0: + cleaned = cleaned[:index] + prefilter + cleaned[index:] +else: + cleaned += prefilter + +fd, ppd_path = tempfile.mkstemp(prefix="fieldlens-", suffix=".ppd") +os.close(fd) + +try: + with open(ppd_path, "w", encoding="utf-8") as target: + target.write(cleaned) + + subprocess.run(["install", "-m", "0755", temp_filter, filter_path], check=True) + subprocess.run(["install", "-d", "-m", "0777", spool_dir], check=True) + + subprocess.run([ + "lpadmin", "-p", "FieldLens-Print", "-E", + "-v", target_port, + "-P", ppd_path, + "-D", "FieldLens Print", + ], check=True) + + subprocess.run([ + "lpadmin", "-p", "FieldLens-Test-Capture", "-E", + "-v", "socket://127.0.0.1:9101", + "-P", ppd_path, + "-D", "FieldLens Test Capture", + ], check=True) + + for queue in ("FieldLens-Print", "FieldLens-Test-Capture"): + subprocess.run(["cupsenable", queue], check=True) + subprocess.run(["cupsaccept", queue], check=True) +finally: + try: + os.unlink(ppd_path) + except OSError: + pass +"#; + + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SystemPrinter { @@ -603,51 +672,42 @@ fn install_linux_virtual_printer(target_printer: &str) -> Result<()> { "selected printer '{target_printer}' is not installed or available in CUPS" ))?; - install_linux_capture_filter()?; - let ppd_path = build_fieldlens_ppd(target_printer)?; + let filter_dir = cups_filter_dir()?; + let filter_path = filter_dir.join("fieldlens-capture"); - let production_args = vec![ - "-p".to_string(), - "FieldLens-Print".to_string(), - "-E".to_string(), - "-v".to_string(), - target.port.clone(), - "-P".to_string(), - ppd_path.to_string_lossy().into_owned(), - "-D".to_string(), - VIRTUAL_PRINTER_NAME.to_string(), - ]; - run_linux_admin( - "lpadmin", - &production_args, - "configure FieldLens production CUPS printer", - )?; + let temp_filter = std::env::temp_dir().join(format!( + "fieldlens-capture-{}.py", + uuid::Uuid::new_v4() + )); + let temp_helper = std::env::temp_dir().join(format!( + "fieldlens-cups-repair-{}.py", + uuid::Uuid::new_v4() + )); - let test_args = vec![ - "-p".to_string(), - TEST_VIRTUAL_PRINTER_QUEUE.to_string(), - "-E".to_string(), - "-v".to_string(), - TEST_CAPTURE_DEVICE_URI.to_string(), - "-P".to_string(), - ppd_path.to_string_lossy().into_owned(), - "-D".to_string(), - TEST_VIRTUAL_PRINTER_NAME.to_string(), - ]; - let test_result = run_linux_admin( - "lpadmin", - &test_args, - "configure FieldLens test-capture CUPS printer", - ); - let _ = fs::remove_file(&ppd_path); - test_result?; + fs::write(&temp_filter, FIELDLENS_CUPS_FILTER) + .context("write FieldLens CUPS capture filter")?; + fs::write(&temp_helper, FIELDLENS_CUPS_REPAIR_HELPER) + .context("write FieldLens CUPS repair helper")?; - // Explicitly repair both CUPS state flags because Chromium disables Print - // when either is stale. - for queue in ["FieldLens-Print", TEST_VIRTUAL_PRINTER_QUEUE] { - run_cups_repair_command("cupsenable", &[queue])?; - run_cups_repair_command("cupsaccept", &[queue])?; + let status = Command::new("pkexec") + .arg("python3") + .arg(&temp_helper) + .arg(target_printer) + .arg(&target.port) + .arg(&temp_filter) + .arg(&filter_path) + .status() + .context("run elevated FieldLens CUPS repair")?; + + let _ = fs::remove_file(&temp_filter); + let _ = fs::remove_file(&temp_helper); + + if !status.success() { + return Err(anyhow!( + "FieldLens printer repair failed; administrator approval may be required" + )); } + Ok(()) }