{- Helpers for thirdPartyPopulated remotes
 -
 - Copyright 2020 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE OverloadedStrings #-}

module Remote.Helper.ThirdPartyPopulated where

import Annex.Common
import Types.Remote
import Types.Import
import Crypto (isEncKey)
import Utility.Metered
import qualified Utility.OsString as OS

-- When a remote is thirdPartyPopulated, the files we want are probably
-- in the .git directory. But, git does not really support .git in paths
-- in a git tree. (Such a tree can be built, but it will lead to problems.)
-- And so anything in .git is prevented from being imported.
-- To work around that, this renames that directory when generating an
-- ImportLocation.
mkThirdPartyImportLocation :: OsPath -> ImportLocation
mkThirdPartyImportLocation =
	mkImportLocation . joinPath . map esc . splitDirectories
  where
	esc x
		| x == literalOsPath ".git" = literalOsPath "dotgit"
		| literalOsPath "dotgit" `OS.isSuffixOf` x = literalOsPath "dot" <> x
		| otherwise = x

fromThirdPartyImportLocation :: ImportLocation -> OsPath
fromThirdPartyImportLocation =
	joinPath . map unesc . splitDirectories . fromImportLocation
  where
	unesc x
		| x == literalOsPath "dotgit" = literalOsPath ".git"
		| literalOsPath "dotgit" `OS.isSuffixOf` x = OS.drop 3 x
		| otherwise = x

-- When a remote is thirdPartyPopulated, and contains a backup of a
-- git-annex repository or some special remotes, this can be used to
-- find only those ImportLocations that are annex object files.
-- All other ImportLocations are ignored.
importKey :: ImportLocation -> ContentIdentifier -> ByteSize -> MeterUpdate -> Annex (Maybe Key)
importKey loc _cid sz _ = return $ importKey' (fromImportLocation loc) (Just sz)

importKey' :: OsPath -> Maybe ByteSize -> Maybe Key
importKey' p msz = case fileKey f of
	Just k
		-- Annex objects always are in a subdirectory with the same
		-- name as the filename. If this is not the case for the file
		-- that was backed up, it is probably not a valid annex object.
		-- Eg, it could be something in annex/bad/, or annex/tmp/.
		-- Or it could be a file that only happens to have a name
		-- like an annex object.
		-- (This does unfortunately prevent recognizing files that are
		-- part of special remotes that don't use that layout. The most
		-- likely special remote to be in a backup, the directory
		-- special remote, does use that layout at least.)
		| lastMaybe (splitDirectories (dropFileName p)) /= Just f -> Nothing
		-- Chunked or encrypted keys used in special remotes are not
		-- supported.
		| isChunkKey k || isEncKey k -> Nothing
		-- Check that the size of the key is the same as the size of the
		-- file stored in the backup. This is a cheap way to make sure it's
		-- probabably the actual content of the file. We don't fully
		-- verify the content here because that could be a very 
		-- expensive operation for a large repository; if the user
		-- wants to detect every possible data corruption problem
		-- (eg, wrong data read off disk during backup, or the object
		-- was corrupt in the git-annex repo and that bad object got
		-- backed up), they can fsck the remote.
		| otherwise -> case (msz, fromKey keySize k) of
			(Just sz, Just sz')
				| sz' == sz -> Just k
				| otherwise -> Nothing
			_ -> Just k
	Nothing -> Nothing
  where
	f = takeFileName p
