diff --git a/espanso/src/cli/worker/engine/dispatch/executor/mod.rs b/espanso/src/cli/worker/engine/dispatch/executor/mod.rs
index 1a63b8b..169b41a 100644
--- a/espanso/src/cli/worker/engine/dispatch/executor/mod.rs
+++ b/espanso/src/cli/worker/engine/dispatch/executor/mod.rs
@@ -22,6 +22,7 @@ pub mod context_menu;
pub mod event_injector;
pub mod icon;
pub mod key_injector;
+pub mod secure_input;
pub trait InjectParamsProvider {
fn get(&self) -> InjectParams;
diff --git a/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs b/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs
new file mode 100644
index 0000000..8d393a9
--- /dev/null
+++ b/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs
@@ -0,0 +1,64 @@
+/*
+ * This file is part of espanso.
+ *
+ * Copyright (C) 2019-2021 Federico Terzi
+ *
+ * espanso is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * espanso is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with espanso. If not, see .
+ */
+use std::process::Stdio;
+
+use anyhow::{bail, Context};
+use log::{error, info};
+
+use crate::engine::dispatch::SecureInputManager;
+
+pub struct SecureInputManagerAdapter {}
+
+impl SecureInputManagerAdapter {
+ pub fn new() -> Self {
+ Self {}
+ }
+}
+
+impl SecureInputManager for SecureInputManagerAdapter {
+ fn display_secure_input_troubleshoot(&self) -> anyhow::Result<()> {
+ // TODO: replace with actual URL
+ // TODO: in the future, this might be a self-contained WebView window
+ opener::open_browser("https://espanso.org/docs")?;
+ Ok(())
+ }
+
+ fn launch_secure_input_autofix(&self) -> anyhow::Result<()> {
+ let espanso_path = std::env::current_exe()?;
+ let child = std::process::Command::new(espanso_path)
+ .args(&["workaround", "secure-input"])
+ .stdout(Stdio::piped())
+ .spawn()
+ .context("unable to spawn workaround process")?;
+ let output = child.wait_with_output()?;
+ let output_str = String::from_utf8_lossy(&output.stdout);
+ let error_str = String::from_utf8_lossy(&output.stderr);
+
+ if output.status.success() {
+ info!(
+ "Secure input workaround executed successfully: {}",
+ output_str
+ );
+ Ok(())
+ } else {
+ error!("Secure input autofix reported error: {}", error_str);
+ bail!("non-successful autofix status code");
+ }
+ }
+}
diff --git a/espanso/src/cli/worker/engine/mod.rs b/espanso/src/cli/worker/engine/mod.rs
index c919457..f7dc7a7 100644
--- a/espanso/src/cli/worker/engine/mod.rs
+++ b/espanso/src/cli/worker/engine/mod.rs
@@ -28,13 +28,7 @@ use espanso_path::Paths;
use espanso_ui::{event::UIEvent, UIRemote};
use log::{debug, error, info, warn};
-use crate::{cli::worker::{context::Context, engine::{
- dispatch::executor::{
- clipboard_injector::ClipboardInjectorAdapter, context_menu::ContextMenuHandlerAdapter,
- event_injector::EventInjectorAdapter, icon::IconHandlerAdapter,
- key_injector::KeyInjectorAdapter,
- },
- process::middleware::{
+use crate::{cli::worker::{context::Context, engine::{dispatch::executor::{clipboard_injector::ClipboardInjectorAdapter, context_menu::ContextMenuHandlerAdapter, event_injector::EventInjectorAdapter, icon::IconHandlerAdapter, key_injector::KeyInjectorAdapter, secure_input::SecureInputManagerAdapter}, process::middleware::{
image_resolve::PathProviderAdapter,
match_select::MatchSelectorAdapter,
matcher::{
@@ -47,8 +41,7 @@ use crate::{cli::worker::{context::Context, engine::{
extension::{clipboard::ClipboardAdapter, form::FormProviderAdapter},
RendererAdapter,
},
- },
- }, match_cache::{CombinedMatchCache, MatchCache}}, engine::event::ExitMode, preferences::Preferences};
+ }}, match_cache::{CombinedMatchCache, MatchCache}}, engine::event::ExitMode, preferences::Preferences};
use super::secure_input::SecureInputEvent;
@@ -196,6 +189,7 @@ pub fn initialize_and_spawn(
let key_injector = KeyInjectorAdapter::new(&*injector, &config_manager);
let context_menu_adapter = ContextMenuHandlerAdapter::new(&*ui_remote);
let icon_adapter = IconHandlerAdapter::new(&*ui_remote);
+ let secure_input_adapter = SecureInputManagerAdapter::new();
let dispatcher = crate::engine::dispatch::default(
&event_injector,
&clipboard_injector,
@@ -205,6 +199,7 @@ pub fn initialize_and_spawn(
&clipboard_injector,
&context_menu_adapter,
&icon_adapter,
+ &secure_input_adapter,
);
// Disable previously granted linux capabilities if not needed anymore
diff --git a/espanso/src/engine/dispatch/default.rs b/espanso/src/engine/dispatch/default.rs
index 4615643..86cc83c 100644
--- a/espanso/src/engine/dispatch/default.rs
+++ b/espanso/src/engine/dispatch/default.rs
@@ -17,7 +17,7 @@
* along with espanso. If not, see .
*/
-use super::{ContextMenuHandler, Event, IconHandler, ImageInjector};
+use super::{ContextMenuHandler, Event, IconHandler, ImageInjector, SecureInputManager};
use super::{ModeProvider, Dispatcher, Executor, KeyInjector, TextInjector, HtmlInjector};
pub struct DefaultDispatcher<'a> {
@@ -34,6 +34,7 @@ impl<'a> DefaultDispatcher<'a> {
image_injector: &'a dyn ImageInjector,
context_menu_handler: &'a dyn ContextMenuHandler,
icon_handler: &'a dyn IconHandler,
+ secure_input_manager: &'a dyn SecureInputManager,
) -> Self {
Self {
executors: vec![
@@ -56,7 +57,10 @@ impl<'a> DefaultDispatcher<'a> {
)),
Box::new(super::executor::icon_update::IconUpdateExecutor::new(
icon_handler,
- ))
+ )),
+ Box::new(super::executor::secure_input::SecureInputExecutor::new(
+ secure_input_manager,
+ )),
],
}
}
diff --git a/espanso/src/engine/dispatch/executor/mod.rs b/espanso/src/engine/dispatch/executor/mod.rs
index 54d7865..586244c 100644
--- a/espanso/src/engine/dispatch/executor/mod.rs
+++ b/espanso/src/engine/dispatch/executor/mod.rs
@@ -22,4 +22,5 @@ pub mod icon_update;
pub mod image_inject;
pub mod html_inject;
pub mod key_inject;
+pub mod secure_input;
pub mod text_inject;
\ No newline at end of file
diff --git a/espanso/src/engine/dispatch/executor/secure_input.rs b/espanso/src/engine/dispatch/executor/secure_input.rs
new file mode 100644
index 0000000..6cf56b0
--- /dev/null
+++ b/espanso/src/engine/dispatch/executor/secure_input.rs
@@ -0,0 +1,58 @@
+/*
+ * This file is part of espanso.
+ *
+ * Copyright (C) 2019-2021 Federico Terzi
+ *
+ * espanso is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * espanso is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with espanso. If not, see .
+ */
+
+use crate::engine::event::EventType;
+
+use super::super::{Event, Executor};
+use anyhow::Result;
+use log::error;
+
+pub trait SecureInputManager {
+ fn display_secure_input_troubleshoot(&self) -> Result<()>;
+ fn launch_secure_input_autofix(&self) -> Result<()>;
+}
+
+pub struct SecureInputExecutor<'a> {
+ manager: &'a dyn SecureInputManager,
+}
+
+impl<'a> SecureInputExecutor<'a> {
+ pub fn new(manager: &'a dyn SecureInputManager) -> Self {
+ Self { manager }
+ }
+}
+
+impl<'a> Executor for SecureInputExecutor<'a> {
+ fn execute(&self, event: &Event) -> bool {
+ if let EventType::DisplaySecureInputTroubleshoot = &event.etype {
+ if let Err(error) = self.manager.display_secure_input_troubleshoot() {
+ error!("unable to display secure input troubleshoot: {}", error);
+ }
+ return true;
+ } else if let EventType::LaunchSecureInputAutoFix = &event.etype {
+ if let Err(error) = self.manager.launch_secure_input_autofix() {
+ error!("unable to launch secure input autofix: {}", error);
+ }
+ return true;
+ }
+
+
+ false
+ }
+}
\ No newline at end of file
diff --git a/espanso/src/engine/dispatch/mod.rs b/espanso/src/engine/dispatch/mod.rs
index edf606e..0072d75 100644
--- a/espanso/src/engine/dispatch/mod.rs
+++ b/espanso/src/engine/dispatch/mod.rs
@@ -38,6 +38,7 @@ pub use executor::text_inject::{Mode, ModeProvider, TextInjector};
pub use executor::image_inject::{ImageInjector};
pub use executor::context_menu::{ContextMenuHandler};
pub use executor::icon_update::IconHandler;
+pub use executor::secure_input::SecureInputManager;
// TODO: move into module
pub trait KeyInjector {
@@ -53,6 +54,7 @@ pub fn default<'a>(
image_injector: &'a dyn ImageInjector,
context_menu_handler: &'a dyn ContextMenuHandler,
icon_handler: &'a dyn IconHandler,
+ secure_input_manager: &'a dyn SecureInputManager,
) -> impl Dispatcher + 'a {
default::DefaultDispatcher::new(
event_injector,
@@ -63,5 +65,6 @@ pub fn default<'a>(
image_injector,
context_menu_handler,
icon_handler,
+ secure_input_manager,
)
}
diff --git a/espanso/src/engine/event/mod.rs b/espanso/src/engine/event/mod.rs
index 029676c..36827cf 100644
--- a/espanso/src/engine/event/mod.rs
+++ b/espanso/src/engine/event/mod.rs
@@ -90,6 +90,10 @@ pub enum EventType {
// UI
ShowContextMenu(ui::ShowContextMenuEvent),
IconStatusChange(ui::IconStatusChangeEvent),
+ DisplaySecureInputTroubleshoot,
+
+ // Other
+ LaunchSecureInputAutoFix,
}
#[derive(Debug, Clone)]
diff --git a/espanso/src/engine/process/middleware/context_menu.rs b/espanso/src/engine/process/middleware/context_menu.rs
index 95efb32..654b4e3 100644
--- a/espanso/src/engine/process/middleware/context_menu.rs
+++ b/espanso/src/engine/process/middleware/context_menu.rs
@@ -29,15 +29,19 @@ const CONTEXT_ITEM_EXIT: u32 = 0;
const CONTEXT_ITEM_RELOAD: u32 = 1;
const CONTEXT_ITEM_ENABLE: u32 = 2;
const CONTEXT_ITEM_DISABLE: u32 = 3;
+const CONTEXT_ITEM_SECURE_INPUT_EXPLAIN: u32 = 4;
+const CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND: u32 = 5;
pub struct ContextMenuMiddleware {
is_enabled: RefCell,
+ is_secure_input_enabled: RefCell,
}
impl ContextMenuMiddleware {
pub fn new() -> Self {
Self {
is_enabled: RefCell::new(true),
+ is_secure_input_enabled: RefCell::new(false),
}
}
}
@@ -49,11 +53,48 @@ impl Middleware for ContextMenuMiddleware {
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
let mut is_enabled = self.is_enabled.borrow_mut();
+ let mut is_secure_input_enabled = self.is_secure_input_enabled.borrow_mut();
match &event.etype {
EventType::TrayIconClicked => {
// TODO: fetch top matches for the active config to be added
+ let mut items = vec![
+ MenuItem::Simple(if *is_enabled {
+ SimpleMenuItem {
+ id: CONTEXT_ITEM_DISABLE,
+ label: "Disable".to_string(),
+ }
+ } else {
+ SimpleMenuItem {
+ id: CONTEXT_ITEM_ENABLE,
+ label: "Enable".to_string(),
+ }
+ }),
+ MenuItem::Separator,
+ MenuItem::Simple(SimpleMenuItem {
+ id: CONTEXT_ITEM_RELOAD,
+ label: "Reload config".to_string(),
+ }),
+ MenuItem::Separator,
+ MenuItem::Simple(SimpleMenuItem {
+ id: CONTEXT_ITEM_EXIT,
+ label: "Exit espanso".to_string(),
+ }),
+ ];
+
+ if *is_secure_input_enabled {
+ items.insert(0, MenuItem::Simple(SimpleMenuItem {
+ id: CONTEXT_ITEM_SECURE_INPUT_EXPLAIN,
+ label: "Why is espanso not working?".to_string(),
+ }));
+ items.insert(1, MenuItem::Simple(SimpleMenuItem {
+ id: CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND,
+ label: "Launch SecureInput auto-fix".to_string(),
+ }));
+ items.insert(2, MenuItem::Separator);
+ }
+
// TODO: my idea is to use a set of reserved u32 ids for built-in
// actions such as Exit, Open Editor etc
// then we need some u32 for the matches, so we need to create
@@ -62,29 +103,7 @@ impl Middleware for ContextMenuMiddleware {
event.source_id,
EventType::ShowContextMenu(ShowContextMenuEvent {
// TODO: add actual entries
- items: vec![
- MenuItem::Simple(if *is_enabled {
- SimpleMenuItem {
- id: CONTEXT_ITEM_DISABLE,
- label: "Disable".to_string(),
- }
- } else {
- SimpleMenuItem {
- id: CONTEXT_ITEM_ENABLE,
- label: "Enable".to_string(),
- }
- }),
- MenuItem::Separator,
- MenuItem::Simple(SimpleMenuItem {
- id: CONTEXT_ITEM_RELOAD,
- label: "Reload config".to_string(),
- }),
- MenuItem::Separator,
- MenuItem::Simple(SimpleMenuItem {
- id: CONTEXT_ITEM_EXIT,
- label: "Exit espanso".to_string(),
- }),
- ],
+ items,
}),
);
}
@@ -106,6 +125,14 @@ impl Middleware for ContextMenuMiddleware {
dispatch(Event::caused_by(event.source_id, EventType::DisableRequest));
Event::caused_by(event.source_id, EventType::NOOP)
}
+ CONTEXT_ITEM_SECURE_INPUT_EXPLAIN => {
+ dispatch(Event::caused_by(event.source_id, EventType::DisplaySecureInputTroubleshoot));
+ Event::caused_by(event.source_id, EventType::NOOP)
+ }
+ CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND => {
+ dispatch(Event::caused_by(event.source_id, EventType::LaunchSecureInputAutoFix));
+ Event::caused_by(event.source_id, EventType::NOOP)
+ }
custom => {
// TODO: handle dynamic items
todo!()
@@ -120,6 +147,14 @@ impl Middleware for ContextMenuMiddleware {
*is_enabled = true;
event
}
+ EventType::SecureInputEnabled(_) => {
+ *is_secure_input_enabled = true;
+ event
+ }
+ EventType::SecureInputDisabled => {
+ *is_secure_input_enabled = false;
+ event
+ }
_ => event,
}
}