module XMonad.Prompt.MyPass ( -- * Usages -- $usages mkPassPrompt ) where import Control.Monad (liftM) 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) -- $usages -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Prompt.Pass -- -- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt': -- -- > , ((modMask x , xK_p) , passPrompt xpconfig) -- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig) -- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig) -- -- For detailed instructions on: -- -- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings". -- -- - how to setup the password storage, see -- type Predicate = String -> String -> Bool getPassCompl :: [String] -> Predicate -> String -> IO [String] getPassCompl compls p s | length s <= minL , all ((> minL) . length) compls = return [] | otherwise = do return $ filter (p s) compls where minL = 3 type PromptLabel = String data Pass = Pass PromptLabel instance XPrompt Pass where showXPrompt (Pass prompt) = prompt ++ ": " commandToComplete _ c = c nextCompletion _ = getNextCompletion -- | Default password store folder in $HOME/.password-store -- passwordStoreFolderDefault :: String -> String passwordStoreFolderDefault home = combine home ".password-store" -- | Compute the password store's location. -- Use the PASSWORD_STORE_DIR environment variable to set the password store. -- If empty, return the password store located in user's home. -- passwordStoreFolder :: IO String passwordStoreFolder = getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory computePasswordStoreDir (Just storeDir) = return storeDir -- | A pass prompt factory -- mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X () mkPassPrompt promptLabel passwordFunction xpconfig = do passwords <- io (passwordStoreFolder >>= getPasswords) mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction -- | Retrieve the list of passwords from the password storage 'passwordStoreDir getPasswords :: FilePath -> IO [String] getPasswords passwordStoreDir = do files <- runProcessWithInput "find" [ passwordStoreDir, "-type", "f", "-name", "*.gpg", "-printf", "%P\n"] [] return $ map removeGpgExtension $ lines files removeGpgExtension :: String -> String removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file | otherwise = file