{-# LANGUAGE CPP #-}
module Hackage.Security.Client.Repository (
Metadata
, Binary
, RemoteFile(..)
, CachedFile(..)
, IndexFile(..)
, remoteFileDefaultFormat
, remoteFileDefaultInfo
, Repository(..)
, AttemptNr(..)
, LogMessage(..)
, UpdateFailure(..)
, SomeRemoteError(..)
, DownloadedFile(..)
, mirrorsUnsupported
, remoteRepoPath
, remoteRepoPath'
, IsCached(..)
, mustCache
) where
import MyPrelude
import Control.Exception
import Data.Typeable (Typeable)
import qualified Codec.Archive.Tar.Index as Tar
import qualified Data.ByteString.Lazy as BS.L
import Distribution.Package
import Distribution.Text
import Hackage.Security.Client.Formats
import Hackage.Security.Client.Verify
import Hackage.Security.Trusted
import Hackage.Security.TUF
import Hackage.Security.Util.Checked
import Hackage.Security.Util.Path
import Hackage.Security.Util.Pretty
import Hackage.Security.Util.Some
import Hackage.Security.Util.Stack
data Metadata
data Binary
data RemoteFile :: * -> * -> * where
RemoteTimestamp :: RemoteFile (FormatUn :- ()) Metadata
RemoteRoot :: Maybe (Trusted FileInfo) -> RemoteFile (FormatUn :- ()) Metadata
RemoteSnapshot :: Trusted FileInfo -> RemoteFile (FormatUn :- ()) Metadata
RemoteMirrors :: Trusted FileInfo -> RemoteFile (FormatUn :- ()) Metadata
RemoteIndex :: HasFormat fs FormatGz
-> Formats fs (Trusted FileInfo)
-> RemoteFile fs Binary
RemotePkgTarGz :: PackageIdentifier
-> Trusted FileInfo
-> RemoteFile (FormatGz :- ()) Binary
deriving instance Show (RemoteFile fs typ)
instance Pretty (RemoteFile fs typ) where
pretty :: RemoteFile fs typ -> String
pretty RemoteFile fs typ
RemoteTimestamp = String
"timestamp"
pretty (RemoteRoot Maybe (Trusted FileInfo)
_) = String
"root"
pretty (RemoteSnapshot Trusted FileInfo
_) = String
"snapshot"
pretty (RemoteMirrors Trusted FileInfo
_) = String
"mirrors"
pretty (RemoteIndex HasFormat fs FormatGz
_ Formats fs (Trusted FileInfo)
_) = String
"index"
pretty (RemotePkgTarGz PackageIdentifier
pkgId Trusted FileInfo
_) = String
"package " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
display PackageIdentifier
pkgId
data CachedFile =
CachedTimestamp
| CachedRoot
| CachedSnapshot
| CachedMirrors
deriving (CachedFile -> CachedFile -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CachedFile -> CachedFile -> Bool
$c/= :: CachedFile -> CachedFile -> Bool
== :: CachedFile -> CachedFile -> Bool
$c== :: CachedFile -> CachedFile -> Bool
Eq, Eq CachedFile
CachedFile -> CachedFile -> Bool
CachedFile -> CachedFile -> Ordering
CachedFile -> CachedFile -> CachedFile
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CachedFile -> CachedFile -> CachedFile
$cmin :: CachedFile -> CachedFile -> CachedFile
max :: CachedFile -> CachedFile -> CachedFile
$cmax :: CachedFile -> CachedFile -> CachedFile
>= :: CachedFile -> CachedFile -> Bool
$c>= :: CachedFile -> CachedFile -> Bool
> :: CachedFile -> CachedFile -> Bool
$c> :: CachedFile -> CachedFile -> Bool
<= :: CachedFile -> CachedFile -> Bool
$c<= :: CachedFile -> CachedFile -> Bool
< :: CachedFile -> CachedFile -> Bool
$c< :: CachedFile -> CachedFile -> Bool
compare :: CachedFile -> CachedFile -> Ordering
$ccompare :: CachedFile -> CachedFile -> Ordering
Ord, Int -> CachedFile -> ShowS
[CachedFile] -> ShowS
CachedFile -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CachedFile] -> ShowS
$cshowList :: [CachedFile] -> ShowS
show :: CachedFile -> String
$cshow :: CachedFile -> String
showsPrec :: Int -> CachedFile -> ShowS
$cshowsPrec :: Int -> CachedFile -> ShowS
Show)
instance Pretty CachedFile where
pretty :: CachedFile -> String
pretty CachedFile
CachedTimestamp = String
"timestamp"
pretty CachedFile
CachedRoot = String
"root"
pretty CachedFile
CachedSnapshot = String
"snapshot"
pretty CachedFile
CachedMirrors = String
"mirrors"
remoteFileDefaultFormat :: RemoteFile fs typ -> Some (HasFormat fs)
remoteFileDefaultFormat :: forall fs typ. RemoteFile fs typ -> Some (HasFormat fs)
remoteFileDefaultFormat RemoteFile fs typ
RemoteTimestamp = forall (f :: * -> *) a. f a -> Some f
Some forall a b. (a -> b) -> a -> b
$ forall f fs. Format f -> HasFormat (f :- fs) f
HFZ Format FormatUn
FUn
remoteFileDefaultFormat (RemoteRoot Maybe (Trusted FileInfo)
_) = forall (f :: * -> *) a. f a -> Some f
Some forall a b. (a -> b) -> a -> b
$ forall f fs. Format f -> HasFormat (f :- fs) f
HFZ Format FormatUn
FUn
remoteFileDefaultFormat (RemoteSnapshot Trusted FileInfo
_) = forall (f :: * -> *) a. f a -> Some f
Some forall a b. (a -> b) -> a -> b
$ forall f fs. Format f -> HasFormat (f :- fs) f
HFZ Format FormatUn
FUn
remoteFileDefaultFormat (RemoteMirrors Trusted FileInfo
_) = forall (f :: * -> *) a. f a -> Some f
Some forall a b. (a -> b) -> a -> b
$ forall f fs. Format f -> HasFormat (f :- fs) f
HFZ Format FormatUn
FUn
remoteFileDefaultFormat (RemotePkgTarGz PackageIdentifier
_ Trusted FileInfo
_) = forall (f :: * -> *) a. f a -> Some f
Some forall a b. (a -> b) -> a -> b
$ forall f fs. Format f -> HasFormat (f :- fs) f
HFZ Format FormatGz
FGz
remoteFileDefaultFormat (RemoteIndex HasFormat fs FormatGz
pf Formats fs (Trusted FileInfo)
_) = forall (f :: * -> *) a. f a -> Some f
Some HasFormat fs FormatGz
pf
remoteFileDefaultInfo :: RemoteFile fs typ -> Maybe (Trusted FileInfo)
remoteFileDefaultInfo :: forall fs typ. RemoteFile fs typ -> Maybe (Trusted FileInfo)
remoteFileDefaultInfo RemoteFile fs typ
RemoteTimestamp = forall a. Maybe a
Nothing
remoteFileDefaultInfo (RemoteRoot Maybe (Trusted FileInfo)
info) = Maybe (Trusted FileInfo)
info
remoteFileDefaultInfo (RemoteSnapshot Trusted FileInfo
info) = forall a. a -> Maybe a
Just Trusted FileInfo
info
remoteFileDefaultInfo (RemoteMirrors Trusted FileInfo
info) = forall a. a -> Maybe a
Just Trusted FileInfo
info
remoteFileDefaultInfo (RemotePkgTarGz PackageIdentifier
_ Trusted FileInfo
info) = forall a. a -> Maybe a
Just Trusted FileInfo
info
remoteFileDefaultInfo (RemoteIndex HasFormat fs FormatGz
pf Formats fs (Trusted FileInfo)
info) = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall fs f a. HasFormat fs f -> Formats fs a -> a
formatsLookup HasFormat fs FormatGz
pf Formats fs (Trusted FileInfo)
info
data Repository down = DownloadedFile down => Repository {
forall (down :: * -> *).
Repository down
-> forall fs typ.
Throws SomeRemoteError =>
AttemptNr
-> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
repGetRemote :: forall fs typ. Throws SomeRemoteError
=> AttemptNr
-> RemoteFile fs typ
-> Verify (Some (HasFormat fs), down typ)
, forall (down :: * -> *).
Repository down -> CachedFile -> IO (Maybe (Path Absolute))
repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
, forall (down :: * -> *). Repository down -> IO (Path Absolute)
repGetCachedRoot :: IO (Path Absolute)
, forall (down :: * -> *). Repository down -> IO ()
repClearCache :: IO ()
, forall (down :: * -> *).
Repository down -> forall a. (Handle -> IO a) -> IO a
repWithIndex :: forall a. (Handle -> IO a) -> IO a
, forall (down :: * -> *). Repository down -> IO TarIndex
repGetIndexIdx :: IO Tar.TarIndex
, forall (down :: * -> *). Repository down -> IO () -> IO ()
repLockCache :: IO () -> IO ()
, forall (down :: * -> *).
Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
, forall (down :: * -> *). Repository down -> LogMessage -> IO ()
repLog :: LogMessage -> IO ()
, forall (down :: * -> *). Repository down -> RepoLayout
repLayout :: RepoLayout
, forall (down :: * -> *). Repository down -> IndexLayout
repIndexLayout :: IndexLayout
, forall (down :: * -> *). Repository down -> String
repDescription :: String
}
instance Show (Repository down) where
show :: Repository down -> String
show = forall (down :: * -> *). Repository down -> String
repDescription
mirrorsUnsupported :: Maybe [Mirror] -> IO a -> IO a
mirrorsUnsupported :: forall a. Maybe [Mirror] -> IO a -> IO a
mirrorsUnsupported Maybe [Mirror]
_ = forall a. a -> a
id
newtype AttemptNr = AttemptNr Int
deriving (AttemptNr -> AttemptNr -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: AttemptNr -> AttemptNr -> Bool
$c/= :: AttemptNr -> AttemptNr -> Bool
== :: AttemptNr -> AttemptNr -> Bool
$c== :: AttemptNr -> AttemptNr -> Bool
Eq, Eq AttemptNr
AttemptNr -> AttemptNr -> Bool
AttemptNr -> AttemptNr -> Ordering
AttemptNr -> AttemptNr -> AttemptNr
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: AttemptNr -> AttemptNr -> AttemptNr
$cmin :: AttemptNr -> AttemptNr -> AttemptNr
max :: AttemptNr -> AttemptNr -> AttemptNr
$cmax :: AttemptNr -> AttemptNr -> AttemptNr
>= :: AttemptNr -> AttemptNr -> Bool
$c>= :: AttemptNr -> AttemptNr -> Bool
> :: AttemptNr -> AttemptNr -> Bool
$c> :: AttemptNr -> AttemptNr -> Bool
<= :: AttemptNr -> AttemptNr -> Bool
$c<= :: AttemptNr -> AttemptNr -> Bool
< :: AttemptNr -> AttemptNr -> Bool
$c< :: AttemptNr -> AttemptNr -> Bool
compare :: AttemptNr -> AttemptNr -> Ordering
$ccompare :: AttemptNr -> AttemptNr -> Ordering
Ord, Integer -> AttemptNr
AttemptNr -> AttemptNr
AttemptNr -> AttemptNr -> AttemptNr
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> AttemptNr
$cfromInteger :: Integer -> AttemptNr
signum :: AttemptNr -> AttemptNr
$csignum :: AttemptNr -> AttemptNr
abs :: AttemptNr -> AttemptNr
$cabs :: AttemptNr -> AttemptNr
negate :: AttemptNr -> AttemptNr
$cnegate :: AttemptNr -> AttemptNr
* :: AttemptNr -> AttemptNr -> AttemptNr
$c* :: AttemptNr -> AttemptNr -> AttemptNr
- :: AttemptNr -> AttemptNr -> AttemptNr
$c- :: AttemptNr -> AttemptNr -> AttemptNr
+ :: AttemptNr -> AttemptNr -> AttemptNr
$c+ :: AttemptNr -> AttemptNr -> AttemptNr
Num)
data LogMessage =
LogRootUpdated
| LogVerificationError VerificationError
| forall fs typ. LogDownloading (RemoteFile fs typ)
| forall fs. LogUpdating (RemoteFile fs Binary)
| LogSelectedMirror MirrorDescription
| forall fs. LogCannotUpdate (RemoteFile fs Binary) UpdateFailure
| LogMirrorFailed MirrorDescription SomeException
| LogLockWait (Path Absolute)
| LogLockWaitDone (Path Absolute)
| LogUnlock (Path Absolute)
data UpdateFailure =
UpdateImpossibleUnsupported
| UpdateImpossibleNoLocalCopy
| UpdateFailedTwice
| UpdateFailed SomeException
class DownloadedFile (down :: * -> *) where
downloadedVerify :: down a -> Trusted FileInfo -> IO Bool
downloadedRead :: down Metadata -> IO BS.L.ByteString
downloadedCopyTo :: down a -> Path Absolute -> IO ()
data SomeRemoteError :: * where
SomeRemoteError :: Exception e => e -> SomeRemoteError
deriving (Typeable)
#if MIN_VERSION_base(4,8,0)
deriving instance Show SomeRemoteError
instance Exception SomeRemoteError where displayException :: SomeRemoteError -> String
displayException = forall a. Pretty a => a -> String
pretty
#else
instance Exception SomeRemoteError
instance Show SomeRemoteError where show = pretty
#endif
instance Pretty SomeRemoteError where
pretty :: SomeRemoteError -> String
pretty (SomeRemoteError e
ex) = forall e. Exception e => e -> String
displayException e
ex
remoteRepoPath :: RepoLayout -> RemoteFile fs typ -> Formats fs RepoPath
remoteRepoPath :: forall fs typ.
RepoLayout -> RemoteFile fs typ -> Formats fs RepoPath
remoteRepoPath RepoLayout{RepoPath
PackageIdentifier -> RepoPath
repoLayoutPkgTarGz :: RepoLayout -> PackageIdentifier -> RepoPath
repoLayoutIndexTar :: RepoLayout -> RepoPath
repoLayoutIndexTarGz :: RepoLayout -> RepoPath
repoLayoutMirrors :: RepoLayout -> RepoPath
repoLayoutSnapshot :: RepoLayout -> RepoPath
repoLayoutTimestamp :: RepoLayout -> RepoPath
repoLayoutRoot :: RepoLayout -> RepoPath
repoLayoutPkgTarGz :: PackageIdentifier -> RepoPath
repoLayoutIndexTar :: RepoPath
repoLayoutIndexTarGz :: RepoPath
repoLayoutMirrors :: RepoPath
repoLayoutSnapshot :: RepoPath
repoLayoutTimestamp :: RepoPath
repoLayoutRoot :: RepoPath
..} = forall fs typ. RemoteFile fs typ -> Formats fs RepoPath
go
where
go :: RemoteFile fs typ -> Formats fs RepoPath
go :: forall fs typ. RemoteFile fs typ -> Formats fs RepoPath
go RemoteFile fs typ
RemoteTimestamp = forall a. a -> Formats (FormatUn :- ()) a
FsUn forall a b. (a -> b) -> a -> b
$ RepoPath
repoLayoutTimestamp
go (RemoteRoot Maybe (Trusted FileInfo)
_) = forall a. a -> Formats (FormatUn :- ()) a
FsUn forall a b. (a -> b) -> a -> b
$ RepoPath
repoLayoutRoot
go (RemoteSnapshot Trusted FileInfo
_) = forall a. a -> Formats (FormatUn :- ()) a
FsUn forall a b. (a -> b) -> a -> b
$ RepoPath
repoLayoutSnapshot
go (RemoteMirrors Trusted FileInfo
_) = forall a. a -> Formats (FormatUn :- ()) a
FsUn forall a b. (a -> b) -> a -> b
$ RepoPath
repoLayoutMirrors
go (RemotePkgTarGz PackageIdentifier
pId Trusted FileInfo
_) = forall a. a -> Formats (FormatGz :- ()) a
FsGz forall a b. (a -> b) -> a -> b
$ PackageIdentifier -> RepoPath
repoLayoutPkgTarGz PackageIdentifier
pId
go (RemoteIndex HasFormat fs FormatGz
_ Formats fs (Trusted FileInfo)
lens) = forall a b fs.
(forall f. Format f -> a -> b) -> Formats fs a -> Formats fs b
formatsMap forall f a. Format f -> a -> RepoPath
goIndex Formats fs (Trusted FileInfo)
lens
goIndex :: Format f -> a -> RepoPath
goIndex :: forall f a. Format f -> a -> RepoPath
goIndex Format f
FUn a
_ = RepoPath
repoLayoutIndexTar
goIndex Format f
FGz a
_ = RepoPath
repoLayoutIndexTarGz
remoteRepoPath' :: RepoLayout -> RemoteFile fs typ -> HasFormat fs f -> RepoPath
remoteRepoPath' :: forall fs typ f.
RepoLayout -> RemoteFile fs typ -> HasFormat fs f -> RepoPath
remoteRepoPath' RepoLayout
repoLayout RemoteFile fs typ
file HasFormat fs f
format =
forall fs f a. HasFormat fs f -> Formats fs a -> a
formatsLookup HasFormat fs f
format forall a b. (a -> b) -> a -> b
$ forall fs typ.
RepoLayout -> RemoteFile fs typ -> Formats fs RepoPath
remoteRepoPath RepoLayout
repoLayout RemoteFile fs typ
file
data IsCached :: * -> * where
CacheAs :: CachedFile -> IsCached Metadata
DontCache :: IsCached Binary
CacheIndex :: IsCached Binary
deriving instance Eq (IsCached typ)
deriving instance Show (IsCached typ)
mustCache :: RemoteFile fs typ -> IsCached typ
mustCache :: forall fs typ. RemoteFile fs typ -> IsCached typ
mustCache RemoteFile fs typ
RemoteTimestamp = CachedFile -> IsCached Metadata
CacheAs CachedFile
CachedTimestamp
mustCache (RemoteRoot Maybe (Trusted FileInfo)
_) = CachedFile -> IsCached Metadata
CacheAs CachedFile
CachedRoot
mustCache (RemoteSnapshot Trusted FileInfo
_) = CachedFile -> IsCached Metadata
CacheAs CachedFile
CachedSnapshot
mustCache (RemoteMirrors Trusted FileInfo
_) = CachedFile -> IsCached Metadata
CacheAs CachedFile
CachedMirrors
mustCache (RemoteIndex {}) = IsCached Binary
CacheIndex
mustCache (RemotePkgTarGz PackageIdentifier
_ Trusted FileInfo
_) = IsCached Binary
DontCache
instance Pretty LogMessage where
pretty :: LogMessage -> String
pretty LogMessage
LogRootUpdated =
String
"Root info updated"
pretty (LogVerificationError VerificationError
err) =
String
"Verification error: " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty VerificationError
err
pretty (LogDownloading RemoteFile fs typ
file) =
String
"Downloading " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty RemoteFile fs typ
file
pretty (LogUpdating RemoteFile fs Binary
file) =
String
"Updating " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty RemoteFile fs Binary
file
pretty (LogSelectedMirror String
mirror) =
String
"Selected mirror " forall a. [a] -> [a] -> [a]
++ String
mirror
pretty (LogCannotUpdate RemoteFile fs Binary
file UpdateFailure
ex) =
String
"Cannot update " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty RemoteFile fs Binary
file forall a. [a] -> [a] -> [a]
++ String
" (" forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty UpdateFailure
ex forall a. [a] -> [a] -> [a]
++ String
")"
pretty (LogMirrorFailed String
mirror SomeException
ex) =
String
"Exception " forall a. [a] -> [a] -> [a]
++ forall e. Exception e => e -> String
displayException SomeException
ex forall a. [a] -> [a] -> [a]
++ String
" when using mirror " forall a. [a] -> [a] -> [a]
++ String
mirror
pretty (LogLockWait Path Absolute
file) =
String
"Waiting to acquire cache lock on " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty Path Absolute
file
pretty (LogLockWaitDone Path Absolute
file) =
String
"Acquired cache lock on " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty Path Absolute
file
pretty (LogUnlock Path Absolute
file) =
String
"Released cache lock on " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty Path Absolute
file
instance Pretty UpdateFailure where
pretty :: UpdateFailure -> String
pretty UpdateFailure
UpdateImpossibleUnsupported =
String
"server does not provide incremental downloads"
pretty UpdateFailure
UpdateImpossibleNoLocalCopy =
String
"no local copy"
pretty UpdateFailure
UpdateFailedTwice =
String
"update failed twice"
pretty (UpdateFailed SomeException
ex) =
forall e. Exception e => e -> String
displayException SomeException
ex