    {-# LANGUAGE StaticPointers #-}

    -- | Main entry point into the Hackage Security framework for clients
    module Hackage.Security.Client (
        -- * Checking for updates
        checkForUpdates
      , HasUpdates(..)
        -- * Downloading targets
      , downloadPackage
      , downloadPackage'
        -- * Access to the Hackage index
      , Directory(..)
      , DirectoryEntry(..)
      , getDirectory
      , IndexFile(..)
      , IndexEntry(..)
      , IndexCallbacks(..)
      , withIndex
        -- * Bootstrapping
      , requiresBootstrap
      , bootstrap
        -- * Re-exports
      , module Hackage.Security.TUF
      , module Hackage.Security.Key
      , trusted
        -- ** We only a few bits from .Repository
        -- TODO: Maybe this is a sign that these should be in a different module?
      , Repository -- opaque
      , DownloadedFile(..)
      , SomeRemoteError(..)
      , LogMessage(..)
        -- * Exceptions
      , uncheckClientErrors
      , VerificationError(..)
      , VerificationHistory
      , RootUpdated(..)
      , InvalidPackageException(..)
      , InvalidFileInIndex(..)
      , LocalFileCorrupted(..)
      ) where

    import Prelude hiding (log)
    import Control.Arrow (first)
    import Control.Exception
    import Control.Monad
    import Control.Monad.IO.Class
    import Data.List (sortBy)
    import Data.Maybe (isNothing)
    import Data.Ord (comparing)
    import Data.Time
    import Data.Traversable (for)
    import qualified Codec.Archive.Tar          as Tar
    import qualified Codec.Archive.Tar.Entry    as Tar
    import qualified Codec.Archive.Tar.Index    as Tar
    import qualified Data.ByteString.Lazy       as BS.L
    import qualified Data.ByteString.Lazy.Char8 as BS.L.C8

    import Distribution.Package (PackageIdentifier)
    import Distribution.Text (display)

    import Hackage.Security.Client.Formats
    import Hackage.Security.Client.Repository
    import Hackage.Security.Client.Verify
    import Hackage.Security.JSON
    import Hackage.Security.Key
    import Hackage.Security.Key.Env (KeyEnv)
    import Hackage.Security.Trusted
    import Hackage.Security.Trusted.TCB
    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
    import qualified Hackage.Security.Key.Env as KeyEnv

    {-------------------------------------------------------------------------------
      Checking for updates
    -------------------------------------------------------------------------------}

    data HasUpdates = HasUpdates | NoUpdates
      deriving (Int -> HasUpdates -> ShowS
    [HasUpdates] -> ShowS
    HasUpdates -> String
    (Int -> HasUpdates -> ShowS)
    -> (HasUpdates -> String)
    -> ([HasUpdates] -> ShowS)
    -> Show HasUpdates
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> HasUpdates -> ShowS
    showsPrec :: Int -> HasUpdates -> ShowS
    $cshow :: HasUpdates -> String
    show :: HasUpdates -> String
    $cshowList :: [HasUpdates] -> ShowS
    showList :: [HasUpdates] -> ShowS
    Show, HasUpdates -> HasUpdates -> Bool
    (HasUpdates -> HasUpdates -> Bool)
    -> (HasUpdates -> HasUpdates -> Bool) -> Eq HasUpdates
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: HasUpdates -> HasUpdates -> Bool
    == :: HasUpdates -> HasUpdates -> Bool
    $c/= :: HasUpdates -> HasUpdates -> Bool
    /= :: HasUpdates -> HasUpdates -> Bool
    Eq, Eq HasUpdates
    Eq HasUpdates =>
    (HasUpdates -> HasUpdates -> Ordering)
    -> (HasUpdates -> HasUpdates -> Bool)
    -> (HasUpdates -> HasUpdates -> Bool)
    -> (HasUpdates -> HasUpdates -> Bool)
    -> (HasUpdates -> HasUpdates -> Bool)
    -> (HasUpdates -> HasUpdates -> HasUpdates)
    -> (HasUpdates -> HasUpdates -> HasUpdates)
    -> Ord HasUpdates
    HasUpdates -> HasUpdates -> Bool
    HasUpdates -> HasUpdates -> Ordering
    HasUpdates -> HasUpdates -> HasUpdates
    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
    $ccompare :: HasUpdates -> HasUpdates -> Ordering
    compare :: HasUpdates -> HasUpdates -> Ordering
    $c< :: HasUpdates -> HasUpdates -> Bool
    < :: HasUpdates -> HasUpdates -> Bool
    $c<= :: HasUpdates -> HasUpdates -> Bool
    <= :: HasUpdates -> HasUpdates -> Bool
    $c> :: HasUpdates -> HasUpdates -> Bool
    > :: HasUpdates -> HasUpdates -> Bool
    $c>= :: HasUpdates -> HasUpdates -> Bool
    >= :: HasUpdates -> HasUpdates -> Bool
    $cmax :: HasUpdates -> HasUpdates -> HasUpdates
    max :: HasUpdates -> HasUpdates -> HasUpdates
    $cmin :: HasUpdates -> HasUpdates -> HasUpdates
    min :: HasUpdates -> HasUpdates -> HasUpdates
    Ord)

    -- | Generic logic for checking if there are updates
    --
    -- This implements the logic described in Section 5.1, "The client application",
    -- of the TUF spec. It checks which of the server metadata has changed, and
    -- downloads all changed metadata to the local cache. (Metadata here refers
    -- both to the TUF security metadata as well as the Hackage package index.)
    --
    -- You should pass @Nothing@ for the UTCTime _only_ under exceptional
    -- circumstances (such as when the main server is down for longer than the
    -- expiry dates used in the timestamp files on mirrors).
    checkForUpdates :: (Throws VerificationError, Throws SomeRemoteError)
                    => Repository down
                    -> Maybe UTCTime -- ^ To check expiry times against (if using)
                    -> IO HasUpdates
    checkForUpdates :: forall (down :: * -> *).
    (Throws VerificationError, Throws SomeRemoteError) =>
    Repository down -> Maybe UTCTime -> IO HasUpdates
    checkForUpdates rep :: Repository down
    rep@Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: forall (down :: * -> *).
    Repository down
    -> forall fs typ.
       Throws SomeRemoteError =>
       AttemptNr
       -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    ..} Maybe UTCTime
    mNow =
        Repository down -> IO HasUpdates -> IO HasUpdates
    forall (down :: * -> *) a. Repository down -> IO a -> IO a
    withMirror Repository down
    rep (IO HasUpdates -> IO HasUpdates) -> IO HasUpdates -> IO HasUpdates
    forall a b. (a -> b) -> a -> b
    $ VerificationHistory -> IO HasUpdates
    limitIterations []
      where
        -- More or less randomly chosen maximum iterations
        -- See <https://github.com/theupdateframework/tuf/issues/287>.
        maxNumIterations :: Int
        maxNumIterations :: Int
    maxNumIterations = Int
    5

        -- The spec stipulates that on a verification error we must download new
        -- root information and start over. However, in order to prevent DoS attacks
        -- we limit how often we go round this loop.
        -- See als <https://github.com/theupdateframework/tuf/issues/287>.
        limitIterations :: VerificationHistory -> IO HasUpdates
        limitIterations :: VerificationHistory -> IO HasUpdates
    limitIterations VerificationHistory
    history | VerificationHistory -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length VerificationHistory
    history Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    maxNumIterations =
            VerificationError -> IO HasUpdates
    forall e a. (Exception e, Throws e) => e -> IO a
    throwChecked (VerificationError -> IO HasUpdates)
    -> VerificationError -> IO HasUpdates
    forall a b. (a -> b) -> a -> b
    $ VerificationHistory -> VerificationError
    VerificationErrorLoop (VerificationHistory -> VerificationHistory
    forall a. [a] -> [a]
    reverse VerificationHistory
    history)
        limitIterations VerificationHistory
    history = do
            -- Get all cached info
            --
            -- NOTE: Although we don't normally update any cached files until the
            -- whole verification process successfully completes, in case of a
            -- verification error, or in case of a regular update of the root info,
            -- we DO update the local files. Hence, we must re-read all local files
            -- on each iteration.
            cachedInfo <- Repository down -> IO CachedInfo
    forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> m CachedInfo
    getCachedInfo Repository down
    rep

            mHasUpdates <- tryChecked -- catch RootUpdated
                         $ tryChecked -- catch VerificationError
                         $ runVerify repLockCache
                         $ go attemptNr cachedInfo
            case mHasUpdates of
              Left VerificationError
    ex -> do
                -- NOTE: This call to updateRoot is not itself protected by an
                -- exception handler, and may therefore throw a VerificationError.
                -- This is intentional: if we get verification errors during the
                -- update process, _and_ we cannot update the main root info, then
                -- we cannot do anything.
                Repository down -> LogMessage -> IO ()
    forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> LogMessage -> m ()
    log Repository down
    rep (LogMessage -> IO ()) -> LogMessage -> IO ()
    forall a b. (a -> b) -> a -> b
    $ VerificationError -> LogMessage
    LogVerificationError VerificationError
    ex
                let history' :: VerificationHistory
    history'   = VerificationError -> Either RootUpdated VerificationError
    forall a b. b -> Either a b
    Right VerificationError
    ex Either RootUpdated VerificationError
    -> VerificationHistory -> VerificationHistory
    forall a. a -> [a] -> [a]
    : VerificationHistory
    history
                    attemptNr' :: AttemptNr
    attemptNr' = AttemptNr
    attemptNr AttemptNr -> AttemptNr -> AttemptNr
    forall a. Num a => a -> a -> a
    + AttemptNr
    1
                Repository down
    -> Maybe UTCTime
    -> AttemptNr
    -> CachedInfo
    -> Either VerificationError (Trusted FileInfo)
    -> IO ()
    forall (down :: * -> *).
    (Throws VerificationError, Throws SomeRemoteError) =>
    Repository down
    -> Maybe UTCTime
    -> AttemptNr
    -> CachedInfo
    -> Either VerificationError (Trusted FileInfo)
    -> IO ()
    updateRoot Repository down
    rep Maybe UTCTime
    mNow AttemptNr
    attemptNr' CachedInfo
    cachedInfo (VerificationError -> Either VerificationError (Trusted FileInfo)
    forall a b. a -> Either a b
    Left VerificationError
    ex)
                VerificationHistory -> IO HasUpdates
    limitIterations VerificationHistory
    history'
              Right (Left RootUpdated
    RootUpdated) -> do
                Repository down -> LogMessage -> IO ()
    forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> LogMessage -> m ()
    log Repository down
    rep (LogMessage -> IO ()) -> LogMessage -> IO ()
    forall a b. (a -> b) -> a -> b
    $ LogMessage
    LogRootUpdated
                let history' :: VerificationHistory
    history' = RootUpdated -> Either RootUpdated VerificationError
    forall a b. a -> Either a b
    Left RootUpdated
    RootUpdated Either RootUpdated VerificationError
    -> VerificationHistory -> VerificationHistory
    forall a. a -> [a] -> [a]
    : VerificationHistory
    history
                VerificationHistory -> IO HasUpdates
    limitIterations VerificationHistory
    history'
              Right (Right HasUpdates
    hasUpdates) ->
                HasUpdates -> IO HasUpdates
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return HasUpdates
    hasUpdates
          where
            attemptNr :: AttemptNr
            attemptNr :: AttemptNr
    attemptNr = Int -> AttemptNr
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int -> AttemptNr) -> Int -> AttemptNr
    forall a b. (a -> b) -> a -> b
    $ VerificationHistory -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length VerificationHistory
    history

        -- The 'Verify' monad only caches the downloaded files after verification.
        -- See also <https://github.com/theupdateframework/tuf/issues/283>.
        go :: Throws RootUpdated => AttemptNr -> CachedInfo -> Verify HasUpdates
        go :: Throws RootUpdated => AttemptNr -> CachedInfo -> Verify HasUpdates
    go AttemptNr
    attemptNr cachedInfo :: CachedInfo
    cachedInfo@CachedInfo{Maybe (Trusted Mirrors)
    Maybe (Trusted FileInfo)
    Maybe (Trusted Timestamp)
    Maybe (Trusted Snapshot)
    KeyEnv
    Trusted Root
    cachedRoot :: Trusted Root
    cachedKeyEnv :: KeyEnv
    cachedTimestamp :: Maybe (Trusted Timestamp)
    cachedSnapshot :: Maybe (Trusted Snapshot)
    cachedMirrors :: Maybe (Trusted Mirrors)
    cachedInfoSnapshot :: Maybe (Trusted FileInfo)
    cachedInfoRoot :: Maybe (Trusted FileInfo)
    cachedInfoMirrors :: Maybe (Trusted FileInfo)
    cachedInfoTarGz :: Maybe (Trusted FileInfo)
    cachedInfoTarGz :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoMirrors :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoRoot :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoSnapshot :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedMirrors :: CachedInfo -> Maybe (Trusted Mirrors)
    cachedSnapshot :: CachedInfo -> Maybe (Trusted Snapshot)
    cachedTimestamp :: CachedInfo -> Maybe (Trusted Timestamp)
    cachedKeyEnv :: CachedInfo -> KeyEnv
    cachedRoot :: CachedInfo -> Trusted Root
    ..} = do
          -- Get the new timestamp
          newTS <- RemoteFile (FormatUn :- ()) Metadata -> Verify (Trusted Timestamp)
    forall a f.
    (VerifyRole a, FromJSON ReadJSON_Keys_Layout (Signed a)) =>
    RemoteFile (f :- ()) Metadata -> Verify (Trusted a)
    getRemoteFile' RemoteFile (FormatUn :- ()) Metadata
    RemoteTimestamp
          let newInfoSS = static Timestamp -> FileInfo
    timestampInfoSnapshot StaticPtr (Timestamp -> FileInfo)
    -> Trusted Timestamp -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$> Trusted Timestamp
    newTS

          -- Check if the snapshot has changed
          if not (fileChanged cachedInfoSnapshot newInfoSS)
            then return NoUpdates
            else do
              -- Get the new snapshot
              newSS <- getRemoteFile' (RemoteSnapshot newInfoSS)
              let newInfoRoot    = static Snapshot -> FileInfo
    snapshotInfoRoot    StaticPtr (Snapshot -> FileInfo)
    -> Trusted Snapshot -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$> Trusted Snapshot
    newSS
                  newInfoMirrors = static Snapshot -> FileInfo
    snapshotInfoMirrors StaticPtr (Snapshot -> FileInfo)
    -> Trusted Snapshot -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$> Trusted Snapshot
    newSS
                  newInfoTarGz   = static Snapshot -> FileInfo
    snapshotInfoTarGz   StaticPtr (Snapshot -> FileInfo)
    -> Trusted Snapshot -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$> Trusted Snapshot
    newSS
                  mNewInfoTar    = Trusted (Maybe FileInfo) -> Maybe (Trusted FileInfo)
    forall (f :: * -> *) a.
    Traversable f =>
    Trusted (f a) -> f (Trusted a)
    trustElems (static Snapshot -> Maybe FileInfo
    snapshotInfoTar StaticPtr (Snapshot -> Maybe FileInfo)
    -> Trusted Snapshot -> Trusted (Maybe FileInfo)
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$> Trusted Snapshot
    newSS)

              -- If root metadata changed, download and restart
              when (rootChanged cachedInfoRoot newInfoRoot) $ liftIO $ do
                updateRoot rep mNow attemptNr cachedInfo (Right newInfoRoot)
                -- By throwing 'RootUpdated' as an exception we make sure that
                -- any files previously downloaded (to temporary locations)
                -- will not be cached.
                throwChecked RootUpdated

              -- If mirrors changed, download and verify
              when (fileChanged cachedInfoMirrors newInfoMirrors) $
                newMirrors =<< getRemoteFile' (RemoteMirrors newInfoMirrors)

              -- If index changed, download and verify
              when (fileChanged cachedInfoTarGz newInfoTarGz) $
                updateIndex newInfoTarGz mNewInfoTar

              return HasUpdates
          where
            getRemoteFile' :: ( VerifyRole a
                              , FromJSON ReadJSON_Keys_Layout (Signed a)
                              )
                           => RemoteFile (f :- ()) Metadata -> Verify (Trusted a)
            getRemoteFile' :: forall a f.
    (VerifyRole a, FromJSON ReadJSON_Keys_Layout (Signed a)) =>
    RemoteFile (f :- ()) Metadata -> Verify (Trusted a)
    getRemoteFile' = ((Trusted a, down Metadata) -> Trusted a)
    -> Verify (Trusted a, down Metadata) -> Verify (Trusted a)
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    liftM (Trusted a, down Metadata) -> Trusted a
    forall a b. (a, b) -> a
    fst (Verify (Trusted a, down Metadata) -> Verify (Trusted a))
    -> (RemoteFile (f :- ()) Metadata
        -> Verify (Trusted a, down Metadata))
    -> RemoteFile (f :- ()) Metadata
    -> Verify (Trusted a)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Repository down
    -> CachedInfo
    -> AttemptNr
    -> Maybe UTCTime
    -> RemoteFile (f :- ()) Metadata
    -> Verify (Trusted a, down Metadata)
    forall a (down :: * -> *) f.
    (Throws VerificationError, Throws SomeRemoteError, VerifyRole a,
     FromJSON ReadJSON_Keys_Layout (Signed a)) =>
    Repository down
    -> CachedInfo
    -> AttemptNr
    -> Maybe UTCTime
    -> RemoteFile (f :- ()) Metadata
    -> Verify (Trusted a, down Metadata)
    getRemoteFile Repository down
    rep CachedInfo
    cachedInfo AttemptNr
    attemptNr Maybe UTCTime
    mNow

            -- Update the index and check against the appropriate hash
            updateIndex :: Trusted FileInfo         -- info about @.tar.gz@
                        -> Maybe (Trusted FileInfo) -- info about @.tar@
                        -> Verify ()
            updateIndex :: Trusted FileInfo -> Maybe (Trusted FileInfo) -> Verify ()
    updateIndex Trusted FileInfo
    newInfoTarGz Maybe (Trusted FileInfo)
    Nothing = do
              (targetPath, tempPath) <- Repository down
    -> AttemptNr
    -> RemoteFile (FormatGz :- ()) Binary
    -> Verify (TargetPath, down Binary)
    forall f (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile (f :- ()) typ
    -> Verify (TargetPath, down typ)
    getRemote' Repository down
    rep AttemptNr
    attemptNr (RemoteFile (FormatGz :- ()) Binary
     -> Verify (TargetPath, down Binary))
    -> RemoteFile (FormatGz :- ()) Binary
    -> Verify (TargetPath, down Binary)
    forall a b. (a -> b) -> a -> b
    $
                HasFormat (FormatGz :- ()) FormatGz
    -> Formats (FormatGz :- ()) (Trusted FileInfo)
    -> RemoteFile (FormatGz :- ()) Binary
    forall a.
    HasFormat a FormatGz
    -> Formats a (Trusted FileInfo) -> RemoteFile a Binary
    RemoteIndex (Format FormatGz -> HasFormat (FormatGz :- ()) FormatGz
    forall b fs. Format b -> HasFormat (b :- fs) b
    HFZ Format FormatGz
    FGz) (Trusted FileInfo -> Formats (FormatGz :- ()) (Trusted FileInfo)
    forall b. b -> Formats (FormatGz :- ()) b
    FsGz Trusted FileInfo
    newInfoTarGz)
              verifyFileInfo' (Just newInfoTarGz) targetPath tempPath
            updateIndex Trusted FileInfo
    newInfoTarGz (Just Trusted FileInfo
    newInfoTar) = do
              (format, targetPath, tempPath) <- Repository down
    -> AttemptNr
    -> RemoteFile (FormatUn :- (FormatGz :- ())) Binary
    -> Verify (Some Format, TargetPath, down Binary)
    forall fs (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile fs typ
    -> Verify (Some Format, TargetPath, down typ)
    getRemote Repository down
    rep AttemptNr
    attemptNr (RemoteFile (FormatUn :- (FormatGz :- ())) Binary
     -> Verify (Some Format, TargetPath, down Binary))
    -> RemoteFile (FormatUn :- (FormatGz :- ())) Binary
    -> Verify (Some Format, TargetPath, down Binary)
    forall a b. (a -> b) -> a -> b
    $
                HasFormat (FormatUn :- (FormatGz :- ())) FormatGz
    -> Formats (FormatUn :- (FormatGz :- ())) (Trusted FileInfo)
    -> RemoteFile (FormatUn :- (FormatGz :- ())) Binary
    forall a.
    HasFormat a FormatGz
    -> Formats a (Trusted FileInfo) -> RemoteFile a Binary
    RemoteIndex (HasFormat (FormatGz :- ()) FormatGz
    -> HasFormat (FormatUn :- (FormatGz :- ())) FormatGz
    forall fs b f'. HasFormat fs b -> HasFormat (f' :- fs) b
    HFS (Format FormatGz -> HasFormat (FormatGz :- ()) FormatGz
    forall b fs. Format b -> HasFormat (b :- fs) b
    HFZ Format FormatGz
    FGz)) (Trusted FileInfo
    -> Trusted FileInfo
    -> Formats (FormatUn :- (FormatGz :- ())) (Trusted FileInfo)
    forall b. b -> b -> Formats (FormatUn :- (FormatGz :- ())) b
    FsUnGz Trusted FileInfo
    newInfoTar Trusted FileInfo
    newInfoTarGz)
              case format of
                Some Format a
    FGz -> Maybe (Trusted FileInfo) -> TargetPath -> down Binary -> Verify ()
    forall (m :: * -> *) (down :: * -> *) typ.
    (MonadIO m, DownloadedFile down) =>
    Maybe (Trusted FileInfo) -> TargetPath -> down typ -> m ()
    verifyFileInfo' (Trusted FileInfo -> Maybe (Trusted FileInfo)
    forall a. a -> Maybe a
    Just Trusted FileInfo
    newInfoTarGz) TargetPath
    targetPath down Binary
    tempPath
                Some Format a
    FUn -> Maybe (Trusted FileInfo) -> TargetPath -> down Binary -> Verify ()
    forall (m :: * -> *) (down :: * -> *) typ.
    (MonadIO m, DownloadedFile down) =>
    Maybe (Trusted FileInfo) -> TargetPath -> down typ -> m ()
    verifyFileInfo' (Trusted FileInfo -> Maybe (Trusted FileInfo)
    forall a. a -> Maybe a
    Just Trusted FileInfo
    newInfoTar)   TargetPath
    targetPath down Binary
    tempPath

        -- Unlike for other files, if we didn't have an old snapshot, consider the
        -- root info unchanged (otherwise we would loop indefinitely).
        -- See also <https://github.com/theupdateframework/tuf/issues/286>
        rootChanged :: Maybe (Trusted FileInfo) -> Trusted FileInfo -> Bool
        rootChanged :: Maybe (Trusted FileInfo) -> Trusted FileInfo -> Bool
    rootChanged Maybe (Trusted FileInfo)
    Nothing    Trusted FileInfo
    _   = Bool
    False
        rootChanged (Just Trusted FileInfo
    old) Trusted FileInfo
    new = Bool -> Bool
    not (Trusted FileInfo -> Trusted FileInfo -> Bool
    trustedFileInfoEqual Trusted FileInfo
    old Trusted FileInfo
    new)

        -- For any file other than the root we consider the file to have changed
        -- if we do not yet have a local snapshot to tell us the old info.
        fileChanged :: Maybe (Trusted FileInfo) -> Trusted FileInfo -> Bool
        fileChanged :: Maybe (Trusted FileInfo) -> Trusted FileInfo -> Bool
    fileChanged Maybe (Trusted FileInfo)
    Nothing    Trusted FileInfo
    _   = Bool
    True
        fileChanged (Just Trusted FileInfo
    old) Trusted FileInfo
    new = Bool -> Bool
    not (Trusted FileInfo -> Trusted FileInfo -> Bool
    trustedFileInfoEqual Trusted FileInfo
    old Trusted FileInfo
    new)

        -- We don't actually _do_ anything with the mirrors file until the next call
        -- to 'checkUpdates', because we want to use a single server for a single
        -- check-for-updates request. If validation was successful the repository
        -- will have cached the mirrors file and it will be available on the next
        -- request.
        newMirrors :: Trusted Mirrors -> Verify ()
        newMirrors :: Trusted Mirrors -> Verify ()
    newMirrors Trusted Mirrors
    _ = () -> Verify ()
    forall a. a -> Verify a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

    -- | Update the root metadata
    --
    -- Note that the new root metadata is verified using the old root metadata,
    -- and only then trusted.
    --
    -- We don't always have root file information available. If we notice during
    -- the normal update process that the root information has changed then the
    -- snapshot will give us the new file information; but if we need to update
    -- the root information due to a verification error we do not.
    --
    -- We additionally delete the cached cached snapshot and timestamp. This is
    -- necessary for two reasons:
    --
    -- 1. If during the normal update process we notice that the root info was
    --    updated (because the hash of @root.json@ in the new snapshot is different
    --    from the old snapshot) we download new root info and start over, without
    --    (yet) downloading a (potential) new index. This means it is important that
    --    we not overwrite our local cached snapshot, because if we did we would
    --    then on the next iteration conclude there were no updates and we would
    --    fail to notice that we should have updated the index. However, unless we
    --    do something, this means that we would conclude on the next iteration once
    --    again that the root info has changed (because the hash in the new shapshot
    --    still doesn't match the hash in the cached snapshot), and we would loop
    --    until we throw a 'VerificationErrorLoop' exception. By deleting the local
    --    snapshot we basically reset the client to its initial state, and we will
    --    not try to download the root info once again. The only downside of this is
    --    that we will also re-download the index after every root info change.
    --    However, this should be infrequent enough that this isn't an issue.
    --    See also <https://github.com/theupdateframework/tuf/issues/285>.
    --
    -- 2. Additionally, deleting the local timestamp and snapshot protects against
    --    an attack where an attacker has set the file version of the snapshot or
    --    timestamp to MAX_INT, thereby making further updates impossible.
    --    (Such an attack would require a timestamp/snapshot key compromise.)
    --
    -- However, we _ONLY_ do this when the root information has actually changed.
    -- If we did this unconditionally it would mean that we delete the locally
    -- cached timestamp whenever the version on the remote timestamp is invalid,
    -- thereby rendering the file version on the timestamp and the snapshot useless.
    -- See <https://github.com/theupdateframework/tuf/issues/283#issuecomment-115739521>
    updateRoot :: (Throws VerificationError, Throws SomeRemoteError)
               => Repository down
               -> Maybe UTCTime
               -> AttemptNr
               -> CachedInfo
               -> Either VerificationError (Trusted FileInfo)
               -> IO ()
    updateRoot :: forall (down :: * -> *).
    (Throws VerificationError, Throws SomeRemoteError) =>
    Repository down
    -> Maybe UTCTime
    -> AttemptNr
    -> CachedInfo
    -> Either VerificationError (Trusted FileInfo)
    -> IO ()
    updateRoot rep :: Repository down
    rep@Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} Maybe UTCTime
    mNow AttemptNr
    isRetry CachedInfo
    cachedInfo Either VerificationError (Trusted FileInfo)
    eFileInfo = do
        rootReallyChanged <- (IO () -> IO ()) -> Verify Bool -> IO Bool
    forall a. (IO () -> IO ()) -> Verify a -> IO a
    runVerify IO () -> IO ()
    repLockCache (Verify Bool -> IO Bool) -> Verify Bool -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ do
          (_newRoot :: Trusted Root, rootTempFile) <- Repository down
    -> CachedInfo
    -> AttemptNr
    -> Maybe UTCTime
    -> RemoteFile (FormatUn :- ()) Metadata
    -> Verify (Trusted Root, down Metadata)
    forall a (down :: * -> *) f.
    (Throws VerificationError, Throws SomeRemoteError, VerifyRole a,
     FromJSON ReadJSON_Keys_Layout (Signed a)) =>
    Repository down
    -> CachedInfo
    -> AttemptNr
    -> Maybe UTCTime
    -> RemoteFile (f :- ()) Metadata
    -> Verify (Trusted a, down Metadata)
    getRemoteFile
            Repository down
    rep
            CachedInfo
    cachedInfo
            AttemptNr
    isRetry
            Maybe UTCTime
    mNow
            (Maybe (Trusted FileInfo) -> RemoteFile (FormatUn :- ()) Metadata
    RemoteRoot (Either VerificationError (Trusted FileInfo)
    -> Maybe (Trusted FileInfo)
    forall a b. Either a b -> Maybe b
    eitherToMaybe Either VerificationError (Trusted FileInfo)
    eFileInfo))

          -- NOTE: It is important that we do this check within the evalContT,
          -- because the temporary file will be deleted once we leave its scope.
          case eFileInfo of
            Right Trusted FileInfo
    _ ->
              -- We are downloading the root info because the hash in the snapshot
              -- changed. In this case the root definitely changed.
              Bool -> Verify Bool
    forall a. a -> Verify a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    True
            Left VerificationError
    _e -> IO Bool -> Verify Bool
    forall a. IO a -> Verify a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Bool -> Verify Bool) -> IO Bool -> Verify Bool
    forall a b. (a -> b) -> a -> b
    $ do
              -- We are downloading the root because of a verification error. In
              -- this case the root info may or may not have changed. In most cases
              -- it would suffice to compare the file version now; however, in the
              -- (exceptional) circumstance where the root info has changed but
              -- the file version has not, this would result in the same infinite
              -- loop described above. Hence, we must compare file hashes, and they
              -- must be computed on the raw file, not the parsed file.
              oldRootFile <- IO (Path Absolute)
    repGetCachedRoot
              oldRootInfo <- DeclareTrusted <$> computeFileInfo oldRootFile
              not <$> downloadedVerify rootTempFile oldRootInfo

        when rootReallyChanged $ clearCache rep

    {-------------------------------------------------------------------------------
      Convenience functions for downloading and parsing various files
    -------------------------------------------------------------------------------}

    data CachedInfo = CachedInfo {
        CachedInfo -> Trusted Root
    cachedRoot         :: Trusted Root
      , CachedInfo -> KeyEnv
    cachedKeyEnv       :: KeyEnv
      , CachedInfo -> Maybe (Trusted Timestamp)
    cachedTimestamp    :: Maybe (Trusted Timestamp)
      , CachedInfo -> Maybe (Trusted Snapshot)
    cachedSnapshot     :: Maybe (Trusted Snapshot)
      , CachedInfo -> Maybe (Trusted Mirrors)
    cachedMirrors      :: Maybe (Trusted Mirrors)
      , CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoSnapshot :: Maybe (Trusted FileInfo)
      , CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoRoot     :: Maybe (Trusted FileInfo)
      , CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoMirrors  :: Maybe (Trusted FileInfo)
      , CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoTarGz    :: Maybe (Trusted FileInfo)
      }

    cachedVersion :: CachedInfo -> RemoteFile fs typ -> Maybe FileVersion
    cachedVersion :: forall fs typ. CachedInfo -> RemoteFile fs typ -> Maybe FileVersion
    cachedVersion CachedInfo{Maybe (Trusted Mirrors)
    Maybe (Trusted FileInfo)
    Maybe (Trusted Timestamp)
    Maybe (Trusted Snapshot)
    KeyEnv
    Trusted Root
    cachedInfoTarGz :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoMirrors :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoRoot :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoSnapshot :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedMirrors :: CachedInfo -> Maybe (Trusted Mirrors)
    cachedSnapshot :: CachedInfo -> Maybe (Trusted Snapshot)
    cachedTimestamp :: CachedInfo -> Maybe (Trusted Timestamp)
    cachedKeyEnv :: CachedInfo -> KeyEnv
    cachedRoot :: CachedInfo -> Trusted Root
    cachedRoot :: Trusted Root
    cachedKeyEnv :: KeyEnv
    cachedTimestamp :: Maybe (Trusted Timestamp)
    cachedSnapshot :: Maybe (Trusted Snapshot)
    cachedMirrors :: Maybe (Trusted Mirrors)
    cachedInfoSnapshot :: Maybe (Trusted FileInfo)
    cachedInfoRoot :: Maybe (Trusted FileInfo)
    cachedInfoMirrors :: Maybe (Trusted FileInfo)
    cachedInfoTarGz :: Maybe (Trusted FileInfo)
    ..} RemoteFile fs typ
    remoteFile =
        case RemoteFile fs typ -> IsCached typ
    forall fs typ. RemoteFile fs typ -> IsCached typ
    mustCache RemoteFile fs typ
    remoteFile of
          CacheAs CachedFile
    CachedTimestamp -> Timestamp -> FileVersion
    timestampVersion (Timestamp -> FileVersion)
    -> (Trusted Timestamp -> Timestamp)
    -> Trusted Timestamp
    -> FileVersion
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Trusted Timestamp -> Timestamp
    forall a. Trusted a -> a
    trusted (Trusted Timestamp -> FileVersion)
    -> Maybe (Trusted Timestamp) -> Maybe FileVersion
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe (Trusted Timestamp)
    cachedTimestamp
          CacheAs CachedFile
    CachedSnapshot  -> Snapshot -> FileVersion
    snapshotVersion  (Snapshot -> FileVersion)
    -> (Trusted Snapshot -> Snapshot)
    -> Trusted Snapshot
    -> FileVersion
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Trusted Snapshot -> Snapshot
    forall a. Trusted a -> a
    trusted (Trusted Snapshot -> FileVersion)
    -> Maybe (Trusted Snapshot) -> Maybe FileVersion
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe (Trusted Snapshot)
    cachedSnapshot
          CacheAs CachedFile
    CachedMirrors   -> Mirrors -> FileVersion
    mirrorsVersion   (Mirrors -> FileVersion)
    -> (Trusted Mirrors -> Mirrors) -> Trusted Mirrors -> FileVersion
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Trusted Mirrors -> Mirrors
    forall a. Trusted a -> a
    trusted (Trusted Mirrors -> FileVersion)
    -> Maybe (Trusted Mirrors) -> Maybe FileVersion
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe (Trusted Mirrors)
    cachedMirrors
          CacheAs CachedFile
    CachedRoot      -> FileVersion -> Maybe FileVersion
    forall a. a -> Maybe a
    Just (FileVersion -> Maybe FileVersion)
    -> (Trusted Root -> FileVersion)
    -> Trusted Root
    -> Maybe FileVersion
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Root -> FileVersion
    rootVersion (Root -> FileVersion)
    -> (Trusted Root -> Root) -> Trusted Root -> FileVersion
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Trusted Root -> Root
    forall a. Trusted a -> a
    trusted (Trusted Root -> Maybe FileVersion)
    -> Trusted Root -> Maybe FileVersion
    forall a b. (a -> b) -> a -> b
    $ Trusted Root
    cachedRoot
          IsCached typ
    CacheIndex -> Maybe FileVersion
    forall a. Maybe a
    Nothing
          IsCached typ
    DontCache  -> Maybe FileVersion
    forall a. Maybe a
    Nothing

    -- | Get all cached info (if any)
    getCachedInfo ::
                     MonadIO m
                  => Repository down -> m CachedInfo
    getCachedInfo :: forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> m CachedInfo
    getCachedInfo Repository down
    rep = do
        (cachedRoot, cachedKeyEnv) <- Repository down -> m (Trusted Root, KeyEnv)
    forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> m (Trusted Root, KeyEnv)
    readLocalRoot Repository down
    rep
        cachedTimestamp <- readLocalFile rep cachedKeyEnv CachedTimestamp
        cachedSnapshot  <- readLocalFile rep cachedKeyEnv CachedSnapshot
        cachedMirrors   <- readLocalFile rep cachedKeyEnv CachedMirrors

        let cachedInfoSnapshot = (Trusted Timestamp -> Trusted FileInfo)
    -> Maybe (Trusted Timestamp) -> Maybe (Trusted FileInfo)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (static Timestamp -> FileInfo
    timestampInfoSnapshot StaticPtr (Timestamp -> FileInfo)
    -> Trusted Timestamp -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$>) Maybe (Trusted Timestamp)
    cachedTimestamp
            cachedInfoRoot     = (Trusted Snapshot -> Trusted FileInfo)
    -> Maybe (Trusted Snapshot) -> Maybe (Trusted FileInfo)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (static Snapshot -> FileInfo
    snapshotInfoRoot      StaticPtr (Snapshot -> FileInfo)
    -> Trusted Snapshot -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$>) Maybe (Trusted Snapshot)
    cachedSnapshot
            cachedInfoMirrors  = (Trusted Snapshot -> Trusted FileInfo)
    -> Maybe (Trusted Snapshot) -> Maybe (Trusted FileInfo)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (static Snapshot -> FileInfo
    snapshotInfoMirrors   StaticPtr (Snapshot -> FileInfo)
    -> Trusted Snapshot -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$>) Maybe (Trusted Snapshot)
    cachedSnapshot
            cachedInfoTarGz    = (Trusted Snapshot -> Trusted FileInfo)
    -> Maybe (Trusted Snapshot) -> Maybe (Trusted FileInfo)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (static Snapshot -> FileInfo
    snapshotInfoTarGz     StaticPtr (Snapshot -> FileInfo)
    -> Trusted Snapshot -> Trusted FileInfo
    forall a b. StaticPtr (a -> b) -> Trusted a -> Trusted b
    <$$>) Maybe (Trusted Snapshot)
    cachedSnapshot

        return CachedInfo{..}

    readLocalRoot :: MonadIO m => Repository down -> m (Trusted Root, KeyEnv)
    readLocalRoot :: forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> m (Trusted Root, KeyEnv)
    readLocalRoot Repository down
    rep = do
        cachedPath <- IO (Path Absolute) -> m (Path Absolute)
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Path Absolute) -> m (Path Absolute))
    -> IO (Path Absolute) -> m (Path Absolute)
    forall a b. (a -> b) -> a -> b
    $ Repository down -> IO (Path Absolute)
    forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCachedRoot Repository down
    rep
        signedRoot <- throwErrorsUnchecked LocalFileCorrupted =<<
                        readCachedJSON rep KeyEnv.empty cachedPath
        return (trustLocalFile signedRoot, rootKeys (signed signedRoot))

    readLocalFile :: ( FromJSON ReadJSON_Keys_Layout (Signed a), MonadIO m
                     )
                  => Repository down -> KeyEnv -> CachedFile -> m (Maybe (Trusted a))
    readLocalFile :: forall a (m :: * -> *) (down :: * -> *).
    (FromJSON ReadJSON_Keys_Layout (Signed a), MonadIO m) =>
    Repository down -> KeyEnv -> CachedFile -> m (Maybe (Trusted a))
    readLocalFile Repository down
    rep KeyEnv
    cachedKeyEnv CachedFile
    file = do
        mCachedPath <- IO (Maybe (Path Absolute)) -> m (Maybe (Path Absolute))
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Maybe (Path Absolute)) -> m (Maybe (Path Absolute)))
    -> IO (Maybe (Path Absolute)) -> m (Maybe (Path Absolute))
    forall a b. (a -> b) -> a -> b
    $ Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetCached Repository down
    rep CachedFile
    file
        for mCachedPath $ \Path Absolute
    cachedPath -> do
          signed <- (DeserializationError -> LocalFileCorrupted)
    -> Either DeserializationError (Signed a) -> m (Signed a)
    forall (m :: * -> *) e' e a.
    (MonadIO m, Exception e') =>
    (e -> e') -> Either e a -> m a
    throwErrorsUnchecked DeserializationError -> LocalFileCorrupted
    LocalFileCorrupted (Either DeserializationError (Signed a) -> m (Signed a))
    -> m (Either DeserializationError (Signed a)) -> m (Signed a)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<<
                      Repository down
    -> KeyEnv
    -> Path Absolute
    -> m (Either DeserializationError (Signed a))
    forall (m :: * -> *) a (down :: * -> *).
    (MonadIO m, FromJSON ReadJSON_Keys_Layout a) =>
    Repository down
    -> KeyEnv -> Path Absolute -> m (Either DeserializationError a)
    readCachedJSON Repository down
    rep KeyEnv
    cachedKeyEnv Path Absolute
    cachedPath
          return $ trustLocalFile signed

    getRemoteFile :: ( Throws VerificationError
                     , Throws SomeRemoteError
                     , VerifyRole a
                     , FromJSON ReadJSON_Keys_Layout (Signed a)
                     )
                  => Repository down
                  -> CachedInfo
                  -> AttemptNr
                  -> Maybe UTCTime
                  -> RemoteFile (f :- ()) Metadata
                  -> Verify (Trusted a, down Metadata)
    getRemoteFile :: forall a (down :: * -> *) f.
    (Throws VerificationError, Throws SomeRemoteError, VerifyRole a,
     FromJSON ReadJSON_Keys_Layout (Signed a)) =>
    Repository down
    -> CachedInfo
    -> AttemptNr
    -> Maybe UTCTime
    -> RemoteFile (f :- ()) Metadata
    -> Verify (Trusted a, down Metadata)
    getRemoteFile rep :: Repository down
    rep@Repository{} cachedInfo :: CachedInfo
    cachedInfo@CachedInfo{Maybe (Trusted Mirrors)
    Maybe (Trusted FileInfo)
    Maybe (Trusted Timestamp)
    Maybe (Trusted Snapshot)
    KeyEnv
    Trusted Root
    cachedInfoTarGz :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoMirrors :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoRoot :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedInfoSnapshot :: CachedInfo -> Maybe (Trusted FileInfo)
    cachedMirrors :: CachedInfo -> Maybe (Trusted Mirrors)
    cachedSnapshot :: CachedInfo -> Maybe (Trusted Snapshot)
    cachedTimestamp :: CachedInfo -> Maybe (Trusted Timestamp)
    cachedKeyEnv :: CachedInfo -> KeyEnv
    cachedRoot :: CachedInfo -> Trusted Root
    cachedRoot :: Trusted Root
    cachedKeyEnv :: KeyEnv
    cachedTimestamp :: Maybe (Trusted Timestamp)
    cachedSnapshot :: Maybe (Trusted Snapshot)
    cachedMirrors :: Maybe (Trusted Mirrors)
    cachedInfoSnapshot :: Maybe (Trusted FileInfo)
    cachedInfoRoot :: Maybe (Trusted FileInfo)
    cachedInfoMirrors :: Maybe (Trusted FileInfo)
    cachedInfoTarGz :: Maybe (Trusted FileInfo)
    ..} AttemptNr
    isRetry Maybe UTCTime
    mNow RemoteFile (f :- ()) Metadata
    file = do
        (targetPath, tempPath) <- Repository down
    -> AttemptNr
    -> RemoteFile (f :- ()) Metadata
    -> Verify (TargetPath, down Metadata)
    forall f (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile (f :- ()) typ
    -> Verify (TargetPath, down typ)
    getRemote' Repository down
    rep AttemptNr
    isRetry RemoteFile (f :- ()) Metadata
    file
        verifyFileInfo' (remoteFileDefaultInfo file) targetPath tempPath
        signed   <- throwErrorsChecked (VerificationErrorDeserialization targetPath) =<<
                      readDownloadedJSON rep cachedKeyEnv tempPath
        verified <- throwErrorsChecked id $ verifyRole
                      cachedRoot
                      targetPath
                      (cachedVersion cachedInfo file)
                      mNow
                      signed
        return (trustVerified verified, tempPath)

    {-------------------------------------------------------------------------------
      Downloading target files
    -------------------------------------------------------------------------------}

    -- | Download a package
    downloadPackage :: ( Throws SomeRemoteError
                       , Throws VerificationError
                       , Throws InvalidPackageException
                       )
                    => Repository down    -- ^ Repository
                    -> PackageIdentifier  -- ^ Package to download
                    -> Path Absolute      -- ^ Destination (see also 'downloadPackage'')
                    -> IO ()
    downloadPackage :: forall (down :: * -> *).
    (Throws SomeRemoteError, Throws VerificationError,
     Throws InvalidPackageException) =>
    Repository down -> PackageIdentifier -> Path Absolute -> IO ()
    downloadPackage rep :: Repository down
    rep@Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} PackageIdentifier
    pkgId Path Absolute
    dest =
        Repository down -> IO () -> IO ()
    forall (down :: * -> *) a. Repository down -> IO a -> IO a
    withMirror Repository down
    rep (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
          Repository down -> (IndexCallbacks -> IO ()) -> IO ()
    forall (down :: * -> *) a.
    Repository down -> (IndexCallbacks -> IO a) -> IO a
    withIndex Repository down
    rep ((IndexCallbacks -> IO ()) -> IO ())
    -> (IndexCallbacks -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \IndexCallbacks{Directory
    Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted ByteString)
    Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted Targets)
    (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted Hash)
    (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    indexLookupEntry :: DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    indexLookupFile :: forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    indexLookupFileEntry :: forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    indexLookupCabal :: Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted ByteString)
    indexLookupMetadata :: Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted Targets)
    indexLookupFileInfo :: (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    indexLookupHash :: (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted Hash)
    indexDirectory :: Directory
    indexDirectory :: IndexCallbacks -> Directory
    indexLookupHash :: IndexCallbacks
    -> (Throws InvalidPackageException, Throws VerificationError) =>
       PackageIdentifier -> IO (Trusted Hash)
    indexLookupFileInfo :: IndexCallbacks
    -> (Throws InvalidPackageException, Throws VerificationError) =>
       PackageIdentifier -> IO (Trusted FileInfo)
    indexLookupMetadata :: IndexCallbacks
    -> Throws InvalidPackageException =>
       PackageIdentifier -> IO (Trusted Targets)
    indexLookupCabal :: IndexCallbacks
    -> Throws InvalidPackageException =>
       PackageIdentifier -> IO (Trusted ByteString)
    indexLookupFileEntry :: IndexCallbacks
    -> forall dec.
       DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    indexLookupFile :: IndexCallbacks
    -> forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    indexLookupEntry :: IndexCallbacks
    -> DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    ..} -> (IO () -> IO ()) -> Verify () -> IO ()
    forall a. (IO () -> IO ()) -> Verify a -> IO a
    runVerify IO () -> IO ()
    repLockCache (Verify () -> IO ()) -> Verify () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ do
            -- Get the metadata (from the previously updated index)
            targetFileInfo <- IO (Trusted FileInfo) -> Verify (Trusted FileInfo)
    forall a. IO a -> Verify a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Trusted FileInfo) -> Verify (Trusted FileInfo))
    -> IO (Trusted FileInfo) -> Verify (Trusted FileInfo)
    forall a b. (a -> b) -> a -> b
    $ PackageIdentifier -> IO (Trusted FileInfo)
    (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    indexLookupFileInfo PackageIdentifier
    pkgId

            -- TODO: should we check if cached package available? (spec says no)
            tarGz <- do
              (targetPath, downloaded) <- getRemote' rep (AttemptNr 0) $
                RemotePkgTarGz pkgId targetFileInfo
              verifyFileInfo' (Just targetFileInfo) targetPath downloaded
              return downloaded

            -- If all checks succeed, copy file to its target location.
            liftIO $ downloadedCopyTo tarGz dest

    -- | Variation on 'downloadPackage' that takes a FilePath instead.
    downloadPackage' :: ( Throws SomeRemoteError
                        , Throws VerificationError
                        , Throws InvalidPackageException
                        )
                     => Repository down    -- ^ Repository
                     -> PackageIdentifier  -- ^ Package to download
                     -> FilePath           -- ^ Destination
                     -> IO ()
    downloadPackage' :: forall (down :: * -> *).
    (Throws SomeRemoteError, Throws VerificationError,
     Throws InvalidPackageException) =>
    Repository down -> PackageIdentifier -> String -> IO ()
    downloadPackage' Repository down
    rep PackageIdentifier
    pkgId String
    dest =
        Repository down -> PackageIdentifier -> Path Absolute -> IO ()
    forall (down :: * -> *).
    (Throws SomeRemoteError, Throws VerificationError,
     Throws InvalidPackageException) =>
    Repository down -> PackageIdentifier -> Path Absolute -> IO ()
    downloadPackage Repository down
    rep PackageIdentifier
    pkgId (Path Absolute -> IO ()) -> IO (Path Absolute) -> IO ()
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< FsPath -> IO (Path Absolute)
    makeAbsolute (String -> FsPath
    fromFilePath String
    dest)

    {-------------------------------------------------------------------------------
      Access to the tar index (the API is exported and used internally)

      NOTE: The files inside the index as evaluated lazily.

      1. The index tarball contains delegated target.json files for both unsigned
         and signed packages. We need to verify the signatures of all signed
         metadata (that is: the metadata for signed packages).

      2. Since the tarball also contains the .cabal files, we should also verify the
         hashes of those .cabal files against the hashes recorded in signed metadata
         (there is no point comparing against hashes recorded in unsigned metadata
         because attackers could just change those).

      Since we don't have author signing yet, we don't have any additional signed
      metadata and therefore we currently don't have to do anything here.

      TODO: If we have explicit, author-signed, lists of versions for a package (as
      described in @README.md@), then evaluating these "middle-level" delegation
      files lazily opens us up to a rollback attack: if we've never downloaded the
      delegations for a package before, then we have nothing to compare the version
      number in the file that we downloaded against. One option is to always
      download and verify all these middle level files (strictly); other is to
      include the version number of all of these files in the snapshot. This is
      described in more detail in
      <https://github.com/theupdateframework/tuf/issues/282#issuecomment-102468421>.

      TODO: Currently we hardcode the location of the package specific metadata. By
      rights we should read the global targets file and apply the delegation rules.
      Until we have author signing however this is unnecessary.
    -------------------------------------------------------------------------------}

    -- | Index directory
    data Directory = Directory {
        -- | The first entry in the dictionary
        Directory -> DirectoryEntry
    directoryFirst :: DirectoryEntry

        -- | The next available (i.e., one after last) directory entry
      , Directory -> DirectoryEntry
    directoryNext :: DirectoryEntry

        -- | Lookup an entry in the dictionary
        --
        -- This is an efficient operation.
      , Directory -> forall dec. IndexFile dec -> Maybe DirectoryEntry
    directoryLookup :: forall dec. IndexFile dec -> Maybe DirectoryEntry

        -- | An enumeration of all entries
        --
        -- This field is lazily constructed, so if you don't need it, it does not
        -- incur a performance overhead. Moreover, the 'IndexFile' is also created
        -- lazily so if you only need the raw 'IndexPath' there is no parsing
        -- overhead.
        --
        -- The entries are ordered by 'DirectoryEntry' so that the entries can
        -- efficiently be read in sequence.
        --
        -- NOTE: This means that there are two ways to enumerate all entries in the
        -- tar file, since when lookup an entry using 'indexLookupEntry' the
        -- 'DirectoryEntry' of the next entry is also returned. However, this
        -- involves reading through the entire @tar@ file. If you only need to read
        -- /some/ files, it is significantly more efficient to enumerate the tar
        -- entries using 'directoryEntries' instead and only call 'indexLookupEntry'
        -- when required.
      , Directory -> [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))]
    directoryEntries :: [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))]
      }

    -- | Entry into the Hackage index.
    newtype DirectoryEntry = DirectoryEntry {
        -- | (Low-level) block number of the tar index entry
        --
        -- Exposed for the benefit of clients who read the @.tar@ file directly.
        -- For this reason also the 'Show' and 'Read' instances for 'DirectoryEntry'
        -- just print and parse the underlying 'TarEntryOffset'.
        DirectoryEntry -> TarEntryOffset
    directoryEntryBlockNo :: Tar.TarEntryOffset
      }
      deriving (DirectoryEntry -> DirectoryEntry -> Bool
    (DirectoryEntry -> DirectoryEntry -> Bool)
    -> (DirectoryEntry -> DirectoryEntry -> Bool) -> Eq DirectoryEntry
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: DirectoryEntry -> DirectoryEntry -> Bool
    == :: DirectoryEntry -> DirectoryEntry -> Bool
    $c/= :: DirectoryEntry -> DirectoryEntry -> Bool
    /= :: DirectoryEntry -> DirectoryEntry -> Bool
    Eq, Eq DirectoryEntry
    Eq DirectoryEntry =>
    (DirectoryEntry -> DirectoryEntry -> Ordering)
    -> (DirectoryEntry -> DirectoryEntry -> Bool)
    -> (DirectoryEntry -> DirectoryEntry -> Bool)
    -> (DirectoryEntry -> DirectoryEntry -> Bool)
    -> (DirectoryEntry -> DirectoryEntry -> Bool)
    -> (DirectoryEntry -> DirectoryEntry -> DirectoryEntry)
    -> (DirectoryEntry -> DirectoryEntry -> DirectoryEntry)
    -> Ord DirectoryEntry
    DirectoryEntry -> DirectoryEntry -> Bool
    DirectoryEntry -> DirectoryEntry -> Ordering
    DirectoryEntry -> DirectoryEntry -> DirectoryEntry
    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
    $ccompare :: DirectoryEntry -> DirectoryEntry -> Ordering
    compare :: DirectoryEntry -> DirectoryEntry -> Ordering
    $c< :: DirectoryEntry -> DirectoryEntry -> Bool
    < :: DirectoryEntry -> DirectoryEntry -> Bool
    $c<= :: DirectoryEntry -> DirectoryEntry -> Bool
    <= :: DirectoryEntry -> DirectoryEntry -> Bool
    $c> :: DirectoryEntry -> DirectoryEntry -> Bool
    > :: DirectoryEntry -> DirectoryEntry -> Bool
    $c>= :: DirectoryEntry -> DirectoryEntry -> Bool
    >= :: DirectoryEntry -> DirectoryEntry -> Bool
    $cmax :: DirectoryEntry -> DirectoryEntry -> DirectoryEntry
    max :: DirectoryEntry -> DirectoryEntry -> DirectoryEntry
    $cmin :: DirectoryEntry -> DirectoryEntry -> DirectoryEntry
    min :: DirectoryEntry -> DirectoryEntry -> DirectoryEntry
    Ord)

    instance Show DirectoryEntry where
      show :: DirectoryEntry -> String
    show = TarEntryOffset -> String
    forall a. Show a => a -> String
    show (TarEntryOffset -> String)
    -> (DirectoryEntry -> TarEntryOffset) -> DirectoryEntry -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DirectoryEntry -> TarEntryOffset
    directoryEntryBlockNo

    instance Read DirectoryEntry where
      readsPrec :: Int -> ReadS DirectoryEntry
    readsPrec Int
    p = ((TarEntryOffset, String) -> (DirectoryEntry, String))
    -> [(TarEntryOffset, String)] -> [(DirectoryEntry, String)]
    forall a b. (a -> b) -> [a] -> [b]
    map ((TarEntryOffset -> DirectoryEntry)
    -> (TarEntryOffset, String) -> (DirectoryEntry, String)
    forall b c d. (b -> c) -> (b, d) -> (c, d)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (b, d) (c, d)
    first TarEntryOffset -> DirectoryEntry
    DirectoryEntry) ([(TarEntryOffset, String)] -> [(DirectoryEntry, String)])
    -> (String -> [(TarEntryOffset, String)]) -> ReadS DirectoryEntry
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> String -> [(TarEntryOffset, String)]
    forall a. Read a => Int -> ReadS a
    readsPrec Int
    p

    -- | Read the Hackage index directory
    --
    -- Should only be called after 'checkForUpdates'.
    getDirectory :: Repository down -> IO Directory
    getDirectory :: forall (down :: * -> *). Repository down -> IO Directory
    getDirectory Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} = TarIndex -> Directory
    mkDirectory (TarIndex -> Directory) -> IO TarIndex -> IO Directory
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> IO TarIndex
    repGetIndexIdx
      where
        mkDirectory :: Tar.TarIndex -> Directory
        mkDirectory :: TarIndex -> Directory
    mkDirectory TarIndex
    idx = Directory {
            directoryFirst :: DirectoryEntry
    directoryFirst   = TarEntryOffset -> DirectoryEntry
    DirectoryEntry TarEntryOffset
    0
          , directoryNext :: DirectoryEntry
    directoryNext    = TarEntryOffset -> DirectoryEntry
    DirectoryEntry (TarEntryOffset -> DirectoryEntry)
    -> TarEntryOffset -> DirectoryEntry
    forall a b. (a -> b) -> a -> b
    $ TarIndex -> TarEntryOffset
    Tar.indexEndEntryOffset TarIndex
    idx
          , directoryLookup :: forall dec. IndexFile dec -> Maybe DirectoryEntry
    directoryLookup  = (TarIndexEntry -> DirectoryEntry)
    -> Maybe TarIndexEntry -> Maybe DirectoryEntry
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    liftM TarIndexEntry -> DirectoryEntry
    dirEntry (Maybe TarIndexEntry -> Maybe DirectoryEntry)
    -> (IndexFile dec -> Maybe TarIndexEntry)
    -> IndexFile dec
    -> Maybe DirectoryEntry
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TarIndex -> String -> Maybe TarIndexEntry
    Tar.lookup TarIndex
    idx (String -> Maybe TarIndexEntry)
    -> (IndexFile dec -> String)
    -> IndexFile dec
    -> Maybe TarIndexEntry
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IndexFile dec -> String
    forall dec. IndexFile dec -> String
    filePath
          , directoryEntries :: [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))]
    directoryEntries = ((String, TarEntryOffset)
     -> (DirectoryEntry, IndexPath, Maybe (Some IndexFile)))
    -> [(String, TarEntryOffset)]
    -> [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))]
    forall a b. (a -> b) -> [a] -> [b]
    map (String, TarEntryOffset)
    -> (DirectoryEntry, IndexPath, Maybe (Some IndexFile))
    mkEntry ([(String, TarEntryOffset)]
     -> [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))])
    -> [(String, TarEntryOffset)]
    -> [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))]
    forall a b. (a -> b) -> a -> b
    $ ((String, TarEntryOffset) -> (String, TarEntryOffset) -> Ordering)
    -> [(String, TarEntryOffset)] -> [(String, TarEntryOffset)]
    forall a. (a -> a -> Ordering) -> [a] -> [a]
    sortBy (((String, TarEntryOffset) -> TarEntryOffset)
    -> (String, TarEntryOffset) -> (String, TarEntryOffset) -> Ordering
    forall a b. Ord a => (b -> a) -> b -> b -> Ordering
    comparing (String, TarEntryOffset) -> TarEntryOffset
    forall a b. (a, b) -> b
    snd) (TarIndex -> [(String, TarEntryOffset)]
    Tar.toList TarIndex
    idx)
          }

        mkEntry :: (FilePath, Tar.TarEntryOffset)
                -> (DirectoryEntry, IndexPath, Maybe (Some IndexFile))
        mkEntry :: (String, TarEntryOffset)
    -> (DirectoryEntry, IndexPath, Maybe (Some IndexFile))
    mkEntry (String
    fp, TarEntryOffset
    off) = (TarEntryOffset -> DirectoryEntry
    DirectoryEntry TarEntryOffset
    off, IndexPath
    path, IndexPath -> Maybe (Some IndexFile)
    indexFile IndexPath
    path)
          where
            path :: IndexPath
    path = String -> IndexPath
    indexPath String
    fp

        dirEntry :: Tar.TarIndexEntry -> DirectoryEntry
        dirEntry :: TarIndexEntry -> DirectoryEntry
    dirEntry (Tar.TarFileEntry TarEntryOffset
    offset) = TarEntryOffset -> DirectoryEntry
    DirectoryEntry TarEntryOffset
    offset
        dirEntry (Tar.TarDir [(String, TarIndexEntry)]
    _) = String -> DirectoryEntry
    forall a. HasCallStack => String -> a
    error String
    "directoryLookup: unexpected directory"

        indexFile :: IndexPath -> Maybe (Some IndexFile)
        indexFile :: IndexPath -> Maybe (Some IndexFile)
    indexFile = IndexLayout -> IndexPath -> Maybe (Some IndexFile)
    indexFileFromPath IndexLayout
    repIndexLayout

        indexPath :: FilePath -> IndexPath
        indexPath :: String -> IndexPath
    indexPath = Path Unrooted -> IndexPath
    forall root. Path Unrooted -> Path root
    rootPath (Path Unrooted -> IndexPath)
    -> (String -> Path Unrooted) -> String -> IndexPath
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Path Unrooted
    fromUnrootedFilePath

        filePath :: IndexFile dec -> FilePath
        filePath :: forall dec. IndexFile dec -> String
    filePath = Path Unrooted -> String
    toUnrootedFilePath (Path Unrooted -> String)
    -> (IndexFile dec -> Path Unrooted) -> IndexFile dec -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IndexPath -> Path Unrooted
    forall root. Path root -> Path Unrooted
    unrootPath (IndexPath -> Path Unrooted)
    -> (IndexFile dec -> IndexPath) -> IndexFile dec -> Path Unrooted
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IndexLayout -> forall dec. IndexFile dec -> IndexPath
    indexFileToPath IndexLayout
    repIndexLayout

    -- | Entry from the Hackage index; see 'withIndex'.
    data IndexEntry dec = IndexEntry {
        -- | The raw path in the tarfile
        forall dec. IndexEntry dec -> IndexPath
    indexEntryPath :: IndexPath

        -- | The parsed file (if recognised)
      , forall dec. IndexEntry dec -> Maybe (IndexFile dec)
    indexEntryPathParsed :: Maybe (IndexFile dec)

        -- | The raw contents
        --
        -- Although this is a lazy bytestring, this is actually read into memory
        -- strictly (i.e., it can safely be used outside the scope of withIndex and
        -- friends).
      , forall dec. IndexEntry dec -> ByteString
    indexEntryContent :: BS.L.ByteString

        -- | The parsed contents
        --
        -- This field is lazily constructed; the parser is not unless you do a
        -- pattern match on this value.
      , forall dec. IndexEntry dec -> Either SomeException dec
    indexEntryContentParsed :: Either SomeException dec

        -- | The time of the entry in the tarfile.
      , forall dec. IndexEntry dec -> EpochTime
    indexEntryTime :: Tar.EpochTime
      }

    -- | Various operations that we can perform on the index once its open
    --
    -- Note that 'IndexEntry' contains a fields both for the raw file contents and
    -- the parsed file contents; clients can choose which to use.
    --
    -- In principle these callbacks will do verification (once we have implemented
    -- author signing). Right now they don't need to do that, because the index as a
    -- whole will have been verified.
    data IndexCallbacks = IndexCallbacks {
        -- | Look up an entry by 'DirectoryEntry'
        --
        -- Since these 'DirectoryEntry's must come from somewhere (probably from the
        -- 'Directory'), it is assumed that they are valid; if they are not, an
        -- (unchecked) exception will be thrown.
        --
        -- This function also returns the 'DirectoryEntry' of the /next/ file in the
        -- index (if any) for the benefit of clients who wish to walk through the
        -- entire index.
        IndexCallbacks
    -> DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    indexLookupEntry :: DirectoryEntry
                         -> IO (Some IndexEntry, Maybe DirectoryEntry)

        -- | Look up an entry by 'IndexFile'
        --
        -- Returns 'Nothing' if the 'IndexFile' does not refer to an existing file.
      , IndexCallbacks
    -> forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    indexLookupFile :: forall dec.
                           IndexFile dec
                        -> IO (Maybe (IndexEntry dec))

        -- | Variation if both the 'DirectoryEntry' and the 'IndexFile' are known
        --
        -- You might use this when scanning the index using 'directoryEntries'.
      , IndexCallbacks
    -> forall dec.
       DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    indexLookupFileEntry :: forall dec.
                                DirectoryEntry
                             -> IndexFile dec
                             -> IO (IndexEntry dec)

        -- | Get (raw) cabal file (wrapper around 'indexLookupFile')
      , IndexCallbacks
    -> Throws InvalidPackageException =>
       PackageIdentifier -> IO (Trusted ByteString)
    indexLookupCabal :: Throws InvalidPackageException
                         => PackageIdentifier
                         -> IO (Trusted BS.L.ByteString)

        -- | Lookup package metadata (wrapper around 'indexLookupFile')
        --
        -- This will throw an (unchecked) exception if the @targets.json@ file
        -- could not be parsed.
      , IndexCallbacks
    -> Throws InvalidPackageException =>
       PackageIdentifier -> IO (Trusted Targets)
    indexLookupMetadata :: Throws InvalidPackageException
                            => PackageIdentifier
                            -> IO (Trusted Targets)

        -- | Get file info (including hash) (wrapper around 'indexLookupFile')
      , IndexCallbacks
    -> (Throws InvalidPackageException, Throws VerificationError) =>
       PackageIdentifier -> IO (Trusted FileInfo)
    indexLookupFileInfo :: ( Throws InvalidPackageException
                               , Throws VerificationError
                               )
                            => PackageIdentifier
                            -> IO (Trusted FileInfo)

        -- | Get the SHA256 hash for a package (wrapper around 'indexLookupInfo')
        --
        -- In addition to the exceptions thrown by 'indexLookupInfo', this will also
        -- throw an exception if the SHA256 is not listed in the 'FileMap' (again,
        -- this will not happen with a well-formed Hackage index.)
      , IndexCallbacks
    -> (Throws InvalidPackageException, Throws VerificationError) =>
       PackageIdentifier -> IO (Trusted Hash)
    indexLookupHash :: ( Throws InvalidPackageException
                           , Throws VerificationError
                           )
                        => PackageIdentifier
                        -> IO (Trusted Hash)

        -- | The 'Directory' for the index
        --
        -- We provide this here because 'withIndex' will have read this anyway.
      , IndexCallbacks -> Directory
    indexDirectory :: Directory
      }

    -- | Look up entries in the Hackage index
    --
    -- This is in 'withFile' style so that clients can efficiently look up multiple
    -- files from the index.
    --
    -- Should only be called after 'checkForUpdates'.
    withIndex :: Repository down -> (IndexCallbacks -> IO a) -> IO a
    withIndex :: forall (down :: * -> *) a.
    Repository down -> (IndexCallbacks -> IO a) -> IO a
    withIndex rep :: Repository down
    rep@Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} IndexCallbacks -> IO a
    callback = do
        -- We need the cached root information in order to resolve key IDs and
        -- verify signatures. Note that whenever we read a JSON file, we verify
        -- signatures (even if we don't verify the keys); if this is a problem
        -- (for performance) we need to parameterize parseJSON.
        (_cachedRoot, keyEnv) <- Repository down -> IO (Trusted Root, KeyEnv)
    forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> m (Trusted Root, KeyEnv)
    readLocalRoot Repository down
    rep

        -- We need the directory to resolve 'IndexFile's and to know the index of
        -- the last entry.
        dir@Directory{..} <- getDirectory rep

        -- Open the index
        repWithIndex $ \Handle
    h -> do
          let getEntry :: DirectoryEntry
                       -> IO (Some IndexEntry, Maybe DirectoryEntry)
              getEntry :: DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    getEntry DirectoryEntry
    entry = do
                (tarEntry, content, next) <- DirectoryEntry -> IO (Entry, ByteString, Maybe DirectoryEntry)
    getTarEntry DirectoryEntry
    entry
                let path = Entry -> IndexPath
    indexPath Entry
    tarEntry
                case indexFile path of
                  Maybe (Some IndexFile)
    Nothing ->
                    (Some IndexEntry, Maybe DirectoryEntry)
    -> IO (Some IndexEntry, Maybe DirectoryEntry)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (IndexEntry (ZonkAny 0) -> Some IndexEntry
    forall (f :: * -> *) a. f a -> Some f
    Some (Entry
    -> ByteString
    -> Maybe (IndexFile (ZonkAny 0))
    -> IndexEntry (ZonkAny 0)
    forall dec.
    Entry -> ByteString -> Maybe (IndexFile dec) -> IndexEntry dec
    mkEntry Entry
    tarEntry ByteString
    content Maybe (IndexFile (ZonkAny 0))
    forall a. Maybe a
    Nothing), Maybe DirectoryEntry
    next)
                  Just (Some IndexFile a
    file) ->
                    (Some IndexEntry, Maybe DirectoryEntry)
    -> IO (Some IndexEntry, Maybe DirectoryEntry)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (IndexEntry a -> Some IndexEntry
    forall (f :: * -> *) a. f a -> Some f
    Some (Entry -> ByteString -> Maybe (IndexFile a) -> IndexEntry a
    forall dec.
    Entry -> ByteString -> Maybe (IndexFile dec) -> IndexEntry dec
    mkEntry Entry
    tarEntry ByteString
    content (IndexFile a -> Maybe (IndexFile a)
    forall a. a -> Maybe a
    Just IndexFile a
    file)), Maybe DirectoryEntry
    next)

              getFile :: IndexFile dec -> IO (Maybe (IndexEntry dec))
              getFile :: forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    getFile IndexFile dec
    file =
                case IndexFile dec -> Maybe DirectoryEntry
    forall dec. IndexFile dec -> Maybe DirectoryEntry
    directoryLookup IndexFile dec
    file of
                  Maybe DirectoryEntry
    Nothing       -> Maybe (IndexEntry dec) -> IO (Maybe (IndexEntry dec))
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe (IndexEntry dec)
    forall a. Maybe a
    Nothing
                  Just DirectoryEntry
    dirEntry -> IndexEntry dec -> Maybe (IndexEntry dec)
    forall a. a -> Maybe a
    Just (IndexEntry dec -> Maybe (IndexEntry dec))
    -> IO (IndexEntry dec) -> IO (Maybe (IndexEntry dec))
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    getFileEntry DirectoryEntry
    dirEntry IndexFile dec
    file

              getFileEntry :: DirectoryEntry
                           -> IndexFile dec
                           -> IO (IndexEntry dec)
              getFileEntry :: forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    getFileEntry DirectoryEntry
    dirEntry IndexFile dec
    file = do
                (tarEntry, content, _next) <- DirectoryEntry -> IO (Entry, ByteString, Maybe DirectoryEntry)
    getTarEntry DirectoryEntry
    dirEntry
                return $ mkEntry tarEntry content (Just file)

              mkEntry :: Tar.Entry
                      -> BS.L.ByteString
                      -> Maybe (IndexFile dec)
                      -> IndexEntry dec
              mkEntry :: forall dec.
    Entry -> ByteString -> Maybe (IndexFile dec) -> IndexEntry dec
    mkEntry Entry
    tarEntry ByteString
    content Maybe (IndexFile dec)
    mFile = IndexEntry {
                  indexEntryPath :: IndexPath
    indexEntryPath          = Entry -> IndexPath
    indexPath Entry
    tarEntry
                , indexEntryPathParsed :: Maybe (IndexFile dec)
    indexEntryPathParsed    = Maybe (IndexFile dec)
    mFile
                , indexEntryContent :: ByteString
    indexEntryContent       = ByteString
    content
                , indexEntryContentParsed :: Either SomeException dec
    indexEntryContentParsed = Maybe (IndexFile dec) -> ByteString -> Either SomeException dec
    forall dec.
    Maybe (IndexFile dec) -> ByteString -> Either SomeException dec
    parseContent Maybe (IndexFile dec)
    mFile ByteString
    content
                , indexEntryTime :: EpochTime
    indexEntryTime          = Entry -> EpochTime
    forall tarPath linkTarget. GenEntry tarPath linkTarget -> EpochTime
    Tar.entryTime Entry
    tarEntry
                }

              parseContent :: Maybe (IndexFile dec)
                           -> BS.L.ByteString -> Either SomeException dec
              parseContent :: forall dec.
    Maybe (IndexFile dec) -> ByteString -> Either SomeException dec
    parseContent Maybe (IndexFile dec)
    Nothing     ByteString
    _   = SomeException -> Either SomeException dec
    forall a b. a -> Either a b
    Left SomeException
    pathNotRecognized
              parseContent (Just IndexFile dec
    file) ByteString
    raw = case IndexFile dec
    file of
                IndexPkgPrefs PackageName
    _ ->
                  dec -> Either SomeException dec
    forall a b. b -> Either a b
    Right () -- We don't currently parse preference files
                IndexPkgCabal PackageIdentifier
    _ ->
                  dec -> Either SomeException dec
    forall a b. b -> Either a b
    Right () -- We don't currently parse .cabal files
                IndexPkgMetadata PackageIdentifier
    _ ->
                  let mkEx :: Either DeserializationError dec -> Either SomeException dec
    mkEx = (DeserializationError -> Either SomeException dec)
    -> (dec -> Either SomeException dec)
    -> Either DeserializationError dec
    -> Either SomeException dec
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either
                               (SomeException -> Either SomeException dec
    forall a b. a -> Either a b
    Left (SomeException -> Either SomeException dec)
    -> (DeserializationError -> SomeException)
    -> DeserializationError
    -> Either SomeException dec
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . InvalidFileInIndex -> SomeException
    forall e. (Exception e, HasExceptionContext) => e -> SomeException
    SomeException (InvalidFileInIndex -> SomeException)
    -> (DeserializationError -> InvalidFileInIndex)
    -> DeserializationError
    -> SomeException
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IndexFile dec
    -> ByteString -> DeserializationError -> InvalidFileInIndex
    forall dec.
    IndexFile dec
    -> ByteString -> DeserializationError -> InvalidFileInIndex
    InvalidFileInIndex IndexFile dec
    file ByteString
    raw)
                               dec -> Either SomeException dec
    forall a b. b -> Either a b
    Right
                  in Either DeserializationError dec -> Either SomeException dec
    mkEx (Either DeserializationError dec -> Either SomeException dec)
    -> Either DeserializationError dec -> Either SomeException dec
    forall a b. (a -> b) -> a -> b
    $ KeyEnv -> ByteString -> Either DeserializationError dec
    forall a.
    FromJSON ReadJSON_Keys_NoLayout a =>
    KeyEnv -> ByteString -> Either DeserializationError a
    parseJSON_Keys_NoLayout KeyEnv
    keyEnv ByteString
    raw

              -- Read an entry from the tar file. Returns entry content separately,
              -- throwing an exception if the entry is not a regular file.
              -- Also throws an exception if the 'DirectoryEntry' is invalid.
              getTarEntry :: DirectoryEntry
                          -> IO (Tar.Entry, BS.L.ByteString, Maybe DirectoryEntry)
              getTarEntry :: DirectoryEntry -> IO (Entry, ByteString, Maybe DirectoryEntry)
    getTarEntry (DirectoryEntry TarEntryOffset
    offset) = do
                entry   <- Handle -> TarEntryOffset -> IO Entry
    Tar.hReadEntry Handle
    h TarEntryOffset
    offset
                content <- case Tar.entryContent entry of
                             Tar.NormalFile ByteString
    content EpochTime
    _sz -> ByteString -> IO ByteString
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ByteString
    content
                             GenEntryContent LinkTarget
    _ -> IOError -> IO ByteString
    forall e a. (HasCallStack, Exception e) => e -> IO a
    throwIO (IOError -> IO ByteString) -> IOError -> IO ByteString
    forall a b. (a -> b) -> a -> b
    $ String -> IOError
    userError String
    "withIndex: unexpected entry"
                let next  = TarEntryOffset -> DirectoryEntry
    DirectoryEntry (TarEntryOffset -> DirectoryEntry)
    -> TarEntryOffset -> DirectoryEntry
    forall a b. (a -> b) -> a -> b
    $ Entry -> TarEntryOffset -> TarEntryOffset
    Tar.nextEntryOffset Entry
    entry TarEntryOffset
    offset
                    mNext = Bool -> Maybe ()
    forall (f :: * -> *). Alternative f => Bool -> f ()
    guard (DirectoryEntry
    next DirectoryEntry -> DirectoryEntry -> Bool
    forall a. Ord a => a -> a -> Bool
    < DirectoryEntry
    directoryNext) Maybe () -> Maybe DirectoryEntry -> Maybe DirectoryEntry
    forall a b. Maybe a -> Maybe b -> Maybe b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> DirectoryEntry -> Maybe DirectoryEntry
    forall a. a -> Maybe a
    forall (m :: * -> *) a. Monad m => a -> m a
    return DirectoryEntry
    next
                return (entry, content, mNext)

              -- Get cabal file
              getCabal :: Throws InvalidPackageException
                       => PackageIdentifier -> IO (Trusted BS.L.ByteString)
              getCabal :: Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted ByteString)
    getCabal PackageIdentifier
    pkgId = do
                mCabal <- IndexFile () -> IO (Maybe (IndexEntry ()))
    forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    getFile (IndexFile () -> IO (Maybe (IndexEntry ())))
    -> IndexFile () -> IO (Maybe (IndexEntry ()))
    forall a b. (a -> b) -> a -> b
    $ PackageIdentifier -> IndexFile ()
    IndexPkgCabal PackageIdentifier
    pkgId
                case mCabal of
                  Maybe (IndexEntry ())
    Nothing ->
                    InvalidPackageException -> IO (Trusted ByteString)
    forall e a. (Exception e, Throws e) => e -> IO a
    throwChecked (InvalidPackageException -> IO (Trusted ByteString))
    -> InvalidPackageException -> IO (Trusted ByteString)
    forall a b. (a -> b) -> a -> b
    $ PackageIdentifier -> InvalidPackageException
    InvalidPackageException PackageIdentifier
    pkgId
                  Just IndexEntry{EpochTime
    Maybe (IndexFile ())
    Either SomeException ()
    ByteString
    IndexPath
    indexEntryPath :: forall dec. IndexEntry dec -> IndexPath
    indexEntryPathParsed :: forall dec. IndexEntry dec -> Maybe (IndexFile dec)
    indexEntryContent :: forall dec. IndexEntry dec -> ByteString
    indexEntryContentParsed :: forall dec. IndexEntry dec -> Either SomeException dec
    indexEntryTime :: forall dec. IndexEntry dec -> EpochTime
    indexEntryPath :: IndexPath
    indexEntryPathParsed :: Maybe (IndexFile ())
    indexEntryContent :: ByteString
    indexEntryContentParsed :: Either SomeException ()
    indexEntryTime :: EpochTime
    ..} ->
                    Trusted ByteString -> IO (Trusted ByteString)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Trusted ByteString -> IO (Trusted ByteString))
    -> Trusted ByteString -> IO (Trusted ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Trusted ByteString
    forall a. a -> Trusted a
    DeclareTrusted ByteString
    indexEntryContent

              -- Get package metadata
              getMetadata :: Throws InvalidPackageException
                          => PackageIdentifier -> IO (Trusted Targets)
              getMetadata :: Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted Targets)
    getMetadata PackageIdentifier
    pkgId = do
                mEntry <- IndexFile (Signed Targets)
    -> IO (Maybe (IndexEntry (Signed Targets)))
    forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    getFile (IndexFile (Signed Targets)
     -> IO (Maybe (IndexEntry (Signed Targets))))
    -> IndexFile (Signed Targets)
    -> IO (Maybe (IndexEntry (Signed Targets)))
    forall a b. (a -> b) -> a -> b
    $ PackageIdentifier -> IndexFile (Signed Targets)
    IndexPkgMetadata PackageIdentifier
    pkgId
                case mEntry of
                  Maybe (IndexEntry (Signed Targets))
    Nothing ->
                    InvalidPackageException -> IO (Trusted Targets)
    forall e a. (Exception e, Throws e) => e -> IO a
    throwChecked (InvalidPackageException -> IO (Trusted Targets))
    -> InvalidPackageException -> IO (Trusted Targets)
    forall a b. (a -> b) -> a -> b
    $ PackageIdentifier -> InvalidPackageException
    InvalidPackageException PackageIdentifier
    pkgId
                  Just IndexEntry{indexEntryContentParsed :: forall dec. IndexEntry dec -> Either SomeException dec
    indexEntryContentParsed = Left SomeException
    ex} ->
                    SomeException -> IO (Trusted Targets)
    forall e a. Exception e => e -> IO a
    throwUnchecked (SomeException -> IO (Trusted Targets))
    -> SomeException -> IO (Trusted Targets)
    forall a b. (a -> b) -> a -> b
    $ SomeException
    ex
                  Just IndexEntry{indexEntryContentParsed :: forall dec. IndexEntry dec -> Either SomeException dec
    indexEntryContentParsed = Right Signed Targets
    signed} ->
                    Trusted Targets -> IO (Trusted Targets)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Trusted Targets -> IO (Trusted Targets))
    -> Trusted Targets -> IO (Trusted Targets)
    forall a b. (a -> b) -> a -> b
    $ Signed Targets -> Trusted Targets
    forall a. Signed a -> Trusted a
    trustLocalFile Signed Targets
    signed

              -- Get package info
              getFileInfo :: ( Throws InvalidPackageException
                             , Throws VerificationError
                             )
                          => PackageIdentifier -> IO (Trusted FileInfo)
              getFileInfo :: (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    getFileInfo PackageIdentifier
    pkgId = do
                targets <- PackageIdentifier -> IO (Trusted Targets)
    Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted Targets)
    getMetadata PackageIdentifier
    pkgId

                let mTargetMetadata :: Maybe (Trusted FileInfo)
                    mTargetMetadata = Trusted (Maybe FileInfo) -> Maybe (Trusted FileInfo)
    forall (f :: * -> *) a.
    Traversable f =>
    Trusted (f a) -> f (Trusted a)
    trustElems
                                    (Trusted (Maybe FileInfo) -> Maybe (Trusted FileInfo))
    -> Trusted (Maybe FileInfo) -> Maybe (Trusted FileInfo)
    forall a b. (a -> b) -> a -> b
    $ StaticPtr (TargetPath -> Targets -> Maybe FileInfo)
    -> Trusted (TargetPath -> Targets -> Maybe FileInfo)
    forall a. StaticPtr a -> Trusted a
    trustStatic (static TargetPath -> Targets -> Maybe FileInfo
    targetsLookup)
                         Trusted (TargetPath -> Targets -> Maybe FileInfo)
    -> Trusted TargetPath -> Trusted (Targets -> Maybe FileInfo)
    forall a b. Trusted (a -> b) -> Trusted a -> Trusted b
    `trustApply` TargetPath -> Trusted TargetPath
    forall a. a -> Trusted a
    DeclareTrusted (PackageIdentifier -> TargetPath
    targetPath PackageIdentifier
    pkgId)
                         Trusted (Targets -> Maybe FileInfo)
    -> Trusted Targets -> Trusted (Maybe FileInfo)
    forall a b. Trusted (a -> b) -> Trusted a -> Trusted b
    `trustApply` Trusted Targets
    targets

                case mTargetMetadata of
                  Maybe (Trusted FileInfo)
    Nothing ->
                    VerificationError -> IO (Trusted FileInfo)
    forall e a. (Exception e, Throws e) => e -> IO a
    throwChecked (VerificationError -> IO (Trusted FileInfo))
    -> VerificationError -> IO (Trusted FileInfo)
    forall a b. (a -> b) -> a -> b
    $ TargetPath -> VerificationError
    VerificationErrorUnknownTarget (PackageIdentifier -> TargetPath
    targetPath PackageIdentifier
    pkgId)
                  Just Trusted FileInfo
    info ->
                    Trusted FileInfo -> IO (Trusted FileInfo)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Trusted FileInfo
    info

              -- Get package SHA256
              getHash :: ( Throws InvalidPackageException
                         , Throws VerificationError
                         )
                      => PackageIdentifier -> IO (Trusted Hash)
              getHash :: (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted Hash)
    getHash PackageIdentifier
    pkgId = do
                info <- PackageIdentifier -> IO (Trusted FileInfo)
    (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    getFileInfo PackageIdentifier
    pkgId

                let mTrustedHash :: Maybe (Trusted Hash)
                    mTrustedHash = Trusted (Maybe Hash) -> Maybe (Trusted Hash)
    forall (f :: * -> *) a.
    Traversable f =>
    Trusted (f a) -> f (Trusted a)
    trustElems
                                 (Trusted (Maybe Hash) -> Maybe (Trusted Hash))
    -> Trusted (Maybe Hash) -> Maybe (Trusted Hash)
    forall a b. (a -> b) -> a -> b
    $ StaticPtr (FileInfo -> Maybe Hash)
    -> Trusted (FileInfo -> Maybe Hash)
    forall a. StaticPtr a -> Trusted a
    trustStatic (static FileInfo -> Maybe Hash
    fileInfoSHA256)
                      Trusted (FileInfo -> Maybe Hash)
    -> Trusted FileInfo -> Trusted (Maybe Hash)
    forall a b. Trusted (a -> b) -> Trusted a -> Trusted b
    `trustApply` Trusted FileInfo
    info

                case mTrustedHash of
                  Maybe (Trusted Hash)
    Nothing ->
                    VerificationError -> IO (Trusted Hash)
    forall e a. (Exception e, Throws e) => e -> IO a
    throwChecked (VerificationError -> IO (Trusted Hash))
    -> VerificationError -> IO (Trusted Hash)
    forall a b. (a -> b) -> a -> b
    $ TargetPath -> VerificationError
    VerificationErrorMissingSHA256 (PackageIdentifier -> TargetPath
    targetPath PackageIdentifier
    pkgId)
                  Just Trusted Hash
    hash ->
                    Trusted Hash -> IO (Trusted Hash)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Trusted Hash
    hash

          IndexCallbacks -> IO a
    callback IndexCallbacks{
              indexLookupEntry :: DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    indexLookupEntry     = DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
    getEntry
            , indexLookupFile :: forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    indexLookupFile      = IndexFile dec -> IO (Maybe (IndexEntry dec))
    forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
    getFile
            , indexLookupFileEntry :: forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    indexLookupFileEntry = DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
    getFileEntry
            , indexDirectory :: Directory
    indexDirectory       = Directory
    dir
            , indexLookupCabal :: Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted ByteString)
    indexLookupCabal     = PackageIdentifier -> IO (Trusted ByteString)
    Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted ByteString)
    getCabal
            , indexLookupMetadata :: Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted Targets)
    indexLookupMetadata  = PackageIdentifier -> IO (Trusted Targets)
    Throws InvalidPackageException =>
    PackageIdentifier -> IO (Trusted Targets)
    getMetadata
            , indexLookupFileInfo :: (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    indexLookupFileInfo  = PackageIdentifier -> IO (Trusted FileInfo)
    (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted FileInfo)
    getFileInfo
            , indexLookupHash :: (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted Hash)
    indexLookupHash      = PackageIdentifier -> IO (Trusted Hash)
    (Throws InvalidPackageException, Throws VerificationError) =>
    PackageIdentifier -> IO (Trusted Hash)
    getHash
            }
      where
        indexPath :: Tar.Entry -> IndexPath
        indexPath :: Entry -> IndexPath
    indexPath = Path Unrooted -> IndexPath
    forall root. Path Unrooted -> Path root
    rootPath (Path Unrooted -> IndexPath)
    -> (Entry -> Path Unrooted) -> Entry -> IndexPath
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Path Unrooted
    fromUnrootedFilePath
                  (String -> Path Unrooted)
    -> (Entry -> String) -> Entry -> Path Unrooted
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TarPath -> String
    Tar.fromTarPathToPosixPath
                  (TarPath -> String) -> (Entry -> TarPath) -> Entry -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Entry -> TarPath
    forall tarPath linkTarget. GenEntry tarPath linkTarget -> tarPath
    Tar.entryTarPath

        indexFile :: IndexPath -> Maybe (Some IndexFile)
        indexFile :: IndexPath -> Maybe (Some IndexFile)
    indexFile = IndexLayout -> IndexPath -> Maybe (Some IndexFile)
    indexFileFromPath IndexLayout
    repIndexLayout

        targetPath :: PackageIdentifier -> TargetPath
        targetPath :: PackageIdentifier -> TargetPath
    targetPath = RepoPath -> TargetPath
    TargetPathRepo (RepoPath -> TargetPath)
    -> (PackageIdentifier -> RepoPath)
    -> PackageIdentifier
    -> TargetPath
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . RepoLayout -> PackageIdentifier -> RepoPath
    repoLayoutPkgTarGz RepoLayout
    repLayout

        pathNotRecognized :: SomeException
        pathNotRecognized :: SomeException
    pathNotRecognized = IOError -> SomeException
    forall e. (Exception e, HasExceptionContext) => e -> SomeException
    SomeException (String -> IOError
    userError String
    "Path not recognized")

    {-------------------------------------------------------------------------------
      Bootstrapping
    -------------------------------------------------------------------------------}

    -- | Check if we need to bootstrap (i.e., if we have root info)
    requiresBootstrap :: Repository down -> IO Bool
    requiresBootstrap :: forall (down :: * -> *). Repository down -> IO Bool
    requiresBootstrap Repository down
    rep = Maybe (Path Absolute) -> Bool
    forall a. Maybe a -> Bool
    isNothing (Maybe (Path Absolute) -> Bool)
    -> IO (Maybe (Path Absolute)) -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetCached Repository down
    rep CachedFile
    CachedRoot

    -- | Bootstrap the chain of trust
    --
    -- New clients might need to obtain a copy of the root metadata. This however
    -- represents a chicken-and-egg problem: how can we verify the root metadata
    -- we downloaded? The only possibility is to be provided with a set of an
    -- out-of-band set of root keys and an appropriate threshold.
    --
    -- Clients who provide a threshold of 0 can do an initial "unsafe" update
    -- of the root information, if they wish.
    --
    -- The downloaded root information will _only_ be verified against the
    -- provided keys, and _not_ against previously downloaded root info (if any).
    -- It is the responsibility of the client to call `bootstrap` only when this
    -- is the desired behaviour.
    bootstrap :: (Throws SomeRemoteError, Throws VerificationError)
              => Repository down -> [KeyId] -> KeyThreshold -> IO ()
    bootstrap :: forall (down :: * -> *).
    (Throws SomeRemoteError, Throws VerificationError) =>
    Repository down -> [KeyId] -> KeyThreshold -> IO ()
    bootstrap rep :: Repository down
    rep@Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} [KeyId]
    trustedRootKeys KeyThreshold
    keyThreshold = Repository down -> IO () -> IO ()
    forall (down :: * -> *) a. Repository down -> IO a -> IO a
    withMirror Repository down
    rep (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ (IO () -> IO ()) -> Verify () -> IO ()
    forall a. (IO () -> IO ()) -> Verify a -> IO a
    runVerify IO () -> IO ()
    repLockCache (Verify () -> IO ()) -> Verify () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ do
        _newRoot :: Trusted Root <- do
          (targetPath, tempPath) <- Repository down
    -> AttemptNr
    -> RemoteFile (FormatUn :- ()) Metadata
    -> Verify (TargetPath, down Metadata)
    forall f (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile (f :- ()) typ
    -> Verify (TargetPath, down typ)
    getRemote' Repository down
    rep (Int -> AttemptNr
    AttemptNr Int
    0) (Maybe (Trusted FileInfo) -> RemoteFile (FormatUn :- ()) Metadata
    RemoteRoot Maybe (Trusted FileInfo)
    forall a. Maybe a
    Nothing)
          signed   <- throwErrorsChecked (VerificationErrorDeserialization targetPath) =<<
                        readDownloadedJSON rep KeyEnv.empty tempPath
          verified <- throwErrorsChecked id $ verifyFingerprints
                        trustedRootKeys
                        keyThreshold
                        targetPath
                        signed
          return $ trustVerified verified

        clearCache rep

    {-------------------------------------------------------------------------------
      Wrapper around the Repository functions
    -------------------------------------------------------------------------------}

    getRemote :: forall fs down typ. Throws SomeRemoteError
              => Repository down
              -> AttemptNr
              -> RemoteFile fs typ
              -> Verify (Some Format, TargetPath, down typ)
    getRemote :: forall fs (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile fs typ
    -> Verify (Some Format, TargetPath, down typ)
    getRemote Repository down
    r AttemptNr
    attemptNr RemoteFile fs typ
    file = do
        (Some format, downloaded) <- Repository down
    -> forall fs typ.
       Throws SomeRemoteError =>
       AttemptNr
       -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    forall (down :: * -> *).
    Repository down
    -> forall fs typ.
       Throws SomeRemoteError =>
       AttemptNr
       -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repGetRemote Repository down
    r AttemptNr
    attemptNr RemoteFile fs typ
    file
        let targetPath = RepoPath -> TargetPath
    TargetPathRepo (RepoPath -> TargetPath) -> RepoPath -> TargetPath
    forall a b. (a -> b) -> a -> b
    $ RepoLayout -> RemoteFile fs typ -> HasFormat fs a -> RepoPath
    forall fs typ f.
    RepoLayout -> RemoteFile fs typ -> HasFormat fs f -> RepoPath
    remoteRepoPath' (Repository down -> RepoLayout
    forall (down :: * -> *). Repository down -> RepoLayout
    repLayout Repository down
    r) RemoteFile fs typ
    file HasFormat fs a
    format
        return (Some (hasFormatGet format), targetPath, downloaded)

    -- | Variation on getRemote where we only expect one type of result
    getRemote' :: forall f down typ. Throws SomeRemoteError
               => Repository down
               -> AttemptNr
               -> RemoteFile (f :- ()) typ
               -> Verify (TargetPath, down typ)
    getRemote' :: forall f (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile (f :- ()) typ
    -> Verify (TargetPath, down typ)
    getRemote' Repository down
    r AttemptNr
    isRetry RemoteFile (f :- ()) typ
    file = (Some Format, TargetPath, down typ) -> (TargetPath, down typ)
    forall {a} {a} {b}. (a, a, b) -> (a, b)
    ignoreFormat ((Some Format, TargetPath, down typ) -> (TargetPath, down typ))
    -> Verify (Some Format, TargetPath, down typ)
    -> Verify (TargetPath, down typ)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Repository down
    -> AttemptNr
    -> RemoteFile (f :- ()) typ
    -> Verify (Some Format, TargetPath, down typ)
    forall fs (down :: * -> *) typ.
    Throws SomeRemoteError =>
    Repository down
    -> AttemptNr
    -> RemoteFile fs typ
    -> Verify (Some Format, TargetPath, down typ)
    getRemote Repository down
    r AttemptNr
    isRetry RemoteFile (f :- ()) typ
    file
      where
        ignoreFormat :: (a, a, b) -> (a, b)
    ignoreFormat (a
    _format, a
    targetPath, b
    tempPath) = (a
    targetPath, b
    tempPath)

    clearCache :: MonadIO m => Repository down -> m ()
    clearCache :: forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> m ()
    clearCache Repository down
    r = IO () -> m ()
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> m ()) -> IO () -> m ()
    forall a b. (a -> b) -> a -> b
    $ Repository down -> IO ()
    forall (down :: * -> *). Repository down -> IO ()
    repClearCache Repository down
    r

    log :: MonadIO m => Repository down -> LogMessage -> m ()
    log :: forall (m :: * -> *) (down :: * -> *).
    MonadIO m =>
    Repository down -> LogMessage -> m ()
    log Repository down
    r LogMessage
    msg = IO () -> m ()
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> m ()) -> IO () -> m ()
    forall a b. (a -> b) -> a -> b
    $ Repository down -> LogMessage -> IO ()
    forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repLog Repository down
    r LogMessage
    msg

    -- Tries to load the cached mirrors file
    withMirror :: Repository down -> IO a -> IO a
    withMirror :: forall (down :: * -> *) a. Repository down -> IO a -> IO a
    withMirror Repository down
    rep IO a
    callback = do
        mMirrors <- Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetCached Repository down
    rep CachedFile
    CachedMirrors
        mirrors  <- case mMirrors of
          Maybe (Path Absolute)
    Nothing -> Maybe [Mirror] -> IO (Maybe [Mirror])
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe [Mirror]
    forall a. Maybe a
    Nothing
          Just Path Absolute
    fp -> UninterpretedSignatures Mirrors -> Maybe [Mirror]
    filterMirrors (UninterpretedSignatures Mirrors -> Maybe [Mirror])
    -> IO (UninterpretedSignatures Mirrors) -> IO (Maybe [Mirror])
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
                       ((DeserializationError -> LocalFileCorrupted)
    -> Either DeserializationError (UninterpretedSignatures Mirrors)
    -> IO (UninterpretedSignatures Mirrors)
    forall (m :: * -> *) e' e a.
    (MonadIO m, Exception e') =>
    (e -> e') -> Either e a -> m a
    throwErrorsUnchecked DeserializationError -> LocalFileCorrupted
    LocalFileCorrupted (Either DeserializationError (UninterpretedSignatures Mirrors)
     -> IO (UninterpretedSignatures Mirrors))
    -> IO
         (Either DeserializationError (UninterpretedSignatures Mirrors))
    -> IO (UninterpretedSignatures Mirrors)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<<
                         Path Absolute
    -> IO
         (Either DeserializationError (UninterpretedSignatures Mirrors))
    forall root a.
    (FsRoot root, FromJSON ReadJSON_NoKeys_NoLayout a) =>
    Path root -> IO (Either DeserializationError a)
    readJSON_NoKeys_NoLayout Path Absolute
    fp)
        repWithMirror rep mirrors $ callback
      where
        filterMirrors :: UninterpretedSignatures Mirrors -> Maybe [Mirror]
        filterMirrors :: UninterpretedSignatures Mirrors -> Maybe [Mirror]
    filterMirrors = [Mirror] -> Maybe [Mirror]
    forall a. a -> Maybe a
    Just
                      ([Mirror] -> Maybe [Mirror])
    -> (UninterpretedSignatures Mirrors -> [Mirror])
    -> UninterpretedSignatures Mirrors
    -> Maybe [Mirror]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Mirror -> Bool) -> [Mirror] -> [Mirror]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (MirrorContent -> Bool
    canUseMirror (MirrorContent -> Bool)
    -> (Mirror -> MirrorContent) -> Mirror -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Mirror -> MirrorContent
    mirrorContent)
                      ([Mirror] -> [Mirror])
    -> (UninterpretedSignatures Mirrors -> [Mirror])
    -> UninterpretedSignatures Mirrors
    -> [Mirror]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Mirrors -> [Mirror]
    mirrorsMirrors
                      (Mirrors -> [Mirror])
    -> (UninterpretedSignatures Mirrors -> Mirrors)
    -> UninterpretedSignatures Mirrors
    -> [Mirror]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . UninterpretedSignatures Mirrors -> Mirrors
    forall a. UninterpretedSignatures a -> a
    uninterpretedSigned

        -- Once we add support for partial mirrors, we wil need an additional
        -- argument to 'repWithMirror' (here, not in the Repository API itself)
        -- that tells us which files we will be requested from the mirror.
        -- We can then compare that against the specification of the partial mirror
        -- to see if all of those files are available from this mirror.
        canUseMirror :: MirrorContent -> Bool
        canUseMirror :: MirrorContent -> Bool
    canUseMirror MirrorContent
    MirrorFull = Bool
    True

    {-------------------------------------------------------------------------------
      Exceptions
    -------------------------------------------------------------------------------}

    -- | Re-throw all exceptions thrown by the client API as unchecked exceptions
    uncheckClientErrors :: ( ( Throws VerificationError
                             , Throws SomeRemoteError
                             , Throws InvalidPackageException
                             ) => IO a )
                         -> IO a
    uncheckClientErrors :: forall a.
    ((Throws VerificationError, Throws SomeRemoteError,
      Throws InvalidPackageException) =>
     IO a)
    -> IO a
    uncheckClientErrors (Throws VerificationError, Throws SomeRemoteError,
     Throws InvalidPackageException) =>
    IO a
    act = (VerificationError -> IO a)
    -> (Throws VerificationError => IO a) -> IO a
    forall e a.
    Exception e =>
    (e -> IO a) -> (Throws e => IO a) -> IO a
    handleChecked VerificationError -> IO a
    forall a. VerificationError -> IO a
    rethrowVerificationError
                            ((Throws VerificationError => IO a) -> IO a)
    -> (Throws VerificationError => IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ (SomeRemoteError -> IO a)
    -> (Throws SomeRemoteError => IO a) -> IO a
    forall e a.
    Exception e =>
    (e -> IO a) -> (Throws e => IO a) -> IO a
    handleChecked SomeRemoteError -> IO a
    forall a. SomeRemoteError -> IO a
    rethrowSomeRemoteError
                            ((Throws SomeRemoteError => IO a) -> IO a)
    -> (Throws SomeRemoteError => IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ (InvalidPackageException -> IO a)
    -> (Throws InvalidPackageException => IO a) -> IO a
    forall e a.
    Exception e =>
    (e -> IO a) -> (Throws e => IO a) -> IO a
    handleChecked InvalidPackageException -> IO a
    forall a. InvalidPackageException -> IO a
    rethrowInvalidPackageException
                            ((Throws InvalidPackageException => IO a) -> IO a)
    -> (Throws InvalidPackageException => IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ IO a
    (Throws VerificationError, Throws SomeRemoteError,
     Throws InvalidPackageException) =>
    IO a
    Throws InvalidPackageException => IO a
    act
      where
         rethrowVerificationError :: VerificationError -> IO a
         rethrowVerificationError :: forall a. VerificationError -> IO a
    rethrowVerificationError = VerificationError -> IO a
    forall e a. (HasCallStack, Exception e) => e -> IO a
    throwIO

         rethrowSomeRemoteError :: SomeRemoteError -> IO a
         rethrowSomeRemoteError :: forall a. SomeRemoteError -> IO a
    rethrowSomeRemoteError = SomeRemoteError -> IO a
    forall e a. (HasCallStack, Exception e) => e -> IO a
    throwIO

         rethrowInvalidPackageException :: InvalidPackageException -> IO a
         rethrowInvalidPackageException :: forall a. InvalidPackageException -> IO a
    rethrowInvalidPackageException = InvalidPackageException -> IO a
    forall e a. (HasCallStack, Exception e) => e -> IO a
    throwIO

    data InvalidPackageException = InvalidPackageException PackageIdentifier

    data LocalFileCorrupted = LocalFileCorrupted DeserializationError

    data InvalidFileInIndex = forall dec. InvalidFileInIndex {
        ()
    invalidFileInIndex      :: IndexFile dec
      , InvalidFileInIndex -> ByteString
    invalidFileInIndexRaw   :: BS.L.ByteString
      , InvalidFileInIndex -> DeserializationError
    invalidFileInIndexError :: DeserializationError
      }

    deriving instance Show InvalidPackageException
    deriving instance Show LocalFileCorrupted
    deriving instance Show InvalidFileInIndex
    instance Exception InvalidPackageException where displayException :: InvalidPackageException -> String
    displayException = InvalidPackageException -> String
    forall a. Pretty a => a -> String
    pretty
    instance Exception LocalFileCorrupted where displayException :: LocalFileCorrupted -> String
    displayException = LocalFileCorrupted -> String
    forall a. Pretty a => a -> String
    pretty
    instance Exception InvalidFileInIndex where displayException :: InvalidFileInIndex -> String
    displayException = InvalidFileInIndex -> String
    forall a. Pretty a => a -> String
    pretty

    instance Pretty InvalidPackageException where
      pretty :: InvalidPackageException -> String
    pretty (InvalidPackageException PackageIdentifier
    pkgId) = String
    "Invalid package " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ PackageIdentifier -> String
    forall a. Pretty a => a -> String
    display PackageIdentifier
    pkgId

    instance Pretty LocalFileCorrupted where
      pretty :: LocalFileCorrupted -> String
    pretty (LocalFileCorrupted DeserializationError
    err) = String
    "Local file corrupted: " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ DeserializationError -> String
    forall a. Pretty a => a -> String
    pretty DeserializationError
    err

    instance Pretty InvalidFileInIndex where
      pretty :: InvalidFileInIndex -> String
    pretty (InvalidFileInIndex IndexFile dec
    file ByteString
    raw DeserializationError
    err) = [String] -> String
    unlines [
          String
    "Invalid file in index: "  String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ IndexFile dec -> String
    forall a. Pretty a => a -> String
    pretty IndexFile dec
    file
        , String
    "Error: " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ DeserializationError -> String
    forall a. Pretty a => a -> String
    pretty DeserializationError
    err
        , String
    "Unparsed file: " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ByteString -> String
    BS.L.C8.unpack ByteString
    raw
        ]

    {-------------------------------------------------------------------------------
      Auxiliary
    -------------------------------------------------------------------------------}

    -- | Local files are assumed trusted
    --
    -- There is no point tracking chain of trust for local files because that chain
    -- would necessarily have to start at an implicitly trusted (though unverified)
    -- file: the root metadata.
    trustLocalFile :: Signed a -> Trusted a
    trustLocalFile :: forall a. Signed a -> Trusted a
    trustLocalFile Signed{a
    Signatures
    signed :: forall a. Signed a -> a
    signed :: a
    signatures :: Signatures
    signatures :: forall a. Signed a -> Signatures
    ..} = a -> Trusted a
    forall a. a -> Trusted a
    DeclareTrusted a
    signed

    -- | Just a simple wrapper around 'verifyFileInfo'
    --
    -- Throws a VerificationError if verification failed.
    verifyFileInfo' :: (MonadIO m, DownloadedFile down)
                    => Maybe (Trusted FileInfo)
                    -> TargetPath  -- ^ For error messages
                    -> down typ    -- ^ File to verify
                    -> m ()
    verifyFileInfo' :: forall (m :: * -> *) (down :: * -> *) typ.
    (MonadIO m, DownloadedFile down) =>
    Maybe (Trusted FileInfo) -> TargetPath -> down typ -> m ()
    verifyFileInfo' Maybe (Trusted FileInfo)
    Nothing     TargetPath
    _          down typ
    _        = () -> m ()
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
    verifyFileInfo' (Just Trusted FileInfo
    info) TargetPath
    targetPath down typ
    tempPath = IO () -> m ()
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> m ()) -> IO () -> m ()
    forall a b. (a -> b) -> a -> b
    $ do
        verified <- down typ -> Trusted FileInfo -> IO Bool
    forall a. down a -> Trusted FileInfo -> IO Bool
    forall (down :: * -> *) a.
    DownloadedFile down =>
    down a -> Trusted FileInfo -> IO Bool
    downloadedVerify down typ
    tempPath Trusted FileInfo
    info
        unless verified $ throw $ VerificationErrorFileInfo targetPath

    readCachedJSON :: (MonadIO m, FromJSON ReadJSON_Keys_Layout a)
                   => Repository down -> KeyEnv -> Path Absolute
                   -> m (Either DeserializationError a)
    readCachedJSON :: forall (m :: * -> *) a (down :: * -> *).
    (MonadIO m, FromJSON ReadJSON_Keys_Layout a) =>
    Repository down
    -> KeyEnv -> Path Absolute -> m (Either DeserializationError a)
    readCachedJSON Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} KeyEnv
    keyEnv Path Absolute
    fp = IO (Either DeserializationError a)
    -> m (Either DeserializationError a)
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Either DeserializationError a)
     -> m (Either DeserializationError a))
    -> IO (Either DeserializationError a)
    -> m (Either DeserializationError a)
    forall a b. (a -> b) -> a -> b
    $ do
        bs <- Path Absolute -> IO ByteString
    forall root. FsRoot root => Path root -> IO ByteString
    readLazyByteString Path Absolute
    fp
        evaluate $ parseJSON_Keys_Layout keyEnv repLayout bs

    readDownloadedJSON :: (MonadIO m, FromJSON ReadJSON_Keys_Layout a)
                       => Repository down -> KeyEnv -> down Metadata
                       -> m (Either DeserializationError a)
    readDownloadedJSON :: forall (m :: * -> *) a (down :: * -> *).
    (MonadIO m, FromJSON ReadJSON_Keys_Layout a) =>
    Repository down
    -> KeyEnv -> down Metadata -> m (Either DeserializationError a)
    readDownloadedJSON Repository{String
    IO ()
    IO TarIndex
    IO (Path Absolute)
    RepoLayout
    IndexLayout
    IO () -> IO ()
    LogMessage -> IO ()
    CachedFile -> IO (Maybe (Path Absolute))
    forall a. Maybe [Mirror] -> IO a -> IO a
    forall a. (Handle -> IO a) -> IO a
    forall fs typ.
    Throws SomeRemoteError =>
    AttemptNr
    -> RemoteFile fs typ -> Verify (Some (HasFormat fs), down typ)
    repDescription :: forall (down :: * -> *). Repository down -> String
    repIndexLayout :: forall (down :: * -> *). Repository down -> IndexLayout
    repLayout :: forall (down :: * -> *). Repository down -> RepoLayout
    repLog :: forall (down :: * -> *). Repository down -> LogMessage -> IO ()
    repWithMirror :: forall (down :: * -> *).
    Repository down -> forall a. Maybe [Mirror] -> IO a -> IO a
    repLockCache :: forall (down :: * -> *). Repository down -> IO () -> IO ()
    repGetIndexIdx :: forall (down :: * -> *). Repository down -> IO TarIndex
    repWithIndex :: forall (down :: * -> *).
    Repository down -> forall a. (Handle -> IO a) -> IO a
    repClearCache :: forall (down :: * -> *). Repository down -> IO ()
    repGetCachedRoot :: forall (down :: * -> *). Repository down -> IO (Path Absolute)
    repGetCached :: forall (down :: * -> *).
    Repository down -> CachedFile -> IO (Maybe (Path Absolute))
    repGetRemote :: 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)
    repGetCached :: CachedFile -> IO (Maybe (Path Absolute))
    repGetCachedRoot :: IO (Path Absolute)
    repClearCache :: IO ()
    repWithIndex :: forall a. (Handle -> IO a) -> IO a
    repGetIndexIdx :: IO TarIndex
    repLockCache :: IO () -> IO ()
    repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a
    repLog :: LogMessage -> IO ()
    repLayout :: RepoLayout
    repIndexLayout :: IndexLayout
    repDescription :: String
    ..} KeyEnv
    keyEnv down Metadata
    fp = IO (Either DeserializationError a)
    -> m (Either DeserializationError a)
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Either DeserializationError a)
     -> m (Either DeserializationError a))
    -> IO (Either DeserializationError a)
    -> m (Either DeserializationError a)
    forall a b. (a -> b) -> a -> b
    $ do
        bs <- down Metadata -> IO ByteString
    forall (down :: * -> *).
    DownloadedFile down =>
    down Metadata -> IO ByteString
    downloadedRead down Metadata
    fp
        evaluate $ parseJSON_Keys_Layout keyEnv repLayout bs

    throwErrorsUnchecked :: ( MonadIO m
                            , Exception e'
                            )
                         => (e -> e') -> Either e a -> m a
    throwErrorsUnchecked :: forall (m :: * -> *) e' e a.
    (MonadIO m, Exception e') =>
    (e -> e') -> Either e a -> m a
    throwErrorsUnchecked e -> e'
    f (Left e
    err) = IO a -> m a
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO a -> m a) -> IO a -> m a
    forall a b. (a -> b) -> a -> b
    $ e' -> IO a
    forall e a. Exception e => e -> IO a
    throwUnchecked (e -> e'
    f e
    err)
    throwErrorsUnchecked e -> e'
    _ (Right a
    a)  = a -> m a
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    a

    throwErrorsChecked :: ( Throws e'
                          , MonadIO m
                          , Exception e'
                          )
                       => (e -> e') -> Either e a -> m a
    throwErrorsChecked :: forall e' (m :: * -> *) e a.
    (Throws e', MonadIO m, Exception e') =>
    (e -> e') -> Either e a -> m a
    throwErrorsChecked e -> e'
    f (Left e
    err) = IO a -> m a
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO a -> m a) -> IO a -> m a
    forall a b. (a -> b) -> a -> b
    $ e' -> IO a
    forall e a. (Exception e, Throws e) => e -> IO a
    throwChecked (e -> e'
    f e
    err)
    throwErrorsChecked e -> e'
    _ (Right a
    a)  = a -> m a
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    a

    eitherToMaybe :: Either a b -> Maybe b
    eitherToMaybe :: forall a b. Either a b -> Maybe b
    eitherToMaybe (Left  a
    _) = Maybe b
    forall a. Maybe a
    Nothing
    eitherToMaybe (Right b
    b) = b -> Maybe b
    forall a. a -> Maybe a
    Just b
    b