Initial commit
This commit is contained in:
commit
2366471f11
56
Main.hs
Normal file
56
Main.hs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import Types
|
||||||
|
import Pretty
|
||||||
|
import Parser
|
||||||
|
|
||||||
|
import Data.List (isInfixOf)
|
||||||
|
import System.FilePath (takeBaseName)
|
||||||
|
import System.Process (readProcess, callCommand)
|
||||||
|
|
||||||
|
|
||||||
|
-- Search functions --
|
||||||
|
|
||||||
|
byServer :: String -> Keychain -> [Item]
|
||||||
|
byServer s = byAttrib (Left "srv", Str s)
|
||||||
|
|
||||||
|
byAccount :: String -> Keychain -> [Item]
|
||||||
|
byAccount a = byAttrib (Left "acct", Str a)
|
||||||
|
|
||||||
|
byAttrib :: Attrib -> Keychain -> [Item]
|
||||||
|
byAttrib a = filter (elem a . attrs)
|
||||||
|
|
||||||
|
attrib :: Name -> Item -> Maybe Value
|
||||||
|
attrib a = lookup a . attrs
|
||||||
|
|
||||||
|
fuzzy :: String -> Keychain -> [Item]
|
||||||
|
fuzzy x = filter (any (isInfixOf x) . strings)
|
||||||
|
where
|
||||||
|
strings = map unvalue . attrs
|
||||||
|
unvalue (_, Str s) = s
|
||||||
|
unvalue _ = ""
|
||||||
|
|
||||||
|
|
||||||
|
-- Keychain access --
|
||||||
|
|
||||||
|
keychainList :: IO [FilePath]
|
||||||
|
keychainList =
|
||||||
|
runParser parseKeychainList <$>
|
||||||
|
readProcess "security" ["list-keychains"] ""
|
||||||
|
|
||||||
|
getKeychain :: IO Keychain
|
||||||
|
getKeychain = do
|
||||||
|
paths <- filter ((/="System") . takeBaseName) <$> keychainList
|
||||||
|
dump <- readProcess "security" ("dump-keychain" : "-d" : paths) ""
|
||||||
|
return (runParser parseKeychain dump)
|
||||||
|
|
||||||
|
sendClipboard :: String -> IO ()
|
||||||
|
sendClipboard text =
|
||||||
|
callCommand $ "echo " ++ (show text) ++ " | pbcopy"
|
||||||
|
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
res <- fuzzy "gog.com" <$> getKeychain
|
||||||
|
sendClipboard (content (head res))
|
||||||
|
pprint res
|
||||||
|
|
||||||
|
|
137
Parser.hs
Normal file
137
Parser.hs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Parser
|
||||||
|
( parseKeychain
|
||||||
|
, parseKeychainList
|
||||||
|
, runParser
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Types
|
||||||
|
import Control.Applicative
|
||||||
|
import Data.Attoparsec.ByteString.Char8
|
||||||
|
import Data.ByteString.Char8 (pack, unpack)
|
||||||
|
import Data.Hex (unhex)
|
||||||
|
import Data.Time
|
||||||
|
|
||||||
|
|
||||||
|
runParser :: Monoid a => Parser a -> String -> a
|
||||||
|
runParser parser = handle . parseOnly parser . pack
|
||||||
|
where
|
||||||
|
handle (Left _) = mempty
|
||||||
|
handle (Right x) = x
|
||||||
|
|
||||||
|
|
||||||
|
-- Parsers --
|
||||||
|
|
||||||
|
parseKeychain :: Parser Keychain
|
||||||
|
parseKeychain = many item
|
||||||
|
|
||||||
|
parseKeychainList :: Parser [FilePath]
|
||||||
|
parseKeychainList = many $
|
||||||
|
between (many space >> quote) quote anyChar
|
||||||
|
|
||||||
|
item :: Parser Item
|
||||||
|
item = do
|
||||||
|
string "keychain: "
|
||||||
|
path <- quoted
|
||||||
|
endOfLine
|
||||||
|
string "class: "
|
||||||
|
class_ <- iclass <|> sclass
|
||||||
|
endOfLine
|
||||||
|
string "attributes:"
|
||||||
|
endOfLine
|
||||||
|
attrs <- many entry
|
||||||
|
endOfLine
|
||||||
|
string "data:"
|
||||||
|
endOfLine
|
||||||
|
raw <- raw <|> hex' <|>
|
||||||
|
raw' <|> pure "" <* endOfLine
|
||||||
|
return Item { keychain = path
|
||||||
|
, itemClass = class_
|
||||||
|
, attrs = attrs
|
||||||
|
, content = raw }
|
||||||
|
|
||||||
|
entry :: Parser Attrib
|
||||||
|
entry = do
|
||||||
|
many space
|
||||||
|
name <- eitherP quoted hex
|
||||||
|
between (char '<') (char '>') anyChar
|
||||||
|
char '='
|
||||||
|
val <- value
|
||||||
|
return (name, val)
|
||||||
|
|
||||||
|
value :: Parser Value
|
||||||
|
value =
|
||||||
|
pure Null <$> string "<NULL>" <|>
|
||||||
|
Date <$> date <|>
|
||||||
|
Str <$> quoted <|>
|
||||||
|
Str <$> quoted' <|>
|
||||||
|
Num <$> hex
|
||||||
|
|
||||||
|
|
||||||
|
sclass :: Parser ItemClass
|
||||||
|
sclass = do
|
||||||
|
quote
|
||||||
|
name <- str
|
||||||
|
quote
|
||||||
|
return name
|
||||||
|
where
|
||||||
|
str = pure Inet <* string "inet" <|>
|
||||||
|
pure Genp <* string "genp"
|
||||||
|
|
||||||
|
iclass :: Parser ItemClass
|
||||||
|
iclass = Id <$> hex
|
||||||
|
|
||||||
|
|
||||||
|
-- Parser helpers --
|
||||||
|
|
||||||
|
between :: Parser a -> Parser b -> Parser c -> Parser [c]
|
||||||
|
between p q x = p >> manyTill x q
|
||||||
|
|
||||||
|
hex :: Parser Integer
|
||||||
|
hex = do
|
||||||
|
string "0x"
|
||||||
|
num <- hexadecimal
|
||||||
|
space
|
||||||
|
return num
|
||||||
|
|
||||||
|
-- decoded hex string
|
||||||
|
hex' :: Parser String
|
||||||
|
hex' =
|
||||||
|
between (string "0x") (space >> endOfLine) anyChar >>= unhex
|
||||||
|
|
||||||
|
quote :: Parser Char
|
||||||
|
quote = char '"'
|
||||||
|
|
||||||
|
-- literal string
|
||||||
|
quoted :: Parser String
|
||||||
|
quoted = between quote quote anyChar
|
||||||
|
|
||||||
|
-- hex followed by string version
|
||||||
|
quoted' :: Parser String
|
||||||
|
quoted' = hex >> space >> quoted
|
||||||
|
|
||||||
|
-- unescaped string
|
||||||
|
raw :: Parser String
|
||||||
|
raw = between quote (quote >> endOfLine) anyChar
|
||||||
|
|
||||||
|
-- hex followed by unescaped string
|
||||||
|
raw' :: Parser String
|
||||||
|
raw' = hex >> space >> raw
|
||||||
|
|
||||||
|
digits :: Read a => Int -> Parser a
|
||||||
|
digits n = read <$> count n digit
|
||||||
|
|
||||||
|
date :: Parser LocalTime
|
||||||
|
date = do
|
||||||
|
hex >> space >> quote
|
||||||
|
year <- digits 4
|
||||||
|
month <- digits 2
|
||||||
|
day <- digits 2
|
||||||
|
hour <- digits 2
|
||||||
|
min <- digits 2
|
||||||
|
sec <- digits 2
|
||||||
|
string "Z\\" >> decimal >> quote
|
||||||
|
return LocalTime { localDay = fromGregorian year month day
|
||||||
|
, localTimeOfDay = TimeOfDay hour min sec }
|
||||||
|
|
63
Pretty.hs
Normal file
63
Pretty.hs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{-# LANGUAGE RecordWildCards, ViewPatterns #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
|
||||||
|
|
||||||
|
module Pretty
|
||||||
|
( Pretty
|
||||||
|
, pretty
|
||||||
|
, pprint
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Types
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import System.FilePath (takeBaseName)
|
||||||
|
|
||||||
|
|
||||||
|
class Pretty a where
|
||||||
|
pretty :: a -> String
|
||||||
|
|
||||||
|
pprint :: Pretty a => a -> IO ()
|
||||||
|
pprint = putStrLn . pretty
|
||||||
|
|
||||||
|
names :: [(Name, String)]
|
||||||
|
names =
|
||||||
|
[ (Left "acct", "username" )
|
||||||
|
, (Left "srvr", "url" )
|
||||||
|
, (Left "cdat", "created" )
|
||||||
|
, (Left "mdat", "last change")
|
||||||
|
, (Left "desc", "description")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
instance Pretty Keychain where
|
||||||
|
pretty = intercalate spacer . map pretty
|
||||||
|
where spacer = '\n' : replicate 57 '~' ++ "\n\n"
|
||||||
|
|
||||||
|
instance Pretty Item where
|
||||||
|
pretty Item {..} = unlines $
|
||||||
|
[ "name: " ++ pretty title
|
||||||
|
, "content: " ++ password
|
||||||
|
, "keychain: " ++ takeBaseName keychain
|
||||||
|
, "class: " ++ show itemClass
|
||||||
|
, "info: "
|
||||||
|
] ++ map pretty entries
|
||||||
|
where
|
||||||
|
title = maybe (Str "") id (lookup (Right 7) attrs)
|
||||||
|
entries = filter ((`elem` keys) . fst) attrs
|
||||||
|
keys = map fst names
|
||||||
|
password = if length content < 45
|
||||||
|
then content
|
||||||
|
else take 45 content ++ "..."
|
||||||
|
|
||||||
|
instance Pretty Attrib where
|
||||||
|
pretty (n,v) = " " ++ pretty n ++ ": "++ pretty v
|
||||||
|
|
||||||
|
instance Pretty Name where
|
||||||
|
pretty (flip lookup names -> Just x) = x
|
||||||
|
pretty (Left x) = x
|
||||||
|
pretty (Right x) = show x
|
||||||
|
|
||||||
|
instance Pretty Value where
|
||||||
|
pretty (Date x) = show x
|
||||||
|
pretty (Num x) = show x
|
||||||
|
pretty (Str x) = x
|
||||||
|
pretty Null = ""
|
25
Types.hs
Normal file
25
Types.hs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
module Types where
|
||||||
|
|
||||||
|
import Data.Time (LocalTime)
|
||||||
|
|
||||||
|
data Item =
|
||||||
|
Item { keychain :: FilePath
|
||||||
|
, itemClass :: ItemClass
|
||||||
|
, attrs :: [Attrib]
|
||||||
|
, content :: String
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
data ItemClass =
|
||||||
|
Id Integer |
|
||||||
|
Inet |
|
||||||
|
Genp deriving (Eq, Show)
|
||||||
|
|
||||||
|
data Value =
|
||||||
|
Date LocalTime |
|
||||||
|
Str String |
|
||||||
|
Num Integer |
|
||||||
|
Null deriving (Eq, Show)
|
||||||
|
|
||||||
|
type Keychain = [Item]
|
||||||
|
type Attrib = (Name, Value)
|
||||||
|
type Name = Either String Integer
|
Loading…
Reference in New Issue
Block a user