module XMonad.Prompt.Pass (
passPrompt
, passOTPPrompt
, passGeneratePrompt
, passGenerateAndCopyPrompt
, passRemovePrompt
, passEditPrompt
, passTypePrompt
) where
import XMonad.Core
import XMonad.Prompt ( XPrompt
, showXPrompt
, commandToComplete
, nextCompletion
, getNextCompletion
, XPConfig
, mkXPrompt
, searchPredicate)
import System.Directory (getHomeDirectory)
import System.FilePath (takeExtension, dropExtension, combine)
import System.Posix.Env (getEnv)
import XMonad.Util.Run (runProcessWithInput)
type Predicate = String -> String -> Bool
getPassCompl :: [String] -> Predicate -> String -> IO [String]
getPassCompl :: [String] -> Predicate -> String -> IO [String]
getPassCompl compls :: [String]
compls p :: Predicate
p s :: String
s = [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String]) -> [String] -> IO [String]
forall a b. (a -> b) -> a -> b
$ (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Predicate
p String
s) [String]
compls
type PromptLabel = String
newtype Pass = Pass PromptLabel
instance XPrompt Pass where
showXPrompt :: Pass -> String
showXPrompt (Pass prompt :: String
prompt) = String
prompt String -> String -> String
forall a. [a] -> [a] -> [a]
++ ": "
commandToComplete :: Pass -> String -> String
commandToComplete _ c :: String
c = String
c
nextCompletion :: Pass -> String -> [String] -> String
nextCompletion _ = String -> [String] -> String
getNextCompletion
passwordStoreFolderDefault :: String -> String
passwordStoreFolderDefault :: String -> String
passwordStoreFolderDefault home :: String
home = String -> String -> String
combine String
home ".password-store"
passwordStoreFolder :: IO String
passwordStoreFolder :: IO String
passwordStoreFolder =
String -> IO (Maybe String)
getEnv "PASSWORD_STORE_DIR" IO (Maybe String) -> (Maybe String -> IO String) -> IO String
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Maybe String -> IO String
computePasswordStoreDir
where computePasswordStoreDir :: Maybe String -> IO String
computePasswordStoreDir Nothing = (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> String
passwordStoreFolderDefault IO String
getHomeDirectory
computePasswordStoreDir (Just storeDir :: String
storeDir) = String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
storeDir
mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt :: String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt promptLabel :: String
promptLabel passwordFunction :: String -> X ()
passwordFunction xpconfig :: XPConfig
xpconfig = do
[String]
passwords <- IO [String] -> X [String]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO String
passwordStoreFolder IO String -> (String -> IO [String]) -> IO [String]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> IO [String]
getPasswords)
Pass
-> XPConfig -> (String -> IO [String]) -> (String -> X ()) -> X ()
forall p.
XPrompt p =>
p
-> XPConfig -> (String -> IO [String]) -> (String -> X ()) -> X ()
mkXPrompt (String -> Pass
Pass String
promptLabel) XPConfig
xpconfig ([String] -> Predicate -> String -> IO [String]
getPassCompl [String]
passwords (Predicate -> String -> IO [String])
-> Predicate -> String -> IO [String]
forall a b. (a -> b) -> a -> b
$ XPConfig -> Predicate
searchPredicate XPConfig
xpconfig) String -> X ()
passwordFunction
passPrompt :: XPConfig -> X ()
passPrompt :: XPConfig -> X ()
passPrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Select password" String -> X ()
selectPassword
passOTPPrompt :: XPConfig -> X ()
passOTPPrompt :: XPConfig -> X ()
passOTPPrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Select OTP" String -> X ()
selectOTP
passGeneratePrompt :: XPConfig -> X ()
passGeneratePrompt :: XPConfig -> X ()
passGeneratePrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Generate password" String -> X ()
generatePassword
passGenerateAndCopyPrompt :: XPConfig -> X ()
passGenerateAndCopyPrompt :: XPConfig -> X ()
passGenerateAndCopyPrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Generate and copy password" String -> X ()
generateAndCopyPassword
passRemovePrompt :: XPConfig -> X ()
passRemovePrompt :: XPConfig -> X ()
passRemovePrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Remove password" String -> X ()
removePassword
passTypePrompt :: XPConfig -> X ()
passTypePrompt :: XPConfig -> X ()
passTypePrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Type password" String -> X ()
typePassword
passEditPrompt :: XPConfig -> X ()
passEditPrompt :: XPConfig -> X ()
passEditPrompt = String -> (String -> X ()) -> XPConfig -> X ()
mkPassPrompt "Edit password" String -> X ()
editPassword
selectPassword :: String -> X ()
selectPassword :: String -> X ()
selectPassword passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass --clip \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\""
selectOTP :: String -> X ()
selectOTP :: String -> X ()
selectOTP passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass otp --clip \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\""
generatePassword :: String -> X ()
generatePassword :: String -> X ()
generatePassword passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass generate --force \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\" 30"
generateAndCopyPassword :: String -> X ()
generateAndCopyPassword :: String -> X ()
generateAndCopyPassword passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass generate --force -c \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\" 30"
removePassword :: String -> X ()
removePassword :: String -> X ()
removePassword passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass rm --force \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\""
editPassword :: String -> X ()
editPassword :: String -> X ()
editPassword passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass edit \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\""
typePassword :: String -> X ()
typePassword :: String -> X ()
typePassword passLabel :: String
passLabel = String -> X ()
forall (m :: * -> *). MonadIO m => String -> m ()
spawn (String -> X ()) -> String -> X ()
forall a b. (a -> b) -> a -> b
$ "pass \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQuote String
passLabel
String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\"|head -n1|tr -d '\n'|xdotool type --clearmodifiers --file -"
escapeQuote :: String -> String
escapeQuote :: String -> String
escapeQuote = (Char -> String) -> String -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
escape
where escape :: Char -> String
escape :: Char -> String
escape '"' = ['\\', '\"']
escape x :: Char
x = Char -> String
forall (m :: * -> *) a. Monad m => a -> m a
return Char
x
getPasswords :: FilePath -> IO [String]
getPasswords :: String -> IO [String]
getPasswords passwordStoreDir :: String
passwordStoreDir = do
String
files <- String -> [String] -> String -> IO String
forall (m :: * -> *).
MonadIO m =>
String -> [String] -> String -> m String
runProcessWithInput "find" [
"-L",
String
passwordStoreDir,
"-type", "f",
"-name", "*.gpg",
"-printf", "%P\n"] []
[String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String])
-> ([String] -> [String]) -> [String] -> IO [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
removeGpgExtension ([String] -> IO [String]) -> [String] -> IO [String]
forall a b. (a -> b) -> a -> b
$ String -> [String]
lines String
files
removeGpgExtension :: String -> String
removeGpgExtension :: String -> String
removeGpgExtension file :: String
file | String -> String
takeExtension String
file Predicate
forall a. Eq a => a -> a -> Bool
== ".gpg" = String -> String
dropExtension String
file
| Bool
otherwise = String
file