initSecurityFeature :: ServerEnv -> IO (CoreFeature -> IO SecurityFeature)
initSecurityFeature env = do
    securityState <- securityStateComponent env (serverStateDir env)
    return $ \coreFeature -> do

       -- Update the security state whenever the main package index changes
       registerHook (indexUpdatedHook coreFeature) $ \_ ->
         updateIndexFileInfo coreFeature securityState

       -- Add package metadata whenever a package is added/changed
       --
       -- For package changes we just add a new metadata file to the index,
       -- which will override any previous one.
       --
       -- TODO: We cannot deal with deletes (they are a problem elsewhere too)
       --
       -- NOTE: this hook is in general _not atomic_ with the package index update related to it.
       -- It is atomic _only_ in the PackageChangeAdd case. As most other significant cases are no-ops
       -- at the moment for adding index entries, this should be ok. (The exception is updated tarball
       -- but this is only used for the mirror client).
       --
       -- If in the future more stuff is registered here, we may need to change code elsewhere
       -- to ensure that it is added atomically as well...
       registerHook (preIndexUpdateHook coreFeature) $ \chg -> do
         let (ents,msg) = case chg of
                      PackageChangeAdd      pkg -> (indexEntriesFor pkg,"PackageChangeAdd")
                      PackageChangeInfo s _ new -> case s of
                        PackageUpdatedTarball    -> (indexEntriesFor new,"PackageChangeInfo:PackageUpdatedTarball")
                        -- .cabal file is not recorded in the TUF metadata
                        -- (until we have author signing anyway)
                        PackageUpdatedCabalFile  -> ([],"PackageChangeInfo:PackageUpdatedCabalFile")
                        -- the uploader is not included in the TUF metadata
                        PackageUpdatedUploader   -> ([],"PackageChangeInfo:PackageUpdatedUploader")
                        -- upload time is not included in the TUF metadata
                        -- (it is recorded in the MetadataEntry because we use it for
                        -- the tarball construction, but it doesn't affect the contents
                        -- of the TUF metadata)
                        PackageUpdatedUploadTime -> ([],"PackageChangeInfo:PackageUpdatedUploadTime")
                      PackageChangeDelete _     -> ([],"PackageChangeDelete")
                      PackageChangeIndexExtra{} -> ([],"PackageChangeIndexExtra")

         loginfo maxBound (mconcat ["TUF preIndexUpdateHook invoked (", msg, ", n = ", show (length ents), ")"])
         return ents

       return $ securityFeature env securityState
  where
    indexEntriesFor :: PkgInfo -> [TarIndexEntry]
    indexEntriesFor pkgInfo =
      case pkgLatestTarball pkgInfo of
        Nothing -> []
        Just (_tarball, (uploadTime, _uploadUserId), latestRev) ->
          [MetadataEntry (pkgInfoId pkgInfo) latestRev uploadTime]

-- | The main security feature
--
-- Missing resources (for Phase 2 of the security work):
--
-- * Top-level targets.json (currently top-level targets.json is not
--   required because it's hardcoded in the clients)
-- * Other targets.json files for OOT targets
--
-- Note that even once we have author signing, per-package targets.json file
-- do not get their own resource, but are instead recorded in the tarball.
initSecurityFeature :: ServerEnv -> IO (CoreFeature -> IO SecurityFeature)
initSecurityFeature env = do
    securityState <- securityStateComponent env (serverStateDir env)
    return $ \coreFeature -> do

       -- Update the security state whenever the main package index changes
       registerHook (indexUpdatedHook coreFeature) $ \_ ->
         updateIndexFileInfo coreFeature securityState

       -- Add package metadata whenever a package is added/changed
       --
       -- For package changes we just add a new metadata file to the index,
       -- which will override any previous one.
       --
       -- TODO: We cannot deal with deletes (they are a problem elsewhere too)
       --
       -- NOTE: this hook is in general _not atomic_ with the package index update related to it.
       -- It is atomic _only_ in the PackageChangeAdd case. As most other significant cases are no-ops
       -- at the moment for adding index entries, this should be ok. (The exception is updated tarball
       -- but this is only used for the mirror client).
       --
       -- If in the future more stuff is registered here, we may need to change code elsewhere
       -- to ensure that it is added atomically as well...
       registerHook (preIndexUpdateHook coreFeature) $ \chg -> do
         let (ents,msg) = case chg of
                      PackageChangeAdd      pkg -> (indexEntriesFor pkg,"PackageChangeAdd")
                      PackageChangeInfo s _ new -> case s of
                        PackageUpdatedTarball    -> (indexEntriesFor new,"PackageChangeInfo:PackageUpdatedTarball")
                        -- .cabal file is not recorded in the TUF metadata
                        -- (until we have author signing anyway)
                        PackageUpdatedCabalFile  -> ([],"PackageChangeInfo:PackageUpdatedCabalFile")
                        -- the uploader is not included in the TUF metadata
                        PackageUpdatedUploader   -> ([],"PackageChangeInfo:PackageUpdatedUploader")
                        -- upload time is not included in the TUF metadata
                        -- (it is recorded in the MetadataEntry because we use it for
                        -- the tarball construction, but it doesn't affect the contents
                        -- of the TUF metadata)
                        PackageUpdatedUploadTime -> ([],"PackageChangeInfo:PackageUpdatedUploadTime")
                      PackageChangeDelete _     -> ([],"PackageChangeDelete")
                      PackageChangeIndexExtra{} -> ([],"PackageChangeIndexExtra")

         loginfo maxBound (mconcat ["TUF preIndexUpdateHook invoked (", msg, ", n = ", show (length ents), ")"])
         return ents

       return $ securityFeature env securityState
  where
    indexEntriesFor :: PkgInfo -> [TarIndexEntry]
    indexEntriesFor pkgInfo =
      case pkgLatestTarball pkgInfo of
        Nothing -> []
        Just (_tarball, (uploadTime, _uploadUserId), latestRev) ->
          [MetadataEntry (pkgInfoId pkgInfo) latestRev uploadTime]

-- | The main security feature
--
-- Missing resources (for Phase 2 of the security work):
--
-- * Top-level targets.json (currently top-level targets.json is not
--   required because it's hardcoded in the clients)
-- * Other targets.json files for OOT targets
--
-- Note that even once we have author signing, per-package targets.json file
-- do not get their own resource, but are instead recorded in the tarball.
initSecurityFeature env = do
    securityState <- securityStateComponent env (serverStateDir env)
    return $ \coreFeature -> do

       -- Update the security state whenever the main package index changes
       registerHook (indexUpdatedHook coreFeature) $ \_ ->
         updateIndexFileInfo coreFeature securityState

       -- Add package metadata whenever a package is added/changed
       --
       -- For package changes we just add a new metadata file to the index,
       -- which will override any previous one.
       --
       -- TODO: We cannot deal with deletes (they are a problem elsewhere too)
       --
       -- NOTE: this hook is in general _not atomic_ with the package index update related to it.
       -- It is atomic _only_ in the PackageChangeAdd case. As most other significant cases are no-ops
       -- at the moment for adding index entries, this should be ok. (The exception is updated tarball
       -- but this is only used for the mirror client).
       --
       -- If in the future more stuff is registered here, we may need to change code elsewhere
       -- to ensure that it is added atomically as well...
       registerHook (preIndexUpdateHook coreFeature) $ \chg -> do
         let (ents,msg) = case chg of
                      PackageChangeAdd      pkg -> (indexEntriesFor pkg,"PackageChangeAdd")
                      PackageChangeInfo s _ new -> case s of
                        PackageUpdatedTarball    -> (indexEntriesFor new,"PackageChangeInfo:PackageUpdatedTarball")
                        -- .cabal file is not recorded in the TUF metadata
                        -- (until we have author signing anyway)
                        PackageUpdatedCabalFile  -> ([],"PackageChangeInfo:PackageUpdatedCabalFile")
                        -- the uploader is not included in the TUF metadata
                        PackageUpdatedUploader   -> ([],"PackageChangeInfo:PackageUpdatedUploader")
                        -- upload time is not included in the TUF metadata
                        -- (it is recorded in the MetadataEntry because we use it for
                        -- the tarball construction, but it doesn't affect the contents
                        -- of the TUF metadata)
                        PackageUpdatedUploadTime -> ([],"PackageChangeInfo:PackageUpdatedUploadTime")
                      PackageChangeDelete _     -> ([],"PackageChangeDelete")
                      PackageChangeIndexExtra{} -> ([],"PackageChangeIndexExtra")

         loginfo maxBound (mconcat ["TUF preIndexUpdateHook invoked (", msg, ", n = ", show (length ents), ")"])
         return ents

       return $ securityFeature env securityState
  where
    indexEntriesFor :: PkgInfo -> [TarIndexEntry]
    indexEntriesFor pkgInfo =
      case pkgLatestTarball pkgInfo of
        Nothing -> []
        Just (_tarball, (uploadTime, _uploadUserId), latestRev) ->
          [MetadataEntry (pkgInfoId pkgInfo) latestRev uploadTime]

-- | The main security feature
--
-- Missing resources (for Phase 2 of the security work):
--
-- * Top-level targets.json (currently top-level targets.json is not
--   required because it's hardcoded in the clients)
-- * Other targets.json files for OOT targets
--
-- Note that even once we have author signing, per-package targets.json file
-- do not get their own resource, but are instead recorded in the tarball.