From 9bae918f986e7475c6d8f10821f6ca0847d79360 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Tue, 10 Mar 2020 20:45:56 +0100
Subject: [PATCH 1/9] Version bump 0.5.3

---
 Cargo.lock     | 2 +-
 Cargo.toml     | 2 +-
 snapcraft.yaml | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index e91938c..7c2fd3d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -370,7 +370,7 @@ dependencies = [
 
 [[package]]
 name = "espanso"
-version = "0.5.2"
+version = "0.5.3"
 dependencies = [
  "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index cdad161..3cb0fbe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "espanso"
-version = "0.5.2"
+version = "0.5.3"
 authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
 license = "GPL-3.0"
 description = "Cross-platform Text Expander written in Rust"
diff --git a/snapcraft.yaml b/snapcraft.yaml
index 1ca478a..d767423 100644
--- a/snapcraft.yaml
+++ b/snapcraft.yaml
@@ -1,5 +1,5 @@
 name: espanso
-version: 0.5.2
+version: 0.5.3
 summary: A Cross-platform Text Expander written in Rust
 description: |
   espanso is a Cross-platform, Text Expander written in Rust.

From 5ad14acf5c03913f6f75850d067d72acd9fc3ea3 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Tue, 10 Mar 2020 20:47:31 +0100
Subject: [PATCH 2/9] Disable conflict check by default, as it yields a lot of
 false positives and doesn't provide much value. See #177

---
 src/config/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/config/mod.rs b/src/config/mod.rs
index 8acf48d..9244868 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -47,7 +47,7 @@ fn default_filter_title() -> String{ "".to_owned() }
 fn default_filter_class() -> String{ "".to_owned() }
 fn default_filter_exec() -> String{ "".to_owned() }
 fn default_log_level() -> i32 { 0 }
-fn default_conflict_check() -> bool{ true }
+fn default_conflict_check() -> bool{ false }
 fn default_ipc_server_port() -> i32 { 34982 }
 fn default_use_system_agent() -> bool { true }
 fn default_config_caching_interval() -> i32 { 800 }

From 8913b59d2b80ad403cb2c9ea11554247663144aa Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Mon, 16 Mar 2020 19:09:04 +0100
Subject: [PATCH 3/9] Version bump 0.5.3

---
 Cargo.lock     | 2 +-
 Cargo.toml     | 2 +-
 snapcraft.yaml | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index e91938c..7c2fd3d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -370,7 +370,7 @@ dependencies = [
 
 [[package]]
 name = "espanso"
-version = "0.5.2"
+version = "0.5.3"
 dependencies = [
  "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index cdad161..3cb0fbe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "espanso"
-version = "0.5.2"
+version = "0.5.3"
 authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
 license = "GPL-3.0"
 description = "Cross-platform Text Expander written in Rust"
diff --git a/snapcraft.yaml b/snapcraft.yaml
index 1ca478a..d767423 100644
--- a/snapcraft.yaml
+++ b/snapcraft.yaml
@@ -1,5 +1,5 @@
 name: espanso
-version: 0.5.2
+version: 0.5.3
 summary: A Cross-platform Text Expander written in Rust
 description: |
   espanso is a Cross-platform, Text Expander written in Rust.

From a1b755d483958562119ef9049ad9812d2fc1c5a3 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Mon, 16 Mar 2020 19:11:07 +0100
Subject: [PATCH 4/9] Change windows CI packaging to use latest MSVC toolchain
 version. See #203

---
 packager.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/packager.py b/packager.py
index b558e47..358f1f9 100644
--- a/packager.py
+++ b/packager.py
@@ -82,10 +82,12 @@ def build_windows(package_info):
     msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
     print("Found Redists: ", msvc_dirs)
 
-    msvc_dir = msvc_dirs[0]
-    print("Using: ",msvc_dir)
-    if len(msvc_dir) == 0:
+    if len(msvc_dirs) == 0:
         raise Exception("Cannot find redistributable dlls")
+
+    msvc_dir = msvc_dirs[-1]  # Take the most recent version of the toolchain
+    print("Using: ",msvc_dir)
+
     dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")
 
     print("Found DLLs:")

From 82975bfbdcc84ab7c8a587640c7a09627f1bcd72 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Mon, 16 Mar 2020 19:17:35 +0100
Subject: [PATCH 5/9] Disable the conflict check by default, as it caused more
 harm than good.

---
 src/config/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/config/mod.rs b/src/config/mod.rs
index 8acf48d..9244868 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -47,7 +47,7 @@ fn default_filter_title() -> String{ "".to_owned() }
 fn default_filter_class() -> String{ "".to_owned() }
 fn default_filter_exec() -> String{ "".to_owned() }
 fn default_log_level() -> i32 { 0 }
-fn default_conflict_check() -> bool{ true }
+fn default_conflict_check() -> bool{ false }
 fn default_ipc_server_port() -> i32 { 34982 }
 fn default_use_system_agent() -> bool { true }
 fn default_config_caching_interval() -> i32 { 800 }

From 54f720eca122c4ba79301cbf7024df60fd70c372 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Mon, 16 Mar 2020 21:18:33 +0100
Subject: [PATCH 6/9] Add alternative package provider to avoid GIT problems

---
 Cargo.toml             |  4 +-
 src/main.rs            | 25 +++++++++---
 src/package/default.rs | 30 ++++++--------
 src/package/git.rs     | 33 ++++++++++++++++
 src/package/mod.rs     |  8 ++++
 src/package/zip.rs     | 88 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 162 insertions(+), 26 deletions(-)
 create mode 100644 src/package/git.rs
 create mode 100644 src/package/zip.rs

diff --git a/Cargo.toml b/Cargo.toml
index 3cb0fbe..2f2c89a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,13 +30,11 @@ git2 = {version = "0.10.1", features = ["https"]}
 tempfile = "3.1.0"
 dialoguer = "0.4.0"
 rand = "0.7.2"
+zip = "0.5.3"
 
 [target.'cfg(unix)'.dependencies]
 libc = "0.2.62"
 
-[target.'cfg(target_os = "macos")'.dependencies]
-zip = "0.5.3"
-
 [build-dependencies]
 cmake = "0.1.31"
 
diff --git a/src/main.rs b/src/main.rs
index 0505a6f..55c3312 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -43,8 +43,10 @@ use crate::ui::UIManager;
 use crate::protocol::*;
 use std::io::{BufReader, BufRead};
 use crate::package::default::DefaultPackageManager;
-use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult};
+use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult, PackageResolver};
 use std::sync::atomic::AtomicBool;
+use crate::package::git::GitPackageResolver;
+use crate::package::zip::ZipPackageResolver;
 
 mod ui;
 mod edit;
@@ -71,6 +73,12 @@ const LOG_FILE: &str = "espanso.log";
 fn main() {
     let install_subcommand = SubCommand::with_name("install")
         .about("Install a package. Equivalent to 'espanso package install'")
+        .arg(Arg::with_name("no-git")
+            .short("g")
+            .long("no-git")
+            .required(false)
+            .takes_value(false)
+            .help("Install packages avoiding the GIT package provider. Try this flag if the default mode is not working."))
         .arg(Arg::with_name("package_name")
             .help("Package name"));
 
@@ -742,7 +750,14 @@ fn install_main(_config_set: ConfigSet, matches: &ArgMatches) {
         exit(1);
     });
 
-    let mut package_manager = DefaultPackageManager::new_default();
+    let package_resolver: Box<dyn PackageResolver> = if matches.is_present("no-git") {
+        println!("Using alternative package provider");
+        Box::new(ZipPackageResolver::new())
+    }else{
+        Box::new(GitPackageResolver::new())
+    };
+
+    let mut package_manager = DefaultPackageManager::new_default(Some(package_resolver));
 
     if package_manager.is_index_outdated() {
         println!("Updating package index...");
@@ -808,7 +823,7 @@ fn remove_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
         exit(1);
     });
 
-    let package_manager = DefaultPackageManager::new_default();
+    let package_manager = DefaultPackageManager::new_default(None);
 
     let res = package_manager.remove_package(package_name);
 
@@ -833,7 +848,7 @@ fn remove_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
 }
 
 fn update_index_main(_config_set: ConfigSet) {
-    let mut package_manager = DefaultPackageManager::new_default();
+    let mut package_manager = DefaultPackageManager::new_default(None);
 
     let res = package_manager.update_index(true);
 
@@ -856,7 +871,7 @@ fn update_index_main(_config_set: ConfigSet) {
 }
 
 fn list_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
-    let package_manager = DefaultPackageManager::new_default();
+    let package_manager = DefaultPackageManager::new_default(None);
 
     let list = package_manager.list_local_packages();
 
diff --git a/src/package/default.rs b/src/package/default.rs
index dfbff4a..d3bbd7f 100644
--- a/src/package/default.rs
+++ b/src/package/default.rs
@@ -18,7 +18,7 @@
  */
 
 use std::path::{PathBuf, Path};
-use crate::package::{PackageIndex, UpdateResult, Package, InstallResult, RemoveResult};
+use crate::package::{PackageIndex, UpdateResult, Package, InstallResult, RemoveResult, PackageResolver};
 use std::error::Error;
 use std::fs::{File, create_dir};
 use std::io::{BufReader, BufRead};
@@ -31,6 +31,7 @@ use git2::Repository;
 use regex::Regex;
 use crate::package::RemoveResult::Removed;
 use std::collections::HashMap;
+use super::git::GitPackageResolver;
 
 const DEFAULT_PACKAGE_INDEX_FILE : &str = "package_index.json";
 
@@ -38,24 +39,28 @@ pub struct DefaultPackageManager {
     package_dir: PathBuf,
     data_dir: PathBuf,
 
+    package_resolver: Option<Box<dyn PackageResolver>>,
+
     local_index: Option<PackageIndex>,
 }
 
 impl DefaultPackageManager {
-    pub fn new(package_dir: PathBuf, data_dir: PathBuf) -> DefaultPackageManager {
+    pub fn new(package_dir: PathBuf, data_dir: PathBuf, package_resolver: Option<Box<dyn PackageResolver>>) -> DefaultPackageManager {
         let local_index = Self::load_local_index(&data_dir);
 
         DefaultPackageManager{
             package_dir,
             data_dir,
+            package_resolver,
             local_index
         }
     }
 
-    pub fn new_default() -> DefaultPackageManager {
+    pub fn new_default(package_resolver: Option<Box<dyn PackageResolver>>) -> DefaultPackageManager {
         DefaultPackageManager::new(
             crate::context::get_package_dir(),
-            crate::context::get_data_dir()
+            crate::context::get_data_dir(),
+            package_resolver,
         )
     }
 
@@ -89,12 +94,6 @@ impl DefaultPackageManager {
         Ok(index)
     }
 
-    fn clone_repo_to_temp(repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
-        let temp_dir = TempDir::new()?;
-        let _repo = Repository::clone(repo_url, temp_dir.path())?;
-        Ok(temp_dir)
-    }
-
     fn parse_package_from_readme(readme_path: &Path) -> Option<Package> {
         lazy_static! {
             static ref FIELD_REGEX: Regex = Regex::new(r###"^\s*(.*?)\s*:\s*"?(.*?)"?$"###).unwrap();
@@ -248,7 +247,7 @@ impl super::PackageManager for DefaultPackageManager {
             return Ok(AlreadyInstalled);
         }
 
-        let temp_dir = Self::clone_repo_to_temp(repo_url)?;
+        let temp_dir = self.package_resolver.as_ref().unwrap().clone_repo_to_temp(repo_url)?;
 
         let temp_package_dir = temp_dir.path().join(name);
         if !temp_package_dir.exists() {
@@ -337,7 +336,8 @@ mod tests {
 
         let package_manager = DefaultPackageManager::new(
             package_dir.path().clone().to_path_buf(),
-            data_dir.path().clone().to_path_buf()
+            data_dir.path().clone().to_path_buf(),
+            Some(Box::new(GitPackageResolver::new())),
         );
 
         TempPackageManager {
@@ -465,12 +465,6 @@ mod tests {
         assert_eq!(temp.package_manager.install_package("italian-accents").unwrap(), AlreadyInstalled);
     }
 
-    #[test]
-    fn test_clone_temp_repository() {
-        let cloned_dir = DefaultPackageManager::clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
-        assert!(cloned_dir.path().join("LICENSE").exists());
-    }
-
     #[test]
     fn test_install_package() {
         let mut temp = create_temp_package_manager(|_, data_dir| {
diff --git a/src/package/git.rs b/src/package/git.rs
new file mode 100644
index 0000000..f8d132b
--- /dev/null
+++ b/src/package/git.rs
@@ -0,0 +1,33 @@
+use tempfile::TempDir;
+use std::error::Error;
+use git2::Repository;
+use super::PackageResolver;
+
+pub struct GitPackageResolver;
+
+impl GitPackageResolver {
+    pub fn new() -> GitPackageResolver {
+        return GitPackageResolver{};
+    }
+}
+
+impl super::PackageResolver for GitPackageResolver {
+    fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
+        let temp_dir = TempDir::new()?;
+        let _repo = Repository::clone(repo_url, temp_dir.path())?;
+        Ok(temp_dir)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use tempfile::{TempDir, NamedTempFile};
+
+    #[test]
+    fn test_clone_temp_repository() {
+        let resolver = GitPackageResolver::new();
+        let cloned_dir = resolver.clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
+        assert!(cloned_dir.path().join("LICENSE").exists());
+    }
+}
\ No newline at end of file
diff --git a/src/package/mod.rs b/src/package/mod.rs
index 3ba505d..78ecaf1 100644
--- a/src/package/mod.rs
+++ b/src/package/mod.rs
@@ -17,9 +17,13 @@
  * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+pub(crate) mod git;
+pub(crate) mod zip;
 pub(crate) mod default;
+
 use serde::{Serialize, Deserialize};
 use std::error::Error;
+use tempfile::TempDir;
 
 pub trait PackageManager {
     fn is_index_outdated(&self) -> bool;
@@ -35,6 +39,10 @@ pub trait PackageManager {
     fn list_local_packages(&self) -> Vec<Package>;
 }
 
+pub trait PackageResolver {
+    fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>>;
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 pub struct Package {
     pub name: String,
diff --git a/src/package/zip.rs b/src/package/zip.rs
new file mode 100644
index 0000000..169e78d
--- /dev/null
+++ b/src/package/zip.rs
@@ -0,0 +1,88 @@
+use tempfile::TempDir;
+use std::error::Error;
+use super::PackageResolver;
+use std::io::{Cursor, copy, Read};
+use std::{fs, io};
+use std::fs::File;
+use log::debug;
+
+pub struct ZipPackageResolver;
+
+impl ZipPackageResolver {
+    pub fn new() -> ZipPackageResolver {
+        return ZipPackageResolver{};
+    }
+}
+
+impl super::PackageResolver for ZipPackageResolver {
+    fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
+        let temp_dir = TempDir::new()?;
+
+        let zip_url = repo_url.to_owned() + "/archive/master.zip";
+
+        // Download the archive from GitHub
+        let mut response = reqwest::get(&zip_url)?;
+
+        // Extract zip file
+        let mut buffer = Vec::new();
+        copy(&mut response, &mut buffer)?;
+
+        let reader = Cursor::new(buffer);
+
+        let mut archive = zip::ZipArchive::new(reader).unwrap();
+
+        // Find the root folder name
+        let mut root_folder = {
+            let mut root_folder = archive.by_index(0).unwrap();
+            let root_folder = root_folder.sanitized_name();
+            root_folder.to_str().unwrap().to_owned()
+        };
+        root_folder.push(std::path::MAIN_SEPARATOR);
+
+        for i in 1..archive.len() {
+            let mut file = archive.by_index(i).unwrap();
+
+            let current_path = file.sanitized_name();
+            let current_filename = current_path.to_str().unwrap();
+            let trimmed_filename = current_filename.trim_start_matches(&root_folder);
+
+            let outpath = temp_dir.path().join(trimmed_filename);
+
+            {
+                let comment = file.comment();
+                if !comment.is_empty() {
+                    debug!("File {} comment: {}", i, comment);
+                }
+            }
+
+            if (&*file.name()).ends_with('/') {
+                debug!("File {} extracted to \"{}\"", i, outpath.as_path().display());
+                fs::create_dir_all(&outpath).unwrap();
+            } else {
+                debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
+                if let Some(p) = outpath.parent() {
+                    if !p.exists() {
+                        fs::create_dir_all(&p).unwrap();
+                    }
+                }
+                let mut outfile = fs::File::create(&outpath).unwrap();
+                io::copy(&mut file, &mut outfile).unwrap();
+            }
+        }
+
+        Ok(temp_dir)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use tempfile::{TempDir, NamedTempFile};
+
+    #[test]
+    fn test_clone_temp_repository() {
+        let resolver = ZipPackageResolver::new();
+        let cloned_dir = resolver.clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
+        assert!(cloned_dir.path().join("LICENSE").exists());
+    }
+}
\ No newline at end of file

From 216f276cb1833e2bc398affac2edd4bad546deaa Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Mon, 16 Mar 2020 21:36:01 +0100
Subject: [PATCH 7/9] Fix bug that prevented Windows context menu from working
 correctly.

---
 native/libwinbridge/bridge.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp
index 3a771b9..dfb6d42 100644
--- a/native/libwinbridge/bridge.cpp
+++ b/native/libwinbridge/bridge.cpp
@@ -381,7 +381,7 @@ int32_t initialize(void * self, wchar_t * ico_path, wchar_t * bmp_path) {
 
         Rid[1].usUsagePage = 0x01;
         Rid[1].usUsage = 0x02;
-        Rid[1].dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK;   // adds HID mouse and also ignores legacy mouse messages
+        Rid[1].dwFlags = RIDEV_INPUTSINK;   // adds HID mouse and also ignores legacy mouse messages
         Rid[1].hwndTarget = window;
 
         if (RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE) {  // Something went wrong, error.

From f95457f76924109d6fe74bef5f3d2b0913ec805d Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Mon, 16 Mar 2020 22:16:12 +0100
Subject: [PATCH 8/9] Add automatic Systemd service path update after changing
 espanso path (for example after an update). Fix #199

---
 src/main.rs      | 43 +++++++++++++++++++++++++-----------------
 src/sysdaemon.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+), 17 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 55c3312..f67560b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,6 +47,7 @@ use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult,
 use std::sync::atomic::AtomicBool;
 use crate::package::git::GitPackageResolver;
 use crate::package::zip::ZipPackageResolver;
+use crate::sysdaemon::{verify, VerifyResult};
 
 mod ui;
 mod edit;
@@ -452,25 +453,33 @@ fn start_daemon(config_set: ConfigSet) {
 
     if config_set.default.use_system_agent && !force_unmanaged {
         // Make sure espanso is currently registered in systemd
-        let res = Command::new("systemctl")
-            .args(&["--user", "is-enabled", "espanso.service"])
-            .output();
-        if !res.unwrap().status.success() {
-            use dialoguer::Confirmation;
-            if Confirmation::new()
-                .with_text("espanso must be registered to systemd (user level) first. Do you want to proceed?")
-                .default(true)
-                .show_default(true)
-                .interact().expect("Unable to read user answer") {
-
+        let res = verify();
+        match res {
+            VerifyResult::EnabledAndValid => {
+                // Do nothing, everything is ok!
+            },
+            VerifyResult::EnabledButInvalidPath => {
+                eprintln!("Updating espanso service file with new path...");
+                unregister_main(config_set.clone());
                 register_main(config_set);
-            }else{
-                eprintln!("Please register espanso to systemd with this command:");
-                eprintln!("   espanso register");
-                // TODO: enable flag to use non-managed daemon mode
+            },
+            VerifyResult::NotEnabled => {
+                use dialoguer::Confirmation;
+                if Confirmation::new()
+                    .with_text("espanso must be registered to systemd (user level) first. Do you want to proceed?")
+                    .default(true)
+                    .show_default(true)
+                    .interact().expect("Unable to read user answer") {
 
-                std::process::exit(4);
-            }
+                    register_main(config_set);
+                }else{
+                    eprintln!("Please register espanso to systemd with this command:");
+                    eprintln!("   espanso register");
+                    // TODO: enable flag to use non-managed daemon mode
+
+                    std::process::exit(4);
+                }
+            },
         }
 
         // Start the espanso service
diff --git a/src/sysdaemon.rs b/src/sysdaemon.rs
index b13e0dc..4bbf06c 100644
--- a/src/sysdaemon.rs
+++ b/src/sysdaemon.rs
@@ -20,6 +20,7 @@
 // This functions are used to register/unregister espanso from the system daemon manager.
 
 use crate::config::ConfigSet;
+use crate::sysdaemon::VerifyResult::{EnabledAndValid, NotEnabled, EnabledButInvalidPath};
 
 // INSTALLATION
 
@@ -181,6 +182,54 @@ pub fn register(config_set: ConfigSet) {
     }
 }
 
+pub enum VerifyResult {
+    EnabledAndValid,
+    EnabledButInvalidPath,
+    NotEnabled,
+}
+
+#[cfg(target_os = "linux")]
+pub fn verify() -> VerifyResult {
+    use regex::Regex;
+    use std::process::{Command, ExitStatus};
+
+    // Check if espanso service is already registered
+    let res = Command::new("systemctl")
+        .args(&["--user", "is-enabled", "espanso"])
+        .output();
+    if let Ok(res) = res {
+        let output = String::from_utf8_lossy(res.stdout.as_slice());
+        let output = output.trim();
+        if !res.status.success() || output != "enabled" {
+            return NotEnabled
+        }
+    }
+
+    lazy_static! {
+        static ref ExecPathRegex: Regex = Regex::new("ExecStart=(?P<path>.*?)\\s").unwrap();
+    }
+
+    // Check if the currently registered path is valid
+    let res = Command::new("systemctl")
+        .args(&["--user", "cat", "espanso"])
+        .output();
+    if let Ok(res) = res {
+        let output = String::from_utf8_lossy(res.stdout.as_slice());
+        let output = output.trim();
+        if res.status.success() {
+            let caps = ExecPathRegex.captures(output).unwrap();
+            let path = caps.get(1).map_or("", |m| m.as_str());
+            let espanso_path = std::env::current_exe().expect("Could not get espanso executable path");
+
+            if espanso_path.to_string_lossy() != path {
+                return EnabledButInvalidPath
+            }
+        }
+    }
+
+    EnabledAndValid
+}
+
 #[cfg(target_os = "linux")]
 pub fn unregister(config_set: ConfigSet) {
     use std::process::{Command, ExitStatus};

From fd1df63ecdfbaa136fd810809970d8906c9427c1 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Tue, 17 Mar 2020 20:58:01 +0100
Subject: [PATCH 9/9] Fix import that broke tests on macOS

---
 src/main.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index f67560b..6218e71 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,7 +47,6 @@ use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult,
 use std::sync::atomic::AtomicBool;
 use crate::package::git::GitPackageResolver;
 use crate::package::zip::ZipPackageResolver;
-use crate::sysdaemon::{verify, VerifyResult};
 
 mod ui;
 mod edit;
@@ -435,6 +434,7 @@ fn start_daemon(config_set: ConfigSet) {
 #[cfg(target_os = "linux")]
 fn start_daemon(config_set: ConfigSet) {
     use std::process::{Command, Stdio};
+    use crate::sysdaemon::{verify, VerifyResult};
 
     // Check if Systemd is available in the system
     let status = Command::new("systemctl")