module System.Certificate.X509.Unix
( getSystemPath
, readAll
, findCertificate
) where
import System.Directory (getDirectoryContents)
import System.Environment (getEnv)
import Data.List (isPrefixOf)
import Data.Either
import Data.Certificate.X509
import Data.Certificate.PEM
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Control.Applicative ((<$>))
import Control.Exception
import Control.Monad
import Prelude hiding (catch)
defaultSystemPath :: FilePath
defaultSystemPath = "/etc/ssl/certs/"
envPathOverride :: String
envPathOverride = "SYSTEM_CERTIFICATE_PATH"
getSystemPath :: IO FilePath
getSystemPath = catch (getEnv envPathOverride) inDefault
where
inDefault :: IOException -> IO FilePath
inDefault _ = return defaultSystemPath
data ReadErr =
Exception IOException
| CertError String
deriving (Show,Eq)
readCertificate :: FilePath -> IO (Either ReadErr X509)
readCertificate file = do
rawdata <- try $ B.readFile file :: IO (Either IOException B.ByteString)
either (return . Left . Exception) parseCert $ rawdata
where
parseCert pemdata = case parsePEMCert pemdata of
Nothing -> return $ Left $ CertError "certificate not in PEM format"
Just certdata -> do
return $ either (Left . CertError) Right $ decodeCertificate $ L.fromChunks [certdata]
readAll :: IO [Either ReadErr X509]
readAll = do
path <- getSystemPath
certfiles <- filter (not . isPrefixOf ".") <$> getDirectoryContents path
forM certfiles $ \certfile -> readCertificate (path ++ certfile)
findCertificate :: (X509 -> Bool) -> IO (Maybe X509)
findCertificate f = do
path <- getSystemPath
certfiles <- filter (not . isPrefixOf ".") <$> getDirectoryContents path
loop $ map (path ++) certfiles
where
loop [] = return Nothing
loop (x:xs) = do
ox509 <- readCertificate x
case ox509 of
Left _ -> loop xs
Right x509 -> if f x509 then return $ Just x509 else loop xs