X.P.Pass: Clean up code

This commit is contained in:
Tony Zorman
2023-09-20 13:36:54 +02:00
parent a379850f50
commit 431ba22e3c

View File

@@ -1,3 +1,4 @@
{-# LANGUAGE LambdaCase #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
-- Module : XMonad.Prompt.Pass -- Module : XMonad.Prompt.Pass
@@ -64,26 +65,29 @@ module XMonad.Prompt.Pass
, passGenerateAndCopyPrompt , passGenerateAndCopyPrompt
, passGenerateAndCopyPrompt' , passGenerateAndCopyPrompt'
-- * Misc -- * One-time-passwords
, passOTPPrompt , passOTPPrompt
) where ) where
import System.Directory (getHomeDirectory) import System.Directory (getHomeDirectory)
import System.FilePath (combine, dropExtension, takeExtension) import System.FilePath (dropExtension, (</>))
import System.Posix.Env (getEnv) import System.Posix.Env (getEnv)
import XMonad.Core import XMonad
import XMonad.Prompt ( XPrompt import XMonad.Prelude
, showXPrompt import XMonad.Prompt
, commandToComplete ( XPConfig,
, nextCompletion XPrompt,
, getNextCompletion commandToComplete,
, XPConfig getNextCompletion,
, mkXPrompt mkXPrompt,
, searchPredicate) nextCompletion,
searchPredicate,
showXPrompt,
)
import XMonad.Util.Run (runProcessWithInput) import XMonad.Util.Run (runProcessWithInput)
-- $usage -- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: -- You can use this module with the following in your @xmonad.hs@:
-- --
-- > import XMonad.Prompt.Pass -- > import XMonad.Prompt.Pass
-- --
@@ -119,10 +123,8 @@ import XMonad.Util.Run (runProcessWithInput)
-- or @man 1 pass@. -- or @man 1 pass@.
-- --
type Predicate = String -> String -> Bool ---------------------------------------------------------------------------------
-- Prompt
getPassCompl :: [String] -> Predicate -> String -> IO [String]
getPassCompl compls p s = return $ filter (p s) compls
type PromptLabel = String type PromptLabel = String
@@ -133,28 +135,6 @@ instance XPrompt Pass where
commandToComplete _ c = c commandToComplete _ c = c
nextCompletion _ = getNextCompletion 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 = fmap 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
-- | A prompt to retrieve a password from a given entry. -- | A prompt to retrieve a password from a given entry.
-- --
passPrompt :: XPConfig -> X () passPrompt :: XPConfig -> X ()
@@ -219,63 +199,103 @@ passEditPrompt = passEditPrompt' "Edit password"
passEditPrompt' :: String -> XPConfig -> X () passEditPrompt' :: String -> XPConfig -> X ()
passEditPrompt' s = mkPassPrompt s editPassword passEditPrompt' s = mkPassPrompt s editPassword
-- | 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
where
getPassCompl :: [String] -> (String -> String -> Bool) -> String -> IO [String]
getPassCompl compls p s = return $ filter (p s) compls
-- 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
-- Default password store folder in @$HOME/.password-store@.
computePasswordStoreDir :: Maybe String -> IO String
computePasswordStoreDir = \case
Nothing -> getHomeDirectory <&> (</> ".password-store")
Just storeDir -> return storeDir
-- Retrieve the list of passwords from the password store @passwordStoreDir@.
getPasswords :: FilePath -> IO [String]
getPasswords passwordStoreDir = do
files <- runProcessWithInput "find" [
"-L", -- Traverse symlinks
passwordStoreDir,
"-type", "f",
"-name", "*.gpg",
"-printf", "%P\n"] []
return . map dropExtension $ lines files
---------------------------------------------------------------------------------
-- Selecting a password
-- | Select a password. -- | Select a password.
-- --
selectPassword :: String -> X () selectPassword :: String -> X ()
selectPassword passLabel = spawn $ "pass --clip \"" ++ escapeQuote passLabel ++ "\"" selectPassword = spawn . pass "--clip"
-- | Select an OTP. -- | Select a one-time-password and copy it to the clipboard.
-- --
selectOTP :: String -> X () selectOTP :: String -> X ()
selectOTP passLabel = spawn $ "pass otp --clip \"" ++ escapeQuote passLabel ++ "\"" selectOTP = spawn . pass "otp --clip"
-- | Select a one-time-password and type it out.
--
selectOTPType :: String -> X ()
selectOTPType = spawn . typeString . pass "otp"
-- | Generate a 30 characters password for a given entry. -- | Generate a 30 characters password for a given entry.
-- If the entry already exists, it is updated with a new password. -- If the entry already exists, it is updated with a new password.
-- --
generatePassword :: String -> X () generatePassword :: String -> X ()
generatePassword passLabel = spawn $ "pass generate --force \"" ++ escapeQuote passLabel ++ "\" 30" generatePassword passLabel = spawn $ pass "generate --force" passLabel ++ " 30"
-- | Generate a 30 characters password for a given entry. -- | Generate a 30 characters password for a given entry.
-- If the entry already exists, it is updated with a new password. -- If the entry already exists, it is updated with a new password.
-- After generating the password, it is copied to the clipboard. -- After generating the password, it is copied to the clipboard.
-- --
generateAndCopyPassword :: String -> X () generateAndCopyPassword :: String -> X ()
generateAndCopyPassword passLabel = spawn $ "pass generate --force -c \"" ++ escapeQuote passLabel ++ "\" 30" generateAndCopyPassword passLabel = spawn $ pass "generate --force -c" passLabel ++ " 30"
-- | Remove a password stored for a given entry. -- | Remove a password stored for a given entry.
-- --
removePassword :: String -> X () removePassword :: String -> X ()
removePassword passLabel = spawn $ "pass rm --force \"" ++ escapeQuote passLabel ++ "\"" removePassword = spawn . pass "rm --force"
-- | Edit a password stored for a given entry. -- | Edit a password stored for a given entry.
-- --
editPassword :: String -> X () editPassword :: String -> X ()
editPassword passLabel = spawn $ "pass edit \"" ++ escapeQuote passLabel ++ "\"" editPassword = spawn . pass "edit"
-- | Type a password stored for a given entry using xdotool. -- | Type a password stored for a given entry using xdotool.
-- --
typePassword :: String -> X () typePassword :: String -> X ()
typePassword passLabel = spawn $ "pass \"" ++ escapeQuote passLabel typePassword = spawn . typeString . pass ""
++ "\"|head -n1|tr -d '\n'|xdotool type --clearmodifiers --file -"
escapeQuote :: String -> String -- | Type the given string with @xdotool@.
escapeQuote = concatMap escape
where escape :: Char -> String
escape '"' = "\\\""
escape x = [x]
-- | Retrieve the list of passwords from the password store 'passwordStoreDir'
-- --
getPasswords :: FilePath -> IO [String] -- >>> typeString (pass "" "arXiv")
getPasswords passwordStoreDir = do -- "pass \"arXiv\" | head -n1 | tr -d '\n' | xdotool type --clearmodifiers --file -"
files <- runProcessWithInput "find" [ typeString :: String -> String
"-L", -- Traverse symlinks typeString cmd = cmd ++ " | head -n1 | tr -d '\n' | xdotool type --clearmodifiers --file -"
passwordStoreDir,
"-type", "f",
"-name", "*.gpg",
"-printf", "%P\n"] []
return . map removeGpgExtension $ lines files
removeGpgExtension :: String -> String -- | Generate a pass prompt.
removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file --
| otherwise = file -- >>> pass "otp" "git\"hub\""
-- "pass otp \"git\\\"hub\\\"\""
pass :: String -> String -> String
pass cmd label = concat ["pass ", cmd, " \"", concatMap escape label, "\""]
where
escape :: Char -> String
escape '"' = "\\\""
escape x = [x]