| 
 | 1 | +{-# LANGUAGE DeriveGeneric #-}  | 
 | 2 | +{-# LANGUAGE OverloadedStrings #-}  | 
 | 3 | + | 
 | 4 | +module Distribution.Client.HookAccept  | 
 | 5 | +  ( HookAccept (..)  | 
 | 6 | +  , assertHookHash  | 
 | 7 | +  , loadHookHasheshMap  | 
 | 8 | +  , parseHooks  | 
 | 9 | +  ) where  | 
 | 10 | + | 
 | 11 | +import Distribution.Client.Compat.Prelude  | 
 | 12 | + | 
 | 13 | +import Data.ByteString.Char8 (ByteString)  | 
 | 14 | +import qualified Data.ByteString.Char8 as BS  | 
 | 15 | + | 
 | 16 | +import qualified Data.Map.Strict as Map  | 
 | 17 | + | 
 | 18 | +import Distribution.Client.Config (getConfigFilePath)  | 
 | 19 | +import Distribution.Client.Errors (CabalInstallException (..))  | 
 | 20 | +import Distribution.Client.HashValue (HashValue, hashValueFromHex, readFileHashValue, showHashValue)  | 
 | 21 | +import Distribution.Simple.Setup (Flag (..))  | 
 | 22 | +import Distribution.Simple.Utils (dieWithException)  | 
 | 23 | +import Distribution.Verbosity (normal)  | 
 | 24 | + | 
 | 25 | +import System.FilePath (takeDirectory, (</>))  | 
 | 26 | + | 
 | 27 | +data HookAccept  | 
 | 28 | +  = AcceptAlways  | 
 | 29 | +  | AcceptHash HashValue  | 
 | 30 | +  deriving (Eq, Show, Generic)  | 
 | 31 | + | 
 | 32 | +instance Monoid HookAccept where  | 
 | 33 | +  mempty = AcceptAlways -- Should never be needed.  | 
 | 34 | +  mappend = (<>)  | 
 | 35 | + | 
 | 36 | +instance Semigroup HookAccept where  | 
 | 37 | +  AcceptAlways <> AcceptAlways = AcceptAlways  | 
 | 38 | +  AcceptAlways <> AcceptHash h = AcceptHash h  | 
 | 39 | +  AcceptHash h <> AcceptAlways = AcceptHash h  | 
 | 40 | +  AcceptHash h <> _ = AcceptHash h  | 
 | 41 | + | 
 | 42 | +instance Binary HookAccept  | 
 | 43 | +instance Structured HookAccept  | 
 | 44 | + | 
 | 45 | +assertHookHash :: Map FilePath HookAccept -> FilePath -> IO ()  | 
 | 46 | +assertHookHash m fpath = do  | 
 | 47 | +  actualHash <- readFileHashValue fpath  | 
 | 48 | +  hsPath <- getHooksSecurityFilePath NoFlag  | 
 | 49 | +  case Map.lookup fpath m of  | 
 | 50 | +    Nothing ->  | 
 | 51 | +      dieWithException normal $  | 
 | 52 | +        HookAcceptUnknown hsPath fpath (showHashValue actualHash)  | 
 | 53 | +    Just AcceptAlways -> pure ()  | 
 | 54 | +    Just (AcceptHash expectedHash) ->  | 
 | 55 | +      when (actualHash /= expectedHash) $  | 
 | 56 | +        dieWithException normal $  | 
 | 57 | +          HookAcceptHashMismatch  | 
 | 58 | +            hsPath  | 
 | 59 | +            fpath  | 
 | 60 | +            (showHashValue expectedHash)  | 
 | 61 | +            (showHashValue actualHash)  | 
 | 62 | + | 
 | 63 | +getHooksSecurityFilePath :: Flag FilePath -> IO FilePath  | 
 | 64 | +getHooksSecurityFilePath configFileFlag = do  | 
 | 65 | +  hfpath <- getConfigFilePath configFileFlag  | 
 | 66 | +  pure $ takeDirectory hfpath </> "hooks-security"  | 
 | 67 | + | 
 | 68 | +loadHookHasheshMap :: Flag FilePath -> IO (Map FilePath HookAccept)  | 
 | 69 | +loadHookHasheshMap configFileFlag = do  | 
 | 70 | +  hookFilePath <- getHooksSecurityFilePath configFileFlag  | 
 | 71 | +  handleNotExists $ fmap parseHooks (BS.readFile hookFilePath)  | 
 | 72 | +  where  | 
 | 73 | +    handleNotExists :: IO (Map FilePath HookAccept) -> IO (Map FilePath HookAccept)  | 
 | 74 | +    handleNotExists action = catchIO action $ \_ -> return mempty  | 
 | 75 | + | 
 | 76 | +parseHooks :: ByteString -> Map FilePath HookAccept  | 
 | 77 | +parseHooks = Map.fromList . map parse . cleanUp . BS.lines  | 
 | 78 | +  where  | 
 | 79 | +    cleanUp :: [ByteString] -> [ByteString]  | 
 | 80 | +    cleanUp = filter (not . BS.null) . map rmComments  | 
 | 81 | + | 
 | 82 | +    rmComments :: ByteString -> ByteString  | 
 | 83 | +    rmComments = fst . BS.breakSubstring "--"  | 
 | 84 | + | 
 | 85 | +parse :: ByteString -> (FilePath, HookAccept)  | 
 | 86 | +parse bs =  | 
 | 87 | +  case BS.words bs of  | 
 | 88 | +    [fp, "AcceptAlways"] -> (BS.unpack fp, AcceptAlways)  | 
 | 89 | +    [fp, "AcceptHash"] -> buildAcceptHash fp "00"  | 
 | 90 | +    [fp, "AcceptHash", h] -> buildAcceptHash fp h  | 
 | 91 | +    _ -> error $ "Not able to parse:" ++ show bs  | 
 | 92 | +  where  | 
 | 93 | +    buildAcceptHash :: ByteString -> ByteString -> (FilePath, HookAccept)  | 
 | 94 | +    buildAcceptHash fp h =  | 
 | 95 | +      case hashValueFromHex h of  | 
 | 96 | +        Left err -> error $ "Distribution.Client.HookAccept.parse :" ++ err  | 
 | 97 | +        Right hv -> (BS.unpack fp, AcceptHash hv)  | 
0 commit comments