2011-03-27 19:56:43 +00:00
|
|
|
{- git-annex remotes
|
|
|
|
-
|
|
|
|
- Copyright 2011 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Remote (
|
2011-03-27 21:12:32 +00:00
|
|
|
Remote,
|
|
|
|
uuid,
|
|
|
|
name,
|
|
|
|
storeKey,
|
|
|
|
retrieveKeyFile,
|
2012-01-20 17:23:11 +00:00
|
|
|
retrieveKeyFileCheap,
|
2011-03-27 21:12:32 +00:00
|
|
|
removeKey,
|
|
|
|
hasKey,
|
|
|
|
hasKeyCheap,
|
2012-02-14 07:49:48 +00:00
|
|
|
whereisKey,
|
2013-10-11 20:03:18 +00:00
|
|
|
remoteFsck,
|
2011-07-05 22:31:46 +00:00
|
|
|
|
2011-03-29 18:55:59 +00:00
|
|
|
remoteTypes,
|
2011-12-30 23:10:54 +00:00
|
|
|
remoteList,
|
2014-01-19 21:35:36 +00:00
|
|
|
gitSyncableRemote,
|
2011-11-18 17:22:48 +00:00
|
|
|
remoteMap,
|
2012-01-07 22:13:12 +00:00
|
|
|
uuidDescriptions,
|
2011-03-27 20:55:43 +00:00
|
|
|
byName,
|
2013-11-07 22:02:00 +00:00
|
|
|
byNameOnly,
|
2013-03-05 19:39:42 +00:00
|
|
|
byNameWithUUID,
|
2012-08-26 18:45:47 +00:00
|
|
|
byCost,
|
2011-07-06 20:06:10 +00:00
|
|
|
prettyPrintUUIDs,
|
2012-07-30 01:54:23 +00:00
|
|
|
prettyListUUIDs,
|
2013-04-03 21:01:40 +00:00
|
|
|
prettyUUID,
|
2013-01-01 17:52:47 +00:00
|
|
|
remoteFromUUID,
|
2011-03-27 19:56:43 +00:00
|
|
|
remotesWithUUID,
|
2011-03-29 03:22:31 +00:00
|
|
|
remotesWithoutUUID,
|
2012-01-10 17:11:16 +00:00
|
|
|
keyLocations,
|
2011-07-06 20:06:10 +00:00
|
|
|
keyPossibilities,
|
|
|
|
keyPossibilitiesTrusted,
|
|
|
|
nameToUUID,
|
2014-03-13 19:35:59 +00:00
|
|
|
nameToUUID',
|
2011-07-05 22:31:46 +00:00
|
|
|
showTriedRemotes,
|
|
|
|
showLocations,
|
2011-10-28 21:26:38 +00:00
|
|
|
forceTrust,
|
2013-10-27 20:42:13 +00:00
|
|
|
logStatus,
|
2014-02-01 14:33:55 +00:00
|
|
|
checkAvailable,
|
|
|
|
isXMPPRemote
|
2011-03-27 19:56:43 +00:00
|
|
|
) where
|
|
|
|
|
2011-03-29 03:22:31 +00:00
|
|
|
import qualified Data.Map as M
|
2011-09-01 20:02:01 +00:00
|
|
|
import Text.JSON
|
|
|
|
import Text.JSON.Generic
|
2013-03-16 21:43:42 +00:00
|
|
|
import Data.Ord
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-10-05 20:02:51 +00:00
|
|
|
import Common.Annex
|
2011-06-02 01:56:04 +00:00
|
|
|
import Types.Remote
|
2011-03-27 19:56:43 +00:00
|
|
|
import qualified Annex
|
2011-10-15 21:47:03 +00:00
|
|
|
import Annex.UUID
|
2011-10-15 20:21:08 +00:00
|
|
|
import Logs.UUID
|
|
|
|
import Logs.Trust
|
2012-12-12 23:20:38 +00:00
|
|
|
import Logs.Location hiding (logStatus)
|
2012-01-10 17:11:16 +00:00
|
|
|
import Remote.List
|
2013-08-22 16:01:53 +00:00
|
|
|
import Config
|
2013-11-07 22:02:00 +00:00
|
|
|
import Git.Types (RemoteName)
|
2014-02-01 14:33:55 +00:00
|
|
|
import qualified Git
|
2011-12-31 05:42:42 +00:00
|
|
|
|
2012-02-14 07:49:48 +00:00
|
|
|
{- Map from UUIDs of Remotes to a calculated value. -}
|
|
|
|
remoteMap :: (Remote -> a) -> Annex (M.Map UUID a)
|
|
|
|
remoteMap c = M.fromList . map (\r -> (uuid r, c r)) .
|
2012-01-08 18:37:57 +00:00
|
|
|
filter (\r -> uuid r /= NoUUID) <$> remoteList
|
2011-11-18 17:22:48 +00:00
|
|
|
|
2012-06-07 15:16:48 +00:00
|
|
|
{- Map of UUIDs of remotes and their descriptions.
|
2012-01-07 22:13:12 +00:00
|
|
|
- The names of Remotes are added to suppliment any description that has
|
2012-06-07 15:16:48 +00:00
|
|
|
- been set for a repository. -}
|
2012-01-07 22:13:12 +00:00
|
|
|
uuidDescriptions :: Annex (M.Map UUID String)
|
2012-02-14 07:49:48 +00:00
|
|
|
uuidDescriptions = M.unionWith addName <$> uuidMap <*> remoteMap name
|
2012-01-07 22:13:12 +00:00
|
|
|
|
2013-09-27 03:28:25 +00:00
|
|
|
addName :: String -> RemoteName -> String
|
2012-01-07 22:13:12 +00:00
|
|
|
addName desc n
|
|
|
|
| desc == n = desc
|
|
|
|
| null desc = n
|
|
|
|
| otherwise = n ++ " (" ++ desc ++ ")"
|
|
|
|
|
2012-01-06 08:02:35 +00:00
|
|
|
{- When a name is specified, looks up the remote matching that name.
|
2013-03-05 19:39:42 +00:00
|
|
|
- (Or it can be a UUID.) -}
|
2013-09-27 03:28:25 +00:00
|
|
|
byName :: Maybe RemoteName -> Annex (Maybe Remote)
|
2012-01-06 08:02:35 +00:00
|
|
|
byName Nothing = return Nothing
|
2012-03-14 21:43:34 +00:00
|
|
|
byName (Just n) = either error Just <$> byName' n
|
2013-03-05 19:39:42 +00:00
|
|
|
|
|
|
|
{- Like byName, but the remote must have a configured UUID. -}
|
2013-09-27 03:28:25 +00:00
|
|
|
byNameWithUUID :: Maybe RemoteName -> Annex (Maybe Remote)
|
2013-08-22 16:01:53 +00:00
|
|
|
byNameWithUUID = checkuuid <=< byName
|
2013-03-05 19:39:42 +00:00
|
|
|
where
|
2013-08-22 16:01:53 +00:00
|
|
|
checkuuid Nothing = return Nothing
|
|
|
|
checkuuid (Just r)
|
2013-11-09 17:37:30 +00:00
|
|
|
| uuid r == NoUUID =
|
2013-08-22 16:01:53 +00:00
|
|
|
if remoteAnnexIgnore (gitconfig r)
|
2013-11-09 17:37:30 +00:00
|
|
|
then error $ noRemoteUUIDMsg r ++
|
|
|
|
" (" ++ show (remoteConfig (repo r) "ignore") ++
|
|
|
|
" is set)"
|
|
|
|
else error $ noRemoteUUIDMsg r
|
2013-08-22 16:01:53 +00:00
|
|
|
| otherwise = return $ Just r
|
2013-03-05 19:39:42 +00:00
|
|
|
|
2013-09-27 03:28:25 +00:00
|
|
|
byName' :: RemoteName -> Annex (Either String Remote)
|
2011-06-14 02:19:44 +00:00
|
|
|
byName' "" = return $ Left "no remote specified"
|
2012-03-14 21:43:34 +00:00
|
|
|
byName' n = handle . filter matching <$> remoteList
|
2012-10-29 01:27:15 +00:00
|
|
|
where
|
|
|
|
handle [] = Left $ "there is no available git remote named \"" ++ n ++ "\""
|
2013-03-05 19:39:42 +00:00
|
|
|
handle (match:_) = Right match
|
2012-10-29 01:27:15 +00:00
|
|
|
matching r = n == name r || toUUID n == uuid r
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2013-11-07 22:02:00 +00:00
|
|
|
{- Only matches remote name, not UUID -}
|
|
|
|
byNameOnly :: RemoteName -> Annex (Maybe Remote)
|
|
|
|
byNameOnly n = headMaybe . filter matching <$> remoteList
|
|
|
|
where
|
|
|
|
matching r = n == name r
|
|
|
|
|
2013-11-09 17:37:30 +00:00
|
|
|
noRemoteUUIDMsg :: Remote -> String
|
|
|
|
noRemoteUUIDMsg r = "cannot determine uuid for " ++ name r
|
|
|
|
|
2011-06-14 02:19:44 +00:00
|
|
|
{- Looks up a remote by name (or by UUID, or even by description),
|
2013-11-09 17:37:30 +00:00
|
|
|
- and returns its UUID. Finds even repositories that are not
|
|
|
|
- configured in .git/config. -}
|
2013-09-27 03:28:25 +00:00
|
|
|
nameToUUID :: RemoteName -> Annex UUID
|
2014-03-13 19:35:59 +00:00
|
|
|
nameToUUID = either error return <=< nameToUUID'
|
|
|
|
|
|
|
|
nameToUUID' :: RemoteName -> Annex (Either String UUID)
|
|
|
|
nameToUUID' "." = Right <$> getUUID -- special case for current repo
|
|
|
|
nameToUUID' "here" = Right <$> getUUID
|
|
|
|
nameToUUID' n = byName' n >>= go
|
2012-10-29 01:27:15 +00:00
|
|
|
where
|
2014-03-13 19:35:59 +00:00
|
|
|
go (Right r) = return $ case uuid r of
|
|
|
|
NoUUID -> Left $ noRemoteUUIDMsg r
|
|
|
|
u -> Right u
|
|
|
|
go (Left e) = do
|
2012-10-29 01:27:15 +00:00
|
|
|
m <- uuidMap
|
2014-03-13 19:35:59 +00:00
|
|
|
return $ case M.keys (M.filter (== n) m) of
|
|
|
|
[u] -> Right u
|
|
|
|
[] -> let u = toUUID n
|
|
|
|
in case M.keys (M.filterWithKey (\k _ -> k == u) m) of
|
|
|
|
[] -> Left e
|
|
|
|
_ -> Right u
|
|
|
|
_us -> Left "Found multiple repositories with that description"
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-09-01 20:02:01 +00:00
|
|
|
{- Pretty-prints a list of UUIDs of remotes, for human display.
|
|
|
|
-
|
|
|
|
- When JSON is enabled, also generates a machine-readable description
|
|
|
|
- of the UUIDs. -}
|
|
|
|
prettyPrintUUIDs :: String -> [UUID] -> Annex String
|
|
|
|
prettyPrintUUIDs desc uuids = do
|
2011-11-08 19:34:10 +00:00
|
|
|
hereu <- getUUID
|
2012-01-07 22:13:12 +00:00
|
|
|
m <- uuidDescriptions
|
2011-11-08 19:34:10 +00:00
|
|
|
maybeShowJSON [(desc, map (jsonify m hereu) uuids)]
|
|
|
|
return $ unwords $ map (\u -> "\t" ++ prettify m hereu u ++ "\n") uuids
|
2012-10-29 01:27:15 +00:00
|
|
|
where
|
|
|
|
finddescription m u = M.findWithDefault "" u m
|
|
|
|
prettify m hereu u
|
|
|
|
| not (null d) = fromUUID u ++ " -- " ++ d
|
|
|
|
| otherwise = fromUUID u
|
|
|
|
where
|
|
|
|
ishere = hereu == u
|
|
|
|
n = finddescription m u
|
|
|
|
d
|
|
|
|
| null n && ishere = "here"
|
|
|
|
| ishere = addName n "here"
|
|
|
|
| otherwise = n
|
|
|
|
jsonify m hereu u = toJSObject
|
|
|
|
[ ("uuid", toJSON $ fromUUID u)
|
|
|
|
, ("description", toJSON $ finddescription m u)
|
|
|
|
, ("here", toJSON $ hereu == u)
|
|
|
|
]
|
2011-07-01 19:24:07 +00:00
|
|
|
|
2012-07-30 02:11:01 +00:00
|
|
|
{- List of remote names and/or descriptions, for human display. -}
|
2012-07-30 01:54:23 +00:00
|
|
|
prettyListUUIDs :: [UUID] -> Annex [String]
|
|
|
|
prettyListUUIDs uuids = do
|
|
|
|
hereu <- getUUID
|
|
|
|
m <- uuidDescriptions
|
2013-04-03 07:52:41 +00:00
|
|
|
return $ map (prettify m hereu) uuids
|
2012-10-29 01:27:15 +00:00
|
|
|
where
|
|
|
|
finddescription m u = M.findWithDefault "" u m
|
|
|
|
prettify m hereu u
|
|
|
|
| u == hereu = addName n "here"
|
|
|
|
| otherwise = n
|
|
|
|
where
|
|
|
|
n = finddescription m u
|
2012-07-30 01:54:23 +00:00
|
|
|
|
2013-04-03 21:01:40 +00:00
|
|
|
{- Nice display of a remote's name and/or description. -}
|
|
|
|
prettyUUID :: UUID -> Annex String
|
|
|
|
prettyUUID u = concat <$> prettyListUUIDs [u]
|
|
|
|
|
2013-09-29 18:51:49 +00:00
|
|
|
{- Gets the remote associated with a UUID. -}
|
2013-01-01 17:52:47 +00:00
|
|
|
remoteFromUUID :: UUID -> Annex (Maybe Remote)
|
|
|
|
remoteFromUUID u = ifM ((==) u <$> getUUID)
|
|
|
|
( return Nothing
|
2014-02-11 05:35:11 +00:00
|
|
|
, maybe tryharder (return . Just) =<< findinmap
|
2012-10-12 16:45:16 +00:00
|
|
|
)
|
2013-09-29 18:51:49 +00:00
|
|
|
where
|
|
|
|
findinmap = M.lookup u <$> remoteMap id
|
|
|
|
{- Re-read remote list in case a new remote has popped up. -}
|
|
|
|
tryharder = do
|
|
|
|
void remoteListRefresh
|
|
|
|
findinmap
|
2012-10-12 16:45:16 +00:00
|
|
|
|
2011-03-27 20:55:43 +00:00
|
|
|
{- Filters a list of remotes to ones that have the listed uuids. -}
|
2011-12-31 08:11:39 +00:00
|
|
|
remotesWithUUID :: [Remote] -> [UUID] -> [Remote]
|
2011-03-27 20:55:43 +00:00
|
|
|
remotesWithUUID rs us = filter (\r -> uuid r `elem` us) rs
|
|
|
|
|
|
|
|
{- Filters a list of remotes to ones that do not have the listed uuids. -}
|
2011-12-31 08:11:39 +00:00
|
|
|
remotesWithoutUUID :: [Remote] -> [UUID] -> [Remote]
|
2011-03-27 20:55:43 +00:00
|
|
|
remotesWithoutUUID rs us = filter (\r -> uuid r `notElem` us) rs
|
|
|
|
|
2012-01-10 17:11:16 +00:00
|
|
|
{- List of repository UUIDs that the location log indicates may have a key.
|
|
|
|
- Dead repositories are excluded. -}
|
|
|
|
keyLocations :: Key -> Annex [UUID]
|
2012-11-11 04:26:29 +00:00
|
|
|
keyLocations key = trustExclude DeadTrusted =<< loggedLocations key
|
2012-01-10 17:11:16 +00:00
|
|
|
|
|
|
|
{- Cost ordered lists of remotes that the location log indicates
|
|
|
|
- may have a key.
|
2011-06-23 17:39:04 +00:00
|
|
|
-}
|
2011-12-31 08:11:39 +00:00
|
|
|
keyPossibilities :: Key -> Annex [Remote]
|
2012-01-10 17:11:16 +00:00
|
|
|
keyPossibilities key = fst <$> keyPossibilities' key []
|
2011-06-23 17:39:04 +00:00
|
|
|
|
2012-01-10 17:11:16 +00:00
|
|
|
{- Cost ordered lists of remotes that the location log indicates
|
|
|
|
- may have a key.
|
2011-06-01 23:10:38 +00:00
|
|
|
-
|
|
|
|
- Also returns a list of UUIDs that are trusted to have the key
|
|
|
|
- (some may not have configured remotes).
|
|
|
|
-}
|
2011-12-31 08:11:39 +00:00
|
|
|
keyPossibilitiesTrusted :: Key -> Annex ([Remote], [UUID])
|
2012-01-10 17:11:16 +00:00
|
|
|
keyPossibilitiesTrusted key = keyPossibilities' key =<< trustGet Trusted
|
2011-06-23 19:30:04 +00:00
|
|
|
|
2012-01-10 17:11:16 +00:00
|
|
|
keyPossibilities' :: Key -> [UUID] -> Annex ([Remote], [UUID])
|
|
|
|
keyPossibilities' key trusted = do
|
2011-10-11 18:43:45 +00:00
|
|
|
u <- getUUID
|
2011-06-01 23:10:38 +00:00
|
|
|
|
2012-01-10 17:11:16 +00:00
|
|
|
-- uuids of all remotes that are recorded to have the key
|
|
|
|
validuuids <- filter (/= u) <$> keyLocations key
|
2011-06-01 23:10:38 +00:00
|
|
|
|
|
|
|
-- note that validuuids is assumed to not have dups
|
2011-09-06 21:19:29 +00:00
|
|
|
let validtrusteduuids = validuuids `intersect` trusted
|
2011-06-01 23:10:38 +00:00
|
|
|
|
|
|
|
-- remotes that match uuids that have the key
|
2013-04-22 18:57:09 +00:00
|
|
|
allremotes <- filter (not . remoteAnnexIgnore . gitconfig)
|
|
|
|
<$> remoteList
|
2011-06-01 23:10:38 +00:00
|
|
|
let validremotes = remotesWithUUID allremotes validuuids
|
|
|
|
|
2013-03-16 21:43:42 +00:00
|
|
|
return (sortBy (comparing cost) validremotes, validtrusteduuids)
|
2011-06-01 23:10:38 +00:00
|
|
|
|
2011-07-05 22:31:46 +00:00
|
|
|
{- Displays known locations of a key. -}
|
2013-01-09 22:53:59 +00:00
|
|
|
showLocations :: Key -> [UUID] -> String -> Annex ()
|
|
|
|
showLocations key exclude nolocmsg = do
|
2011-10-11 18:43:45 +00:00
|
|
|
u <- getUUID
|
2011-07-05 22:31:46 +00:00
|
|
|
uuids <- keyLocations key
|
|
|
|
untrusteduuids <- trustGet UnTrusted
|
|
|
|
let uuidswanted = filteruuids uuids (u:exclude++untrusteduuids)
|
|
|
|
let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted)
|
2011-09-01 20:02:01 +00:00
|
|
|
ppuuidswanted <- Remote.prettyPrintUUIDs "wanted" uuidswanted
|
|
|
|
ppuuidsskipped <- Remote.prettyPrintUUIDs "skipped" uuidsskipped
|
2011-07-05 22:31:46 +00:00
|
|
|
showLongNote $ message ppuuidswanted ppuuidsskipped
|
2013-09-06 20:54:31 +00:00
|
|
|
ignored <- filter (remoteAnnexIgnore . gitconfig) <$> remoteList
|
|
|
|
unless (null ignored) $
|
|
|
|
showLongNote $ "(Note that these git remotes have annex-ignore set: " ++ unwords (map name ignored) ++ ")"
|
2012-10-29 01:27:15 +00:00
|
|
|
where
|
|
|
|
filteruuids l x = filter (`notElem` x) l
|
2013-01-09 22:53:59 +00:00
|
|
|
message [] [] = nolocmsg
|
2012-10-29 01:27:15 +00:00
|
|
|
message rs [] = "Try making some of these repositories available:\n" ++ rs
|
|
|
|
message [] us = "Also these untrusted repositories may contain the file:\n" ++ us
|
|
|
|
message rs us = message rs [] ++ message [] us
|
2011-07-05 22:31:46 +00:00
|
|
|
|
2011-12-31 08:11:39 +00:00
|
|
|
showTriedRemotes :: [Remote] -> Annex ()
|
2012-04-22 03:32:33 +00:00
|
|
|
showTriedRemotes [] = noop
|
2011-07-05 22:31:46 +00:00
|
|
|
showTriedRemotes remotes =
|
|
|
|
showLongNote $ "Unable to access these remotes: " ++
|
2013-04-23 00:24:53 +00:00
|
|
|
intercalate ", " (map name remotes)
|
2011-07-05 22:31:46 +00:00
|
|
|
|
2011-06-02 06:33:31 +00:00
|
|
|
forceTrust :: TrustLevel -> String -> Annex ()
|
2012-01-10 17:11:16 +00:00
|
|
|
forceTrust level remotename = do
|
|
|
|
u <- nameToUUID remotename
|
2011-06-02 06:33:31 +00:00
|
|
|
Annex.changeState $ \s ->
|
2012-01-10 17:11:16 +00:00
|
|
|
s { Annex.forcetrust = M.insert u level (Annex.forcetrust s) }
|
2011-10-28 21:26:38 +00:00
|
|
|
|
|
|
|
{- Used to log a change in a remote's having a key. The change is logged
|
|
|
|
- in the local repo, not on the remote. The process of transferring the
|
|
|
|
- key to the remote, or removing the key from it *may* log the change
|
|
|
|
- on the remote, but this cannot always be relied on. -}
|
2012-01-19 19:24:05 +00:00
|
|
|
logStatus :: Remote -> Key -> LogStatus -> Annex ()
|
2012-02-16 04:41:30 +00:00
|
|
|
logStatus remote key = logChange key (uuid remote)
|
2012-08-26 18:45:47 +00:00
|
|
|
|
|
|
|
{- Orders remotes by cost, with ones with the lowest cost grouped together. -}
|
|
|
|
byCost :: [Remote] -> [[Remote]]
|
2013-03-16 21:43:42 +00:00
|
|
|
byCost = map snd . sortBy (comparing fst) . M.toList . costmap
|
2012-10-29 01:27:15 +00:00
|
|
|
where
|
|
|
|
costmap = M.fromListWith (++) . map costpair
|
|
|
|
costpair r = (cost r, [r])
|
2013-10-27 20:42:13 +00:00
|
|
|
|
|
|
|
checkAvailable :: Bool -> Remote -> IO Bool
|
|
|
|
checkAvailable assumenetworkavailable =
|
|
|
|
maybe (return assumenetworkavailable) doesDirectoryExist . localpath
|
2014-02-01 14:33:55 +00:00
|
|
|
|
|
|
|
{- Remotes using the XMPP transport have urls like xmpp::user@host -}
|
|
|
|
isXMPPRemote :: Remote -> Bool
|
|
|
|
isXMPPRemote remote = Git.repoIsUrl r && "xmpp::" `isPrefixOf` Git.repoLocation r
|
|
|
|
where
|
|
|
|
r = repo remote
|