11
11
--
12
12
-- Functions for fetching packages
13
13
-----------------------------------------------------------------------------
14
- {-# LANGUAGE RecordWildCards #-}
14
+ {-# LANGUAGE RecordWildCards, ScopedTypeVariables #-}
15
15
module Distribution.Client.FetchUtils (
16
16
17
17
-- * fetching packages
@@ -22,6 +22,7 @@ module Distribution.Client.FetchUtils (
22
22
-- ** specifically for repo packages
23
23
checkRepoTarballFetched ,
24
24
fetchRepoTarball ,
25
+ verifyFetchedTarball ,
25
26
26
27
-- ** fetching packages asynchronously
27
28
asyncFetchPackages ,
@@ -43,7 +44,7 @@ import Distribution.Client.HttpUtils
43
44
import Distribution.Package
44
45
( PackageId , packageName , packageVersion )
45
46
import Distribution.Simple.Utils
46
- ( notice , info , debug , die' )
47
+ ( notice , info , debug , warn , die' )
47
48
import Distribution.Verbosity
48
49
( verboseUnmarkOutput )
49
50
import Distribution.Client.GlobalFlags
@@ -56,7 +57,8 @@ import qualified Control.Exception.Safe as Safe
56
57
import Control.Concurrent.Async
57
58
import Control.Concurrent.MVar
58
59
import System.Directory
59
- ( doesFileExist , createDirectoryIfMissing , getTemporaryDirectory )
60
+ ( doesFileExist , createDirectoryIfMissing , getTemporaryDirectory
61
+ , getFileSize )
60
62
import System.IO
61
63
( openTempFile , hClose )
62
64
import System.FilePath
@@ -67,6 +69,8 @@ import Network.URI
67
69
( URI (uriPath ) )
68
70
69
71
import qualified Hackage.Security.Client as Sec
72
+ import qualified Hackage.Security.Util.Path as Sec
73
+ import qualified Hackage.Security.Util.Checked as Sec
70
74
71
75
-- ------------------------------------------------------------
72
76
-- * Actually fetch things
@@ -118,6 +122,34 @@ checkRepoTarballFetched repo pkgid = do
118
122
then return (Just file)
119
123
else return Nothing
120
124
125
+ verifyFetchedTarball :: Verbosity -> RepoContext -> Repo -> PackageId -> IO Bool
126
+ verifyFetchedTarball verbosity repoCtxt repo pkgid =
127
+ let file = packageFile repo pkgid
128
+ handleError :: IO Bool -> IO Bool
129
+ handleError act = do
130
+ res <- Safe. try act
131
+ case res of
132
+ Left e -> warn verbosity (" Error verifying fetched tarball " ++ file ++ " , will redownload: " ++ show (e :: SomeException )) >> pure False
133
+ Right b -> pure b
134
+ in handleError $ case repo of
135
+ -- a secure repo has hashes we can compare against to confirm this is the correct file.
136
+ RepoSecure {} ->
137
+ repoContextWithSecureRepo repoCtxt repo $ \ repoSecure ->
138
+ Sec. withIndex repoSecure $ \ callbacks ->
139
+ let warnAndFail s = warn verbosity (" Fetched tarball " ++ file ++ " does not match server, will redownload: " ++ s) >> return False
140
+ -- the do block in parens is due to dealing with the checked exceptions mechanism.
141
+ in (do fileInfo <- Sec. indexLookupFileInfo callbacks pkgid
142
+ sz <- Sec. FileLength . fromInteger <$> getFileSize file
143
+ if sz /= Sec. fileInfoLength (Sec. trusted fileInfo)
144
+ then warnAndFail " file length mismatch"
145
+ else do
146
+ res <- Sec. compareTrustedFileInfo (Sec. trusted fileInfo) <$> Sec. computeFileInfo (Sec. Path file :: Sec. Path Sec. Absolute )
147
+ if res
148
+ then pure True
149
+ else warnAndFail " file hash mismatch" )
150
+ `Sec.catchChecked` (\ (e :: Sec. InvalidPackageException ) -> warnAndFail (show e))
151
+ `Sec.catchChecked` (\ (e :: Sec. VerificationError ) -> warnAndFail (show e))
152
+ _ -> pure True
121
153
122
154
-- | Fetch a package if we don't have it already.
123
155
--
0 commit comments