module Passman.Core.Config
(
Config(..)
, ConfigError(..)
, loadConfig
, saveConfig
) where
import Passman.Core.Config.Optional (OptionalConfig)
import Safe (readMay)
import System.Directory (getHomeDirectory)
import System.PosixCompat.Files (fileMode, getFileStatus, setFileMode, intersectFileModes, ownerModes)
import Control.Monad.List (guard)
import Control.Exception (tryJust)
import System.IO (IOMode(WriteMode, ReadMode), hPutStr, hGetContents, openFile, hClose)
import System.IO.Error (isDoesNotExistError)
import Data.Functor ((<$>))
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Error (ErrorT(..), Error(..), runErrorT)
data Config = Config {
masterPasswordHash :: String
, optionalConfig :: OptionalConfig
} deriving (Show, Read)
data ConfigError =
ConfigFileNotFound |
InvalidConfig FilePath
instance Error ConfigError where
noMsg = ConfigFileNotFound
strMsg = const ConfigFileNotFound
configFile :: IO FilePath
configFile = (++"/.passman") <$> getHomeDirectory
correctPermissions :: FilePath -> IO ()
correctPermissions fp = do
currentMode <- fileMode <$> getFileStatus fp
let newMode = intersectFileModes currentMode ownerModes
setFileMode fp newMode
saveConfig :: Config -> IO ()
saveConfig config = do
f <- configFile
out <- openFile f WriteMode
hPutStr out $ show config
hClose out
correctPermissions f
loadConfig :: IO (Either ConfigError Config)
loadConfig = runErrorT $ do
f <- liftIO configFile
h <- ErrorT $ tryJust fileNotFound $ openFile f ReadMode
contents <- liftIO $ hGetContents h
ErrorT $ return $ maybe (Left $ InvalidConfig f) Right $ readMay contents
fileNotFound :: IOError -> Maybe ConfigError
fileNotFound = fmap (const ConfigFileNotFound) . guard . isDoesNotExistError