git-annex 3.20121112 → 3.20121126
raw patch · 165 files changed
+4002/−971 lines, 165 filesdep +DAVdep +aesondep +http-conduitdep −asyncdep ~mtlbinary-added
Dependencies added: DAV, aeson, http-conduit, xml-conduit
Dependencies removed: async
Dependency ranges changed: mtl
Files
- Assistant/Alert.hs +0/−12
- Assistant/DaemonStatus.hs +4/−1
- Assistant/Drop.hs +10/−9
- Assistant/NetMessager.hs +1/−3
- Assistant/Threads/ConfigMonitor.hs +3/−2
- Assistant/Threads/TransferScanner.hs +1/−1
- Assistant/Threads/TransferWatcher.hs +4/−4
- Assistant/Threads/Transferrer.hs +9/−0
- Assistant/Threads/Watcher.hs +7/−4
- Assistant/Threads/WebApp.hs +3/−4
- Assistant/Types/DaemonStatus.hs +3/−0
- Assistant/WebApp.hs +0/−40
- Assistant/WebApp/Common.hs +17/−0
- Assistant/WebApp/Configurators.hs +45/−22
- Assistant/WebApp/Configurators/AWS.hs +164/−0
- Assistant/WebApp/Configurators/Edit.hs +18/−11
- Assistant/WebApp/Configurators/Local.hs +18/−23
- Assistant/WebApp/Configurators/Pairing.hs +7/−12
- Assistant/WebApp/Configurators/S3.hs +0/−125
- Assistant/WebApp/Configurators/Ssh.hs +5/−20
- Assistant/WebApp/Configurators/WebDAV.hs +114/−0
- Assistant/WebApp/Configurators/XMPP.hs +12/−22
- Assistant/WebApp/DashBoard.hs +6/−11
- Assistant/WebApp/Documentation.hs +3/−9
- Assistant/WebApp/Notifications.hs +20/−2
- Assistant/WebApp/Page.hs +66/−0
- Assistant/WebApp/SideBar.hs +10/−0
- Assistant/WebApp/Types.hs +18/−0
- Assistant/WebApp/routes +7/−0
- Assistant/XMPP/Buddies.hs +4/−0
- Assistant/XMPP/Client.hs +7/−18
- Assistant/XMPP/Git.hs +12/−12
- CHANGELOG +30/−0
- Command/Drop.hs +12/−9
- Command/DropUnused.hs +1/−1
- Command/Find.hs +1/−1
- Command/Fix.hs +1/−1
- Command/SendKey.hs +0/−1
- Command/Unannex.hs +0/−1
- Command/Uninit.hs +3/−3
- Config.hs +2/−0
- Creds.hs +151/−0
- Crypto.hs +24/−30
- GitAnnex.hs +1/−0
- Locations.hs +2/−0
- Makefile +1/−1
- Messages.hs +33/−10
- Meters.hs +40/−0
- Remote/Bup.hs +3/−2
- Remote/Directory.hs +48/−107
- Remote/Glacier.hs +254/−0
- Remote/Helper/AWS.hs +21/−0
- Remote/Helper/Chunked.hs +125/−0
- Remote/Helper/Encryptable.hs +21/−12
- Remote/Hook.hs +4/−2
- Remote/List.hs +8/−0
- Remote/Rsync.hs +4/−2
- Remote/S3.hs +47/−122
- Remote/WebDAV.hs +334/−0
- Seek.hs +9/−3
- Types.hs +3/−1
- Types/Messages.hs +5/−1
- Types/Meters.hs +12/−0
- Types/Remote.hs +3/−5
- Types/StandardGroups.hs +11/−7
- Utility/Gpg.hs +9/−9
- Utility/OSX.hs +1/−0
- Utility/Observed.hs +43/−0
- Utility/Path.hs +14/−18
- Utility/Process.hs +1/−1
- Utility/TempFile.hs +19/−19
- Utility/ThreadScheduler.hs +0/−15
- debian/changelog +30/−0
- debian/control +3/−2
- doc/assistant.mdwn +3/−1
- doc/assistant/archival_walkthrough.mdwn +28/−0
- doc/assistant/release_notes.mdwn +1/−1
- doc/assistant/repogroup.png binary
- doc/bugs/3.20121112:_build_error_in_assistant.mdwn +432/−0
- doc/bugs/3.20121112_build_fails_on_Ubuntu_12.04.mdwn +97/−0
- doc/bugs/3.20121113_build_error___39__not_in_scope_getAddBoxComR__39__.mdwn +33/−0
- doc/bugs/Detection_assumes_that_shell_is_bash.mdwn +2/−0
- doc/bugs/Install_of_git-annex-3.20121112_fails.mdwn +20/−0
- doc/bugs/Is_there_any_way_to_rate_limit_uploads_to_an_S3_backend__63__.mdwn +19/−0
- doc/bugs/another_build_error_in_assistant.mdwn +79/−0
- doc/bugs/com.branchable.git-annex.assistant.plist_is_invalid.mdwn +15/−0
- doc/bugs/dropping_and_re-adding_from_web_remotes_doesn__39__t_work.mdwn +131/−0
- doc/bugs/fat_support/comment_8_acc947643a635eb10a1bff92083a3506._comment +10/−0
- doc/bugs/glacier_with_assistant_bugs.mdwn +13/−0
- doc/bugs/map_not_respecting_annex_ssh_options__63__.mdwn +37/−0
- doc/bugs/migrated_files_not_showing_up_in_unused_list.mdwn +3/−0
- doc/bugs/unlock_fails_silently_with_directory_symlinks.mdwn +53/−0
- doc/design/assistant.mdwn +1/−1
- doc/design/assistant/OSX.mdwn +1/−3
- doc/design/assistant/blog/day_129__release.mdwn +4/−0
- doc/design/assistant/blog/day_130__what_now.mdwn +36/−0
- doc/design/assistant/blog/day_131__webdav_groundwork.mdwn +28/−0
- doc/design/assistant/blog/day_132__webdav_continued.mdwn +22/−0
- doc/design/assistant/blog/day_133__webdav_working.mdwn +31/−0
- doc/design/assistant/blog/day_134__box.com_configurator.mdwn +8/−0
- doc/design/assistant/blog/day_135__progress_revisited.mdwn +37/−0
- doc/design/assistant/blog/day_136__misc.mdwn +14/−0
- doc/design/assistant/blog/day_137__Glacier.mdwn +30/−0
- doc/design/assistant/blog/day_138__back.mdwn +25/−0
- doc/design/assistant/blog/day_139__catch_up.mdwn +11/−0
- doc/design/assistant/blog/day_36__minimal_test_case.mdwn +1/−1
- doc/design/assistant/cloud.mdwn +0/−17
- doc/design/assistant/more_cloud_providers.mdwn +16/−0
- doc/design/assistant/polls/prioritizing_special_remotes.mdwn +1/−1
- doc/design/assistant/progressbars.mdwn +3/−2
- doc/design/assistant/transfer_control.mdwn +3/−0
- doc/design/assistant/xmpp.mdwn +0/−4
- doc/forum/Building_git-annex-3.20121112-19309.mdwn +78/−0
- doc/forum/Default_text__47__html_handler.mdwn +2/−0
- doc/forum/How_to_restore_symlinks.mdwn +1/−0
- doc/forum/Push__47__Pull_with_the_Assistant.mdwn +1/−0
- doc/forum/Setup_of_rsync_special_remote_with_non-standard_ssh_port.mdwn +13/−0
- doc/forum/Special_remote_without_chmod.mdwn +12/−0
- doc/forum/get_and_copy_with_bare_repositories.mdwn +7/−0
- doc/git-annex.mdwn +25/−4
- doc/install/Debian/comment_5_38e6399083e10a6a274f35bddc15d4ac._comment +18/−0
- doc/install/Debian/comment_6_2e7bbdbaabbfb9d89de22e913066e822._comment +8/−0
- doc/install/OSX/comment_10_4d15bfc4fc26e7249953bebfbb09e0aa._comment +11/−0
- doc/install/OSX/comment_9_c6b1b31d16f2144ad08abd8c767b6ab9._comment +23/−0
- doc/install/comment_2_fd560811c57df5cbc3976639642b8b19._comment +8/−0
- doc/install/comment_3_08613b2e2318680508483d204a43da76._comment +75/−0
- doc/install/fromscratch.mdwn +2/−1
- doc/news/version_3.20121009.mdwn +0/−25
- doc/news/version_3.20121126.mdwn +27/−0
- doc/preferred_content.mdwn +15/−8
- doc/special_remotes.mdwn +3/−0
- doc/special_remotes/S3.mdwn +18/−10
- doc/special_remotes/bup.mdwn +5/−3
- doc/special_remotes/directory.mdwn +7/−5
- doc/special_remotes/glacier.mdwn +50/−0
- doc/special_remotes/hook.mdwn +6/−4
- doc/special_remotes/rsync.mdwn +7/−5
- doc/special_remotes/webdav.mdwn +45/−0
- doc/tips/using_Amazon_Glacier.mdwn +75/−0
- doc/tips/using_Amazon_S3.mdwn +1/−1
- doc/tips/using_box.com_as_a_special_remote.mdwn +13/−2
- doc/todo/resuming_encrypted_uploads.mdwn +22/−0
- doc/todo/special_remote_for_amazon_glacier.mdwn +5/−0
- git-annex.1 +22/−4
- git-annex.cabal +16/−8
- templates/bootstrap.hamlet +4/−4
- templates/configurators/addbox.com.hamlet +26/−0
- templates/configurators/adddrive.hamlet +1/−1
- templates/configurators/addglacier.hamlet +40/−0
- templates/configurators/addrsync.net.hamlet +1/−1
- templates/configurators/adds3.hamlet +1/−1
- templates/configurators/editrepository.hamlet +1/−1
- templates/configurators/enableaws.hamlet +30/−0
- templates/configurators/enables3.hamlet +0/−30
- templates/configurators/enablewebdav.hamlet +22/−0
- templates/configurators/needglaciercli.hamlet +10/−0
- templates/configurators/pairing/local/prompt.hamlet +1/−1
- templates/configurators/pairing/xmpp/end.hamlet +12/−27
- templates/configurators/repositories.hamlet +1/−3
- templates/configurators/repositories/cloud.hamlet +9/−7
- templates/configurators/repositories/list.hamlet +36/−0
- templates/configurators/repositories/table.hamlet +0/−25
- templates/configurators/ssh/add.hamlet +1/−1
- templates/configurators/ssh/enable.hamlet +1/−1
- templates/configurators/xmpp.hamlet +4/−4
Assistant/Alert.hs view
@@ -18,7 +18,6 @@ import Data.Text (Text) import qualified Data.Map as M import Data.String-import Yesod {- Different classes of alerts are displayed differently. -} data AlertClass = Success | Message | Activity | Warning | Error@@ -56,17 +55,6 @@ } data AlertIcon = ActivityIcon | SuccessIcon | ErrorIcon | InfoIcon | TheCloud--htmlIcon :: AlertIcon -> GWidget sub master ()-htmlIcon ActivityIcon = bootStrapIcon "refresh"-htmlIcon InfoIcon = bootStrapIcon "info-sign"-htmlIcon SuccessIcon = bootStrapIcon "ok"-htmlIcon ErrorIcon = bootStrapIcon "exclamation-sign"--- utf-8 umbrella (utf-8 cloud looks too stormy)-htmlIcon TheCloud = [whamlet|☂|]--bootStrapIcon :: Text -> GWidget sub master ()-bootStrapIcon name = [whamlet|<i .icon-#{name}></i>|] {- When clicked, a button always redirects to a URL - It may also run an IO action in the background, which is useful
Assistant/DaemonStatus.hs view
@@ -44,6 +44,7 @@ sendNotification $ changeNotifier s return b + {- Returns a function that updates the lists of syncable remotes. -} calcSyncRemotes :: Annex (DaemonStatus -> DaemonStatus) calcSyncRemotes = do@@ -60,7 +61,9 @@ {- Updates the sycRemotes list from the list of all remotes in Annex state. -} updateSyncRemotes :: Assistant ()-updateSyncRemotes = modifyDaemonStatus_ =<< liftAnnex calcSyncRemotes+updateSyncRemotes = do+ modifyDaemonStatus_ =<< liftAnnex calcSyncRemotes+ liftIO . sendNotification =<< syncRemotesNotifier <$> getDaemonStatus {- Load any previous daemon status file, and store it in a MVar for this - process to use as its DaemonStatus. Also gets current transfer status. -}
Assistant/Drop.hs view
@@ -19,18 +19,19 @@ import Config {- Drop from local and/or remote when allowed by the preferred content and- - numcopies settings. -}-handleDrops :: Bool -> Key -> AssociatedFile -> Assistant ()-handleDrops _ _ Nothing = noop-handleDrops fromhere key f = do+ - numcopies settings. If it's known to be present on a particular remote,+ - -}+handleDrops :: Bool -> Key -> AssociatedFile -> Maybe Remote -> Assistant ()+handleDrops _ _ Nothing _ = noop+handleDrops fromhere key f knownpresentremote = do syncrs <- syncDataRemotes <$> getDaemonStatus liftAnnex $ do locs <- loggedLocations key- handleDrops' locs syncrs fromhere key f+ handleDrops' locs syncrs fromhere key f knownpresentremote -handleDrops' :: [UUID] -> [Remote] -> Bool -> Key -> AssociatedFile -> Annex ()-handleDrops' _ _ _ _ Nothing = noop-handleDrops' locs rs fromhere key (Just f)+handleDrops' :: [UUID] -> [Remote] -> Bool -> Key -> AssociatedFile -> Maybe Remote -> Annex ()+handleDrops' _ _ _ _ Nothing _ = noop+handleDrops' locs rs fromhere key (Just f) knownpresentremote | fromhere = do n <- getcopies if checkcopies n@@ -59,7 +60,7 @@ ) dropl n = checkdrop n Nothing $ \numcopies ->- Command.Drop.startLocal f numcopies key+ Command.Drop.startLocal f numcopies key knownpresentremote dropr r n = checkdrop n (Just $ Remote.uuid r) $ \numcopies -> Command.Drop.startRemote f numcopies key r
Assistant/NetMessager.hs view
@@ -9,15 +9,12 @@ import Assistant.Common import Assistant.Types.NetMessager-import qualified Types.Remote as Remote-import qualified Git import Control.Concurrent import Control.Concurrent.STM import Control.Concurrent.MSampleVar import Control.Exception as E import qualified Data.Set as S-import qualified Data.Text as T sendNetMessage :: NetMessage -> Assistant () sendNetMessage m = @@ -95,3 +92,4 @@ waitNetPushMessage :: PushSide -> Assistant (NetMessage) waitNetPushMessage side = (atomically . readTChan) <<~ (getSide side . netMessagesPush . netMessager)+
Assistant/Threads/ConfigMonitor.hs view
@@ -72,8 +72,9 @@ sequence_ as void preferredContentMapLoad {- Changes to the remote log, or the trust log, can affect the- - syncRemotes list -}- when (Logs.Remote.remoteLog `elem` fs || Logs.Trust.trustLog `elem` fs) $+ - syncRemotes list. Changes to the uuid log may affect its+ - display so are also included. -}+ when (any (`elem` fs) [remoteLog, trustLog, uuidLog]) $ updateSyncRemotes where (fs, as) = unzip $ filter (flip S.member changedfiles . fst)
Assistant/Threads/TransferScanner.hs view
@@ -119,7 +119,7 @@ locs <- loggedLocations key present <- inAnnex key - handleDrops' locs syncrs present key (Just f)+ handleDrops' locs syncrs present key (Just f) Nothing let slocs = S.fromList locs let use a = return $ catMaybes $ map (a key slocs) syncrs
Assistant/Threads/TransferWatcher.hs view
@@ -102,11 +102,11 @@ threadDelay 10000000 -- 10 seconds finished t minfo -{- Queue uploads of files we successfully downloaded, spreading them+{- Queue uploads of files downloaded to us, spreading them - out to other reachable remotes. - - Downloading a file may have caused a remote to not want it;- - so drop it from the remote.+ - so check for drops from remotes. - - Uploading a file may cause the local repo, or some other remote to not - want it; handle that too.@@ -115,9 +115,9 @@ finishedTransfer t (Just info) | transferDirection t == Download = whenM (liftAnnex $ inAnnex $ transferKey t) $ do- handleDrops False (transferKey t) (associatedFile info)+ handleDrops False (transferKey t) (associatedFile info) Nothing queueTransfersMatching (/= transferUUID t) Later (transferKey t) (associatedFile info) Upload- | otherwise = handleDrops True (transferKey t) (associatedFile info)+ | otherwise = handleDrops True (transferKey t) (associatedFile info) Nothing finishedTransfer _ _ = noop
Assistant/Threads/Transferrer.hs view
@@ -13,6 +13,7 @@ import Assistant.TransferSlots import Assistant.Alert import Assistant.Commits+import Assistant.Drop import Logs.Transfer import Logs.Location import Annex.Content@@ -65,6 +66,10 @@ - so there's no point in bothering the user about - those. The assistant should recover. -+ - After a successful upload, handle dropping it from+ - here, if desired. In this case, the remote it was+ - uploaded to is known to have it.+ - - Also, after a successful transfer, the location - log has changed. Indicate that a commit has been - made, in order to queue a push of the git-annex@@ -74,6 +79,10 @@ whenM (liftIO $ (==) ExitSuccess <$> waitForProcess pid) $ do void $ addAlert $ makeAlertFiller True $ transferFileAlert direction True file+ unless isdownload $+ handleDrops True (transferKey t)+ (associatedFile info)+ (Just remote) recordCommit where params =
Assistant/Threads/Watcher.hs view
@@ -178,13 +178,16 @@ madeChange file LinkChange {- When a new link appears, or a link is changed, after the startup- - scan, handle getting or dropping the key's content. -}+ - scan, handle getting or dropping the key's content.+ - Also, moving or copying a link may caused it be be transferred+ - elsewhere, so check that too. -} checkcontent key daemonstatus | scanComplete daemonstatus = do present <- liftAnnex $ inAnnex key- unless present $- queueTransfers Next key (Just file) Download- handleDrops present key (Just file)+ if present+ then queueTransfers Next key (Just file) Upload+ else queueTransfers Next key (Just file) Download+ handleDrops present key (Just file) Nothing | otherwise = noop onDel :: Handler
Assistant/Threads/WebApp.hs view
@@ -5,7 +5,7 @@ - Licensed under the GNU GPL version 3 or higher. -} -{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes, CPP #-}+{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Assistant.Threads.WebApp where@@ -21,9 +21,8 @@ import Assistant.WebApp.Configurators.Local import Assistant.WebApp.Configurators.Ssh import Assistant.WebApp.Configurators.Pairing-#ifdef WITH_S3-import Assistant.WebApp.Configurators.S3-#endif+import Assistant.WebApp.Configurators.AWS+import Assistant.WebApp.Configurators.WebDAV import Assistant.WebApp.Configurators.XMPP import Assistant.WebApp.Documentation import Assistant.WebApp.OtherRepos
Assistant/Types/DaemonStatus.hs view
@@ -47,6 +47,8 @@ , transferNotifier :: NotificationBroadcaster -- Broadcasts notifications when there's a change to the alerts , alertNotifier :: NotificationBroadcaster+ -- Broadcasts notifications when the syncRemotes change+ , syncRemotesNotifier :: NotificationBroadcaster } type TransferMap = M.Map Transfer TransferInfo@@ -67,6 +69,7 @@ <*> pure [] <*> pure [] <*> pure Nothing+ <*> newNotificationBroadcaster <*> newNotificationBroadcaster <*> newNotificationBroadcaster <*> newNotificationBroadcaster
Assistant/WebApp.hs view
@@ -16,52 +16,12 @@ import Locations.UserConfig import Yesod-import Text.Hamlet import Data.Text (Text) import Control.Concurrent.STM import Control.Concurrent -data NavBarItem = DashBoard | Config | About- deriving (Eq)--navBarName :: NavBarItem -> Text-navBarName DashBoard = "Dashboard"-navBarName Config = "Configuration"-navBarName About = "About"--navBarRoute :: NavBarItem -> Route WebApp-navBarRoute DashBoard = HomeR-navBarRoute Config = ConfigR-navBarRoute About = AboutR--defaultNavBar :: [NavBarItem]-defaultNavBar = [DashBoard, Config, About]--firstRunNavBar :: [NavBarItem]-firstRunNavBar = [Config, About]--selectNavBar :: Handler [NavBarItem]-selectNavBar = ifM (inFirstRun) (return firstRunNavBar, return defaultNavBar)- inFirstRun :: Handler Bool inFirstRun = isNothing . relDir <$> getYesod--{- Used instead of defaultContent; highlights the current page if it's- - on the navbar. -}-bootstrap :: Maybe NavBarItem -> Widget -> Handler RepHtml-bootstrap navbaritem content = do- webapp <- getYesod- navbar <- map navdetails <$> selectNavBar- page <- widgetToPageContent $ do- addStylesheet $ StaticR css_bootstrap_css- addStylesheet $ StaticR css_bootstrap_responsive_css- addScript $ StaticR jquery_full_js- addScript $ StaticR js_bootstrap_dropdown_js- addScript $ StaticR js_bootstrap_modal_js- $(widgetFile "page")- hamletToRepHtml $(hamletFile $ hamletTemplate "bootstrap")- where- navdetails i = (navBarName i, navBarRoute i, Just i == navbaritem) newWebAppState :: IO (TMVar WebAppState) newWebAppState = do
+ Assistant/WebApp/Common.hs view
@@ -0,0 +1,17 @@+{- git-annex assistant webapp, common imports+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++module Assistant.WebApp.Common (module X) where++import Assistant.Common as X+import Assistant.WebApp as X+import Assistant.WebApp.Page as X+import Assistant.WebApp.Types as X+import Utility.Yesod as X++import Data.Text as X (Text)+
Assistant/WebApp/Configurators.hs view
@@ -9,14 +9,11 @@ module Assistant.WebApp.Configurators where -import Assistant.Common+import Assistant.WebApp.Common import Assistant.DaemonStatus-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar+import Assistant.WebApp.Notifications import Assistant.WebApp.Utility import Assistant.WebApp.Configurators.Local-import Utility.Yesod import qualified Remote import qualified Types.Remote as Remote import Annex.UUID (getUUID)@@ -29,21 +26,18 @@ #endif import Yesod-import Data.Text (Text) import qualified Data.Map as M {- The main configuration screen. -} getConfigR :: Handler RepHtml getConfigR = ifM (inFirstRun) ( getFirstRepositoryR- , bootstrap (Just Config) $ do+ , page "Configuration" (Just Config) $ do #ifdef WITH_XMPP xmppconfigured <- lift $ runAnnex False $ isJust <$> getXMPPCreds #else let xmppconfigured = False #endif- sideBarDisplay- setTitle "Configuration" $(widgetFile "configurators/main") ) @@ -51,7 +45,11 @@ introDisplay :: Text -> Widget introDisplay ident = do webapp <- lift getYesod- repolist <- lift $ repoList False True False+ repolist <- lift $ repoList $ RepoSelector+ { onlyCloud = False+ , onlyConfigured = True+ , includeHere = False+ } let n = length repolist let numrepos = show n $(widgetFile "configurators/intro")@@ -65,10 +63,12 @@ {- Lists known repositories, followed by options to add more. -} getRepositoriesR :: Handler RepHtml-getRepositoriesR = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Repositories"- repolist <- lift $ repoList False False True+getRepositoriesR = page "Repositories" (Just Config) $ do+ let repolist = repoListDisplay $ RepoSelector+ { onlyCloud = False+ , onlyConfigured = False+ , includeHere = True+ } $(widgetFile "configurators/repositories") data Actions@@ -103,16 +103,35 @@ notSyncing (SyncingRepoActions _ _) = False notSyncing _ = True -repoTable :: RepoList -> Widget-repoTable repolist = $(widgetFile "configurators/repositories/table")+{- Called by client to get a list of repos, that refreshes+ - when new repos as added.+ -+ - Returns a div, which will be inserted into the calling page.+ -}+getRepoListR :: RepoListNotificationId -> Handler RepHtml+getRepoListR (RepoListNotificationId nid reposelector) = do+ waitNotifier getRepoListBroadcaster nid+ p <- widgetToPageContent $ repoListDisplay reposelector+ hamletToRepHtml $ [hamlet|^{pageBody p}|] +repoListDisplay :: RepoSelector -> Widget+repoListDisplay reposelector = do+ autoUpdate ident (NotifierRepoListR reposelector) (10 :: Int) (10 :: Int)++ repolist <- lift $ repoList reposelector++ $(widgetFile "configurators/repositories/list")++ where+ ident = "repolist"+ type RepoList = [(String, String, Actions)] {- A numbered list of known repositories, - with actions that can be taken on them. -}-repoList :: Bool -> Bool -> Bool -> Handler RepoList-repoList onlycloud onlyconfigured includehere- | onlyconfigured = list =<< configured+repoList :: RepoSelector -> Handler RepoList+repoList reposelector+ | onlyConfigured reposelector = list =<< configured | otherwise = list =<< (++) <$> configured <*> rest where configured = do@@ -121,7 +140,7 @@ runAnnex [] $ do u <- getUUID let l = map Remote.uuid rs- let l' = if includehere then u : l else l+ let l' = if includeHere reposelector then u : l else l return $ zip l' $ map mkSyncingRepoActions l' rest = runAnnex [] $ do m <- readRemoteLog@@ -134,11 +153,11 @@ return $ zip unsyncable (map mkNotSyncingRepoActions unsyncable) ++ unconfigured wantedrepo r | Remote.readonly r = False- | onlycloud = Git.repoIsUrl (Remote.repo r) && not (isXMPPRemote r)+ | onlyCloud reposelector = Git.repoIsUrl (Remote.repo r) && not (isXMPPRemote r) | otherwise = True wantedremote Nothing = False wantedremote (Just (iscloud, _))- | onlycloud = iscloud+ | onlyCloud reposelector = iscloud | otherwise = True findinfo m u = case M.lookup u m of Nothing -> Nothing@@ -147,6 +166,10 @@ Just "directory" -> val False EnableDirectoryR #ifdef WITH_S3 Just "S3" -> val True EnableS3R+#endif+ Just "glacier" -> val True EnableGlacierR+#ifdef WITH_WEBDAV+ Just "webdav" -> val True EnableWebDAVR #endif _ -> Nothing where
+ Assistant/WebApp/Configurators/AWS.hs view
@@ -0,0 +1,164 @@+{- git-annex assistant webapp configurators for Amazon AWS services+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}++module Assistant.WebApp.Configurators.AWS where++import Assistant.WebApp.Common+import Assistant.MakeRemote+import Assistant.Sync+#ifdef WITH_S3+import qualified Remote.S3 as S3+#endif+import qualified Remote.Glacier as Glacier+import qualified Remote.Helper.AWS as AWS+import Logs.Remote+import qualified Remote+import Types.Remote (RemoteConfig)+import Types.StandardGroups+import Logs.PreferredContent++import Yesod+import qualified Data.Text as T+import qualified Data.Map as M++awsConfigurator :: Widget -> Handler RepHtml+awsConfigurator = page "Add an Amazon repository" (Just Config)++glacierConfigurator :: Widget -> Handler RepHtml+glacierConfigurator a = do+ ifM (liftIO $ inPath "glacier")+ ( awsConfigurator a+ , awsConfigurator needglaciercli+ )+ where+ needglaciercli = $(widgetFile "configurators/needglaciercli")++data StorageClass = StandardRedundancy | ReducedRedundancy+ deriving (Eq, Enum, Bounded)++instance Show StorageClass where+ show StandardRedundancy = "STANDARD" + show ReducedRedundancy = "REDUCED_REDUNDANCY"++data AWSInput = AWSInput+ { accessKeyID :: Text+ , secretAccessKey :: Text+ -- Free form text for datacenter because Amazon adds new ones.+ , datacenter :: Text+ -- Only used for S3, not Glacier.+ , storageClass :: StorageClass+ , repoName :: Text+ }++data AWSCreds = AWSCreds Text Text++extractCreds :: AWSInput -> AWSCreds+extractCreds i = AWSCreds (accessKeyID i) (secretAccessKey i)++s3InputAForm :: AForm WebApp WebApp AWSInput+s3InputAForm = AWSInput+ <$> areq textField "Access Key ID" Nothing+ <*> areq passwordField "Secret Access Key" Nothing+ <*> areq textField "Datacenter" (Just "US")+ <*> areq (selectFieldList storageclasses) "Storage class" (Just StandardRedundancy)+ <*> areq textField "Repository name" (Just "S3")+ where+ storageclasses :: [(Text, StorageClass)]+ storageclasses =+ [ ("Standard redundancy", StandardRedundancy)+ , ("Reduced redundancy (costs less)", ReducedRedundancy)+ ]++glacierInputAForm :: AForm WebApp WebApp AWSInput+glacierInputAForm = AWSInput+ <$> areq textField "Access Key ID" Nothing+ <*> areq passwordField "Secret Access Key" Nothing+ <*> areq textField "Datacenter" (Just "us-east-1")+ <*> pure StandardRedundancy+ <*> areq textField "Repository name" (Just "glacier")++awsCredsAForm :: AForm WebApp WebApp AWSCreds+awsCredsAForm = AWSCreds+ <$> areq textField "Access Key ID" Nothing+ <*> areq passwordField "Secret Access Key" Nothing++getAddS3R :: Handler RepHtml+#ifdef WITH_S3+getAddS3R = awsConfigurator $ do+ ((result, form), enctype) <- lift $+ runFormGet $ renderBootstrap s3InputAForm+ case result of+ FormSuccess input -> lift $ do+ let name = T.unpack $ repoName input+ makeAWSRemote S3.remote (extractCreds input) name setgroup $ M.fromList+ [ ("encryption", "shared")+ , ("type", "S3")+ , ("datacenter", T.unpack $ datacenter input)+ , ("storageclass", show $ storageClass input)+ ]+ _ -> $(widgetFile "configurators/adds3")+ where+ setgroup r = runAnnex () $+ setStandardGroup (Remote.uuid r) TransferGroup+#else+getAddS3R = error "S3 not supported by this build"+#endif++getAddGlacierR :: Handler RepHtml+getAddGlacierR = glacierConfigurator $ do+ ((result, form), enctype) <- lift $+ runFormGet $ renderBootstrap glacierInputAForm+ case result of+ FormSuccess input -> lift $ do+ let name = T.unpack $ repoName input+ makeAWSRemote Glacier.remote (extractCreds input) name setgroup $ M.fromList+ [ ("encryption", "shared")+ , ("type", "glacier")+ , ("datacenter", T.unpack $ datacenter input)+ ]+ _ -> $(widgetFile "configurators/addglacier")+ where+ setgroup r = runAnnex () $+ setStandardGroup (Remote.uuid r) SmallArchiveGroup++getEnableS3R :: UUID -> Handler RepHtml+#ifdef WITH_S3+getEnableS3R = awsConfigurator . enableAWSRemote S3.remote+#else+getEnableS3R _ = error "S3 not supported by this build"+#endif++getEnableGlacierR :: UUID -> Handler RepHtml+getEnableGlacierR = glacierConfigurator . enableAWSRemote Glacier.remote++enableAWSRemote :: RemoteType -> UUID -> Widget+enableAWSRemote remotetype uuid = do+ ((result, form), enctype) <- lift $+ runFormGet $ renderBootstrap awsCredsAForm+ case result of+ FormSuccess creds -> lift $ do+ m <- runAnnex M.empty readRemoteLog+ let name = fromJust $ M.lookup "name" $+ fromJust $ M.lookup uuid m+ makeAWSRemote remotetype creds name (const noop) M.empty+ _ -> do+ description <- lift $ runAnnex "" $+ T.pack . concat <$> Remote.prettyListUUIDs [uuid]+ $(widgetFile "configurators/enableaws")++makeAWSRemote :: RemoteType -> AWSCreds -> String -> (Remote -> Handler ()) -> RemoteConfig -> Handler ()+makeAWSRemote remotetype (AWSCreds ak sk) name setup config = do+ remotename <- runAnnex name $ fromRepo $ uniqueRemoteName name 0+ liftIO $ AWS.setCredsEnv (T.unpack ak, T.unpack sk)+ r <- liftAssistant $ liftAnnex $ addRemote $ do+ makeSpecialRemote name remotetype config+ return remotename+ setup r+ liftAssistant $ syncNewRemote r+ redirect $ EditNewCloudRepositoryR $ Remote.uuid r
Assistant/WebApp/Configurators/Edit.hs view
@@ -9,15 +9,11 @@ module Assistant.WebApp.Configurators.Edit where -import Assistant.Common-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar+import Assistant.WebApp.Common import Assistant.WebApp.Utility import Assistant.DaemonStatus import Assistant.MakeRemote (uniqueRemoteName) import Assistant.WebApp.Configurators.XMPP (xmppNeeded)-import Utility.Yesod import qualified Remote import qualified Remote.List as Remote import Logs.UUID@@ -30,7 +26,6 @@ import qualified Git.Config import Yesod-import Data.Text (Text) import qualified Data.Text as T import qualified Data.Map as M import qualified Data.Set as S@@ -116,21 +111,33 @@ getEditNewCloudRepositoryR uuid = xmppNeeded >> editForm True uuid editForm :: Bool -> UUID -> Handler RepHtml-editForm new uuid = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Configure repository"-+editForm new uuid = page "Configure repository" (Just Config) $ do (repo, mremote) <- lift $ runAnnex undefined $ Remote.repoFromUUID uuid curr <- lift $ runAnnex undefined $ getRepoConfig uuid repo mremote+ lift $ checkarchivedirectory curr ((result, form), enctype) <- lift $ runFormGet $ renderBootstrap $ editRepositoryAForm curr case result of FormSuccess input -> lift $ do+ checkarchivedirectory input setRepoConfig uuid mremote curr input redirect RepositoriesR _ -> showform form enctype curr where showform form enctype curr = do let istransfer = repoGroup curr == RepoGroupStandard TransferGroup- let authtoken = webAppFormAuthToken $(widgetFile "configurators/editrepository")++ {- Makes a toplevel archive directory, so the user can get on with+ - using it. This is done both when displaying the form, as well+ - as after it's posted, because the user may not post the form,+ - but may see that the repo is set up to use the archive+ - directory. -}+ checkarchivedirectory cfg+ | repoGroup cfg == RepoGroupStandard SmallArchiveGroup = go+ | repoGroup cfg == RepoGroupStandard FullArchiveGroup = go+ | otherwise = noop+ where+ go = runAnnex undefined $ inRepo $ \g ->+ createDirectoryIfMissing True $+ Git.repoPath g </> "archive"
Assistant/WebApp/Configurators/Local.hs view
@@ -5,17 +5,13 @@ - Licensed under the GNU AGPL version 3 or higher. -} -{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}+{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-} module Assistant.WebApp.Configurators.Local where -import Assistant.Common-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar+import Assistant.WebApp.Common import Assistant.WebApp.Utility import Assistant.MakeRemote-import Utility.Yesod import Init import qualified Git import qualified Git.Construct@@ -35,7 +31,6 @@ import Utility.UserInfo import Yesod-import Data.Text (Text) import qualified Data.Text as T import Data.Char import System.Posix.Directory@@ -49,7 +44,17 @@ - Validates that the path entered is not empty, and is a safe value - to use as a repository. -} repositoryPathField :: forall sub. Bool -> Field sub WebApp Text-repositoryPathField autofocus = Field { fieldParse = parse, fieldView = view }+repositoryPathField autofocus = Field+#ifdef WITH_OLD_YESOD+ { fieldParse = parse+#else+ { fieldParse = \l _ -> parse l+#endif+ , fieldView = view+#ifndef WITH_OLD_YESOD+ , fieldEnctype = UrlEncoded+#endif+ } where view idAttr nameAttr attrs val isReq = [whamlet|<input type="text" *{attrs} id="#{idAttr}" name="#{nameAttr}" :isReq:required :autofocus:autofocus value="#{either id id val}">|]@@ -123,9 +128,7 @@ {- Making the first repository, when starting the webapp for the first time. -} getFirstRepositoryR :: Handler RepHtml-getFirstRepositoryR = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Getting started" +getFirstRepositoryR = page "Getting started" (Just Config) $ do path <- liftIO . defaultRepositoryPath =<< lift inFirstRun ((res, form), enctype) <- lift $ runFormGet $ newRepositoryForm path case res of@@ -135,9 +138,7 @@ {- Adding a new, separate repository. -} getNewRepositoryR :: Handler RepHtml-getNewRepositoryR = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Add another repository"+getNewRepositoryR = page "Add another repository" (Just Config) $ do home <- liftIO myHomeDir ((res, form), enctype) <- lift $ runFormGet $ newRepositoryForm home case res of@@ -174,9 +175,7 @@ {- Adding a removable drive. -} getAddDriveR :: Handler RepHtml-getAddDriveR = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Add a removable drive"+getAddDriveR = page "AAdd a removable drive" (Just Config) $ do removabledrives <- liftIO $ driveList writabledrives <- liftIO $ filterM (canWrite . T.unpack . mountPoint) removabledrives@@ -185,9 +184,7 @@ case res of FormSuccess (RemovableDrive { mountPoint = d }) -> lift $ make (T.unpack d) >>= redirect . EditNewRepositoryR- _ -> do- let authtoken = webAppFormAuthToken- $(widgetFile "configurators/adddrive")+ _ -> $(widgetFile "configurators/adddrive") where make mountpoint = do liftIO $ makerepo dir@@ -216,9 +213,7 @@ addRemote $ makeGitRemote name dir getEnableDirectoryR :: UUID -> Handler RepHtml-getEnableDirectoryR uuid = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Enable a repository"+getEnableDirectoryR uuid = page "Enable a repository" (Just Config) $ do description <- lift $ runAnnex "" $ T.pack . concat <$> prettyListUUIDs [uuid] $(widgetFile "configurators/enabledirectory")
Assistant/WebApp/Configurators/Pairing.hs view
@@ -11,13 +11,9 @@ module Assistant.WebApp.Configurators.Pairing where import Assistant.Pairing-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar+import Assistant.WebApp.Common import Assistant.Types.Buddies-import Utility.Yesod #ifdef WITH_PAIRING-import Assistant.Common import Assistant.Pairing.Network import Assistant.Pairing.MakeRemote import Assistant.Ssh@@ -42,7 +38,6 @@ import Git import Yesod-import Data.Text (Text) #ifdef WITH_PAIRING import qualified Data.Text as T import qualified Data.Text.Encoding as T@@ -158,7 +153,11 @@ xmppPairEnd :: Bool -> Maybe JID -> Handler RepHtml xmppPairEnd inprogress theirjid = pairPage $ do let friend = buddyName <$> theirjid- cloudrepolist <- lift $ repoList True False False+ let cloudrepolist = repoListDisplay $ RepoSelector+ { onlyCloud = True+ , onlyConfigured = False+ , includeHere = False+ } $(widgetFile "configurators/pairing/xmpp/end") #endif @@ -260,7 +259,6 @@ (verifiableVal . fromPairMsg <$> msg) u <- T.pack <$> liftIO myUserName let sameusername = username == u- let authtoken = webAppFormAuthToken $(widgetFile "configurators/pairing/local/prompt") {- This counts unicode characters as more than one character,@@ -289,10 +287,7 @@ #endif pairPage :: Widget -> Handler RepHtml-pairPage w = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Pairing"- w+pairPage = page "Pairing" (Just Config) noPairing :: Text -> Handler RepHtml noPairing pairingtype = pairPage $
− Assistant/WebApp/Configurators/S3.hs
@@ -1,125 +0,0 @@-{- git-annex assistant webapp configurator for Amazon S3- -- - Copyright 2012 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU AGPL version 3 or higher.- -}--{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}--module Assistant.WebApp.Configurators.S3 where--import Assistant.Common-import Assistant.MakeRemote-import Assistant.Sync-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar-import Utility.Yesod-import qualified Remote.S3 as S3-import Logs.Remote-import qualified Remote-import Types.Remote (RemoteConfig)-import Types.StandardGroups-import Logs.PreferredContent--import Yesod-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Map as M--s3Configurator :: Widget -> Handler RepHtml-s3Configurator a = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Add an Amazon S3 repository"- a--data StorageClass = StandardRedundancy | ReducedRedundancy- deriving (Eq, Enum, Bounded)--instance Show StorageClass where- show StandardRedundancy = "STANDARD" - show ReducedRedundancy = "REDUCED_REDUNDANCY"--data S3Input = S3Input- { accessKeyID :: Text- , secretAccessKey :: Text- -- Free form text for datacenter because Amazon adds new ones.- , datacenter :: Text- , storageClass :: StorageClass- , repoName :: Text- }--data S3Creds = S3Creds Text Text--extractCreds :: S3Input -> S3Creds-extractCreds i = S3Creds (accessKeyID i) (secretAccessKey i)--s3InputAForm :: AForm WebApp WebApp S3Input-s3InputAForm = S3Input- <$> areq textField "Access Key ID" Nothing- <*> areq passwordField "Secret Access Key" Nothing- <*> areq textField "Datacenter" (Just "US")- <*> areq (selectFieldList storageclasses) "Storage class" (Just StandardRedundancy)- <*> areq textField "Repository name" (Just "S3")- where- storageclasses :: [(Text, StorageClass)]- storageclasses =- [ ("Standard redundancy", StandardRedundancy)- , ("Reduced redundancy (costs less)", ReducedRedundancy)- ]--s3CredsAForm :: AForm WebApp WebApp S3Creds-s3CredsAForm = S3Creds- <$> areq textField "Access Key ID" Nothing- <*> areq passwordField "Secret Access Key" Nothing--getAddS3R :: Handler RepHtml-getAddS3R = s3Configurator $ do- ((result, form), enctype) <- lift $- runFormGet $ renderBootstrap s3InputAForm- case result of- FormSuccess s3input -> lift $ do- let name = T.unpack $ repoName s3input- makeS3Remote (extractCreds s3input) name setgroup $ M.fromList- [ ("encryption", "shared")- , ("type", "S3")- , ("datacenter", T.unpack $ datacenter s3input)- , ("storageclass", show $ storageClass s3input)- ]- _ -> showform form enctype- where- showform form enctype = do- let authtoken = webAppFormAuthToken- $(widgetFile "configurators/adds3")- setgroup r = runAnnex () $- setStandardGroup (Remote.uuid r) TransferGroup--getEnableS3R :: UUID -> Handler RepHtml-getEnableS3R uuid = s3Configurator $ do- ((result, form), enctype) <- lift $- runFormGet $ renderBootstrap s3CredsAForm- case result of- FormSuccess s3creds -> lift $ do- m <- runAnnex M.empty readRemoteLog- let name = fromJust $ M.lookup "name" $- fromJust $ M.lookup uuid m- makeS3Remote s3creds name (const noop) M.empty- _ -> showform form enctype- where- showform form enctype = do- let authtoken = webAppFormAuthToken- description <- lift $ runAnnex "" $- T.pack . concat <$> Remote.prettyListUUIDs [uuid]- $(widgetFile "configurators/enables3")--makeS3Remote :: S3Creds -> String -> (Remote -> Handler ()) -> RemoteConfig -> Handler ()-makeS3Remote (S3Creds ak sk) name setup config = do- remotename <- runAnnex name $ fromRepo $ uniqueRemoteName name 0- liftIO $ S3.s3SetCredsEnv ( T.unpack ak, T.unpack sk)- r <- liftAssistant $ liftAnnex $ addRemote $ do- makeSpecialRemote name S3.remote config- return remotename- setup r- liftAssistant $ syncNewRemote r- redirect $ EditNewCloudRepositoryR $ Remote.uuid r
Assistant/WebApp/Configurators/Ssh.hs view
@@ -9,13 +9,9 @@ module Assistant.WebApp.Configurators.Ssh where -import Assistant.Common+import Assistant.WebApp.Common import Assistant.Ssh import Assistant.MakeRemote-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar-import Utility.Yesod import Utility.Rsync (rsyncUrlIsShell) import Logs.Remote import Remote@@ -24,16 +20,12 @@ import Utility.UserInfo import Yesod-import Data.Text (Text) import qualified Data.Text as T import qualified Data.Map as M import Network.Socket sshConfigurator :: Widget -> Handler RepHtml-sshConfigurator a = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Add a remote server"- a+sshConfigurator = page "Add a remote server" (Just Config) data SshInput = SshInput { hostname :: Maybe Text@@ -108,9 +100,7 @@ Right sshdata -> lift $ redirect $ ConfirmSshR sshdata _ -> showform form enctype UntestedServer where- showform form enctype status = do- let authtoken = webAppFormAuthToken- $(widgetFile "configurators/ssh/add")+ showform form enctype status = $(widgetFile "configurators/ssh/add") {- To enable an existing rsync special remote, parse the SshInput from - its rsyncurl, and display a form whose only real purpose is to check@@ -143,7 +133,6 @@ showform form enctype status = do description <- lift $ runAnnex "" $ T.pack . concat <$> prettyListUUIDs [u]- let authtoken = webAppFormAuthToken $(widgetFile "configurators/ssh/enable") enable sshdata = lift $ redirect $ ConfirmSshR $ sshdata { rsyncOnly = True }@@ -245,8 +234,7 @@ $(widgetFile "configurators/ssh/error") getConfirmSshR :: SshData -> Handler RepHtml-getConfirmSshR sshdata = sshConfigurator $ do- let authtoken = webAppFormAuthToken+getConfirmSshR sshdata = sshConfigurator $ $(widgetFile "configurators/ssh/confirm") getMakeSshGitR :: SshData -> Handler RepHtml@@ -291,10 +279,7 @@ ((result, form), enctype) <- runFormGet $ renderBootstrap $ sshInputAForm $ SshInput Nothing Nothing Nothing- let showform status = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Add a Rsync.net repository" - let authtoken = webAppFormAuthToken+ let showform status = page "Add a Rsync.net repository" (Just Config) $ $(widgetFile "configurators/addrsync.net") case result of FormSuccess sshinput
+ Assistant/WebApp/Configurators/WebDAV.hs view
@@ -0,0 +1,114 @@+{- git-annex assistant webapp configurators for WebDAV remotes+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}++module Assistant.WebApp.Configurators.WebDAV where++import Assistant.WebApp.Common+import Assistant.MakeRemote+import Assistant.Sync+#ifdef WITH_WEBDAV+import qualified Remote.WebDAV as WebDAV+#endif+import qualified Remote+import Types.Remote (RemoteConfig)+import Types.StandardGroups+import Logs.PreferredContent+import Logs.Remote++import Yesod+import qualified Data.Text as T+import qualified Data.Map as M++webDAVConfigurator :: Widget -> Handler RepHtml+webDAVConfigurator = page "Add a WebDAV repository" (Just Config)++boxConfigurator :: Widget -> Handler RepHtml+boxConfigurator = page "Add a Box.com repository" (Just Config)++data WebDAVInput = WebDAVInput+ { user :: Text+ , password :: Text+ , directory :: Text+ }++boxComAForm :: AForm WebApp WebApp WebDAVInput+boxComAForm = WebDAVInput+ <$> areq textField "Username or Email" Nothing+ <*> areq passwordField "Box.com Password" Nothing+ <*> areq textField "Directory" (Just "annex")++webDAVCredsAForm :: AForm WebApp WebApp WebDAVInput+webDAVCredsAForm = WebDAVInput+ <$> areq textField "Username or Email" Nothing+ <*> areq passwordField "Password" Nothing+ <*> pure (T.empty)++getAddBoxComR :: Handler RepHtml+#ifdef WITH_WEBDAV+getAddBoxComR = boxConfigurator $ do+ ((result, form), enctype) <- lift $+ runFormGet $ renderBootstrap boxComAForm+ case result of+ FormSuccess input -> lift $ + makeWebDavRemote "box.com" input setgroup $ M.fromList+ [ ("encryption", "shared")+ , ("type", "webdav")+ , ("url", "https://www.box.com/dav/" ++ T.unpack (directory input))+ -- Box.com has a max file size of 100 mb, but+ -- using smaller chunks has better memory+ -- performance.+ , ("chunksize", "10mb")+ ]+ _ -> $(widgetFile "configurators/addbox.com")+ where+ setgroup r = runAnnex () $+ setStandardGroup (Remote.uuid r) TransferGroup+#else+getAddBoxComR = error "WebDAV not supported by this build"+#endif++getEnableWebDAVR :: UUID -> Handler RepHtml+#ifdef WITH_WEBDAV+getEnableWebDAVR uuid = do+ m <- runAnnex M.empty readRemoteLog+ let c = fromJust $ M.lookup uuid m+ let name = fromJust $ M.lookup "name" c+ let url = fromJust $ M.lookup "url" c+ go name url+ where+ go name url+ | "box.com/" `isInfixOf` url = boxConfigurator $ enable name url+ | otherwise = webDAVConfigurator $ enable name url++ enable name url = do+ ((result, form), enctype) <- lift $+ runFormGet $ renderBootstrap webDAVCredsAForm+ case result of+ FormSuccess creds -> lift $+ makeWebDavRemote name creds (const noop) M.empty+ _ -> do+ description <- lift $ runAnnex "" $+ T.pack . concat <$> Remote.prettyListUUIDs [uuid]+ $(widgetFile "configurators/enablewebdav")+#else+getEnableWebDAVR _ = error "WebDAV not supported by this build"+#endif++#ifdef WITH_WEBDAV+makeWebDavRemote :: String -> WebDAVInput -> (Remote -> Handler ()) -> RemoteConfig -> Handler ()+makeWebDavRemote name input setup config = do+ remotename <- runAnnex name $ fromRepo $ uniqueRemoteName name 0+ liftIO $ WebDAV.setCredsEnv (T.unpack $ user input, T.unpack $ password input)+ r <- liftAssistant $ liftAnnex $ addRemote $ do+ makeSpecialRemote name WebDAV.remote config+ return remotename+ setup r+ liftAssistant $ syncNewRemote r+ redirect $ EditNewCloudRepositoryR $ Remote.uuid r+#endif
Assistant/WebApp/Configurators/XMPP.hs view
@@ -10,14 +10,10 @@ module Assistant.WebApp.Configurators.XMPP where -import Assistant.WebApp-import Assistant.WebApp.Types+import Assistant.WebApp.Common import Assistant.WebApp.Notifications-import Assistant.WebApp.SideBar-import Utility.Yesod import Utility.NotificationBroadcaster #ifdef WITH_XMPP-import Assistant.Common import Assistant.XMPP.Client import Assistant.XMPP.Buddies import Assistant.Types.Buddies@@ -31,8 +27,8 @@ #ifdef WITH_XMPP import Network import Network.Protocol.XMPP-import Data.Text (Text) import qualified Data.Text as T+import Control.Exception (SomeException) #endif {- Displays an alert suggesting to configure XMPP, with a button. -}@@ -74,13 +70,11 @@ oldcreds <- runAnnex Nothing getXMPPCreds runFormGet $ renderBootstrap $ xmppAForm $ creds2Form <$> oldcreds- let showform problem = do- let authtoken = webAppFormAuthToken- $(widgetFile "configurators/xmpp")+ let showform problem = $(widgetFile "configurators/xmpp") case result of- FormSuccess f -> maybe (showform True) (lift . storecreds)+ FormSuccess f -> either (showform . Just . show) (lift . storecreds) =<< liftIO (validateForm f)- _ -> showform False+ _ -> showform Nothing where storecreds creds = do void $ runAnnex undefined $ setXMPPCreds creds@@ -96,8 +90,8 @@ getBuddyListR nid = do waitNotifier getBuddyListBroadcaster nid - page <- widgetToPageContent $ buddyListDisplay- hamletToRepHtml $ [hamlet|^{pageBody page}|]+ p <- widgetToPageContent buddyListDisplay+ hamletToRepHtml $ [hamlet|^{pageBody p}|] buddyListDisplay :: Widget buddyListDisplay = do@@ -133,7 +127,7 @@ bad :: Text bad = "This should look like an email address.." -validateForm :: XMPPForm -> IO (Maybe XMPPCreds)+validateForm :: XMPPForm -> IO (Either SomeException XMPPCreds) validateForm f = do let jid = fromMaybe (error "bad JID") $ parseJID (formJID f) let domain = T.unpack $ strDomain $ jidDomain jid@@ -155,15 +149,11 @@ , xmppJID = formJID f } -testXMPP :: XMPPCreds -> IO (Maybe XMPPCreds)-testXMPP creds = either (const $ return Nothing)- (const $ return $ Just creds)- =<< connectXMPP creds (const noop)+testXMPP :: XMPPCreds -> IO (Either SomeException XMPPCreds)+testXMPP creds = either Left (const $ Right creds)+ <$> connectXMPP creds (const noop) #endif xmppPage :: Widget -> Handler RepHtml-xmppPage w = bootstrap (Just Config) $ do- sideBarDisplay- setTitle "Jabber"- w+xmppPage = page "Jabber" (Just Config)
Assistant/WebApp/DashBoard.hs view
@@ -9,16 +9,12 @@ module Assistant.WebApp.DashBoard where -import Assistant.Common-import Assistant.WebApp-import Assistant.WebApp.Types+import Assistant.WebApp.Common import Assistant.WebApp.Utility-import Assistant.WebApp.SideBar import Assistant.WebApp.Notifications import Assistant.WebApp.Configurators import Assistant.TransferQueue import Utility.NotificationBroadcaster-import Utility.Yesod import Logs.Transfer import Utility.Percentage import Utility.DataUnits@@ -73,20 +69,19 @@ getTransfersR nid = do waitNotifier getTransferBroadcaster nid - page <- widgetToPageContent $ transfersDisplay False- hamletToRepHtml $ [hamlet|^{pageBody page}|]+ p <- widgetToPageContent $ transfersDisplay False+ hamletToRepHtml $ [hamlet|^{pageBody p}|] {- The main dashboard. -} dashboard :: Bool -> Widget dashboard warnNoScript = do- sideBarDisplay let content = transfersDisplay warnNoScript $(widgetFile "dashboard/main") getHomeR :: Handler RepHtml getHomeR = ifM (inFirstRun) ( redirect ConfigR- , bootstrap (Just DashBoard) $ dashboard True+ , page "" (Just DashBoard) $ dashboard True ) {- Used to test if the webapp is running. -}@@ -95,11 +90,11 @@ {- Same as HomeR, except no autorefresh at all (and no noscript warning). -} getNoScriptR :: Handler RepHtml-getNoScriptR = bootstrap (Just DashBoard) $ dashboard False+getNoScriptR = page "" (Just DashBoard) $ dashboard False {- Same as HomeR, except with autorefreshing via meta refresh. -} getNoScriptAutoR :: Handler RepHtml-getNoScriptAutoR = bootstrap (Just DashBoard) $ do+getNoScriptAutoR = page "" (Just DashBoard) $ do let ident = NoScriptR let delayseconds = 3 :: Int let this = NoScriptAutoR
Assistant/WebApp/Documentation.hs view
@@ -9,12 +9,8 @@ module Assistant.WebApp.Documentation where -import Assistant.Common-import Assistant.WebApp-import Assistant.WebApp.Types-import Assistant.WebApp.SideBar+import Assistant.WebApp.Common import Assistant.Install (standaloneAppBase)-import Utility.Yesod import Build.SysConfig (packageversion) import Yesod@@ -27,9 +23,7 @@ return $ (</> "LICENSE") <$> base getAboutR :: Handler RepHtml-getAboutR = bootstrap (Just About) $ do- sideBarDisplay- setTitle "About git-annex"+getAboutR = page "About git-annex" (Just About) $ do builtinlicense <- isJust <$> liftIO licenseFile $(widgetFile "documentation/about") @@ -38,7 +32,7 @@ v <- liftIO licenseFile case v of Nothing -> redirect AboutR- Just f -> bootstrap (Just About) $ do+ Just f -> customPage (Just About) $ do -- no sidebar, just pages of legalese.. setTitle "License" license <- liftIO $ readFile f
Assistant/WebApp/Notifications.hs view
@@ -5,7 +5,7 @@ - Licensed under the GNU AGPL version 3 or higher. -} -{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}+{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-} module Assistant.WebApp.Notifications where @@ -20,6 +20,9 @@ import Yesod import Data.Text (Text) import qualified Data.Text as T+#ifndef WITH_OLD_YESOD+import qualified Data.Aeson.Types as Aeson+#endif {- Add to any widget to make it auto-update using long polling. -@@ -32,9 +35,16 @@ - ms_startdelay is how long to delay before updating with AJAX at the start -} autoUpdate :: Text -> Route WebApp -> Int -> Int -> Widget-autoUpdate ident geturl ms_delay ms_startdelay = do+autoUpdate tident geturl ms_delay ms_startdelay = do+#ifdef WITH_OLD_YESOD let delay = show ms_delay let startdelay = show ms_startdelay+ let ident = tident+#else+ let delay = Aeson.String (T.pack (show ms_delay))+ let startdelay = Aeson.String (T.pack (show ms_startdelay))+ let ident = Aeson.String tident+#endif addScript $ StaticR longpolling_js $(widgetFile "notifications/longpolling") @@ -62,6 +72,11 @@ getNotifierBuddyListR :: Handler RepPlain getNotifierBuddyListR = notifierUrl BuddyListR getBuddyListBroadcaster +getNotifierRepoListR :: RepoSelector -> Handler RepPlain+getNotifierRepoListR reposelector = notifierUrl route getRepoListBroadcaster+ where+ route nid = RepoListR $ RepoListNotificationId nid reposelector+ getTransferBroadcaster :: Assistant NotificationBroadcaster getTransferBroadcaster = transferNotifier <$> getDaemonStatus @@ -70,3 +85,6 @@ getBuddyListBroadcaster :: Assistant NotificationBroadcaster getBuddyListBroadcaster = getBuddyBroadcaster <$> getAssistant buddyList++getRepoListBroadcaster :: Assistant NotificationBroadcaster+getRepoListBroadcaster = syncRemotesNotifier <$> getDaemonStatus
+ Assistant/WebApp/Page.hs view
@@ -0,0 +1,66 @@+{- git-annex assistant webapp page display+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}++module Assistant.WebApp.Page where++import Assistant.Common+import Assistant.WebApp+import Assistant.WebApp.Types+import Assistant.WebApp.SideBar+import Utility.Yesod++import Yesod+import Text.Hamlet+import Data.Text (Text)++data NavBarItem = DashBoard | Config | About+ deriving (Eq)++navBarName :: NavBarItem -> Text+navBarName DashBoard = "Dashboard"+navBarName Config = "Configuration"+navBarName About = "About"++navBarRoute :: NavBarItem -> Route WebApp+navBarRoute DashBoard = HomeR+navBarRoute Config = ConfigR+navBarRoute About = AboutR++defaultNavBar :: [NavBarItem]+defaultNavBar = [DashBoard, Config, About]++firstRunNavBar :: [NavBarItem]+firstRunNavBar = [Config, About]++selectNavBar :: Handler [NavBarItem]+selectNavBar = ifM (inFirstRun) (return firstRunNavBar, return defaultNavBar)++{- A standard page of the webapp, with a title, a sidebar, and that may+ - be highlighted on the navbar. -}+page :: Html -> Maybe NavBarItem -> Widget -> Handler RepHtml+page title navbaritem content = customPage navbaritem $ do+ setTitle title+ sideBarDisplay+ content++{- A custom page, with no title or sidebar set. -}+customPage :: Maybe NavBarItem -> Widget -> Handler RepHtml+customPage navbaritem content = do+ webapp <- getYesod+ navbar <- map navdetails <$> selectNavBar+ pageinfo <- widgetToPageContent $ do+ addStylesheet $ StaticR css_bootstrap_css+ addStylesheet $ StaticR css_bootstrap_responsive_css+ addScript $ StaticR jquery_full_js+ addScript $ StaticR js_bootstrap_dropdown_js+ addScript $ StaticR js_bootstrap_modal_js+ $(widgetFile "page")+ hamletToRepHtml $(hamletFile $ hamletTemplate "bootstrap")+ where+ navdetails i = (navBarName i, navBarRoute i, Just i == navbaritem)
Assistant/WebApp/SideBar.hs view
@@ -88,3 +88,13 @@ redirect $ buttonUrl b _ -> redirectBack +htmlIcon :: AlertIcon -> GWidget sub master ()+htmlIcon ActivityIcon = bootstrapIcon "refresh"+htmlIcon InfoIcon = bootstrapIcon "info-sign"+htmlIcon SuccessIcon = bootstrapIcon "ok"+htmlIcon ErrorIcon = bootstrapIcon "exclamation-sign"+-- utf-8 umbrella (utf-8 cloud looks too stormy)+htmlIcon TheCloud = [whamlet|☂|]++bootstrapIcon :: Text -> GWidget sub master ()+bootstrapIcon name = [whamlet|<i .icon-#{name}></i>|]
Assistant/WebApp/Types.hs view
@@ -62,6 +62,16 @@ , otherRepos :: [(String, String)] -- name and path to other repos } +data RepoSelector = RepoSelector+ { onlyCloud :: Bool+ , onlyConfigured :: Bool+ , includeHere :: Bool+ }+ deriving (Read, Show, Eq)++data RepoListNotificationId = RepoListNotificationId NotificationId RepoSelector+ deriving (Read, Show, Eq)+ instance PathPiece SshData where toPathPiece = pack . show fromPathPiece = readish . unpack@@ -95,5 +105,13 @@ fromPathPiece = readish . unpack instance PathPiece PairKey where+ toPathPiece = pack . show+ fromPathPiece = readish . unpack++instance PathPiece RepoListNotificationId where+ toPathPiece = pack . show+ fromPathPiece = readish . unpack++instance PathPiece RepoSelector where toPathPiece = pack . show fromPathPiece = readish . unpack
Assistant/WebApp/routes view
@@ -25,6 +25,8 @@ /config/repository/add/ssh/make/rsync/#SshData MakeSshRsyncR GET /config/repository/add/cloud/rsync.net AddRsyncNetR GET /config/repository/add/cloud/S3 AddS3R GET+/config/repository/add/cloud/glacier AddGlacierR GET+/config/repository/add/cloud/box.com AddBoxComR GET /config/repository/pair/local/start StartLocalPairR GET /config/repository/pair/local/running/#SecretReminder RunningLocalPairR GET@@ -37,6 +39,8 @@ /config/repository/enable/rsync/#UUID EnableRsyncR GET /config/repository/enable/directory/#UUID EnableDirectoryR GET /config/repository/enable/S3/#UUID EnableS3R GET+/config/repository/enable/glacier/#UUID EnableGlacierR GET+/config/repository/enable/webdav/#UUID EnableWebDAVR GET /transfers/#NotificationId TransfersR GET /notifier/transfers NotifierTransfersR GET@@ -46,6 +50,9 @@ /buddylist/#NotificationId BuddyListR GET /notifier/buddylist NotifierBuddyListR GET++/repolist/#RepoListNotificationId RepoListR GET+/notifier/repolist/#RepoSelector NotifierRepoListR GET /alert/close/#AlertId CloseAlert GET /alert/click/#AlertId ClickAlert GET
Assistant/XMPP/Buddies.hs view
@@ -23,6 +23,10 @@ buddyName :: JID -> Text buddyName j = maybe (T.pack "") strNode (jidNode j) +ucFirst :: Text -> Text+ucFirst s = let (first, rest) = T.splitAt 1 s+ in T.concat [T.toUpper first, rest]+ {- Summary of info about a buddy. - - If the buddy has no clients at all anymore, returns Nothing. -}
Assistant/XMPP/Client.hs view
@@ -8,8 +8,8 @@ module Assistant.XMPP.Client where import Assistant.Common-import Utility.FileMode import Utility.SRV+import Creds import Network.Protocol.XMPP import Network@@ -63,23 +63,12 @@ runClientError s j u p x = either (error . show) return =<< runClient s j u p x getXMPPCreds :: Annex (Maybe XMPPCreds)-getXMPPCreds = do- f <- xmppCredsFile- s <- liftIO $ catchMaybeIO $ readFile f- return $ readish =<< s+getXMPPCreds = parse <$> readCacheCreds xmppCredsFile+ where+ parse s = readish =<< s setXMPPCreds :: XMPPCreds -> Annex ()-setXMPPCreds creds = do- f <- xmppCredsFile- liftIO $ do- createDirectoryIfMissing True (parentDir f)- h <- openFile f WriteMode- modifyFileMode f $ removeModes- [groupReadMode, otherReadMode]- hPutStr h (show creds)- hClose h +setXMPPCreds creds = writeCacheCreds (show creds) xmppCredsFile -xmppCredsFile :: Annex FilePath-xmppCredsFile = do- dir <- fromRepo gitAnnexCredsDir- return $ dir </> "xmpp"+xmppCredsFile :: FilePath+xmppCredsFile = "xmpp"
Assistant/XMPP/Git.hs view
@@ -25,7 +25,6 @@ import Locations.UserConfig import qualified Types.Remote as Remote import Utility.FileMode-import Utility.ThreadScheduler import Network.Protocol.XMPP import qualified Data.Text as T@@ -33,6 +32,7 @@ import System.Posix.Types import System.Process (std_in, std_out, std_err) import Control.Concurrent+import System.Timeout import qualified Data.ByteString as B import qualified Data.Map as M @@ -119,16 +119,16 @@ then liftIO $ killThread =<< myThreadId else sendNetMessage $ Pushing cid $ SendPackOutput b fromxmpp outh controlh = forever $ do- m <- runTimeout xmppTimeout <~> waitNetPushMessage SendPack+ m <- timeout xmppTimeout <~> waitNetPushMessage SendPack case m of- (Right (Pushing _ (ReceivePackOutput b))) -> + (Just (Pushing _ (ReceivePackOutput b))) -> liftIO $ writeChunk outh b- (Right (Pushing _ (ReceivePackDone exitcode))) ->+ (Just (Pushing _ (ReceivePackDone exitcode))) -> liftIO $ do hPrint controlh exitcode hFlush controlh- (Right _) -> noop- (Left _) -> do+ (Just _) -> noop+ Nothing -> do debug ["timeout waiting for git receive-pack output via XMPP"] -- Send a synthetic exit code to git-annex -- xmppgit, which will exit and cause git push@@ -220,12 +220,12 @@ sendNetMessage $ Pushing cid $ ReceivePackOutput b relaytoxmpp outh relayfromxmpp inh = forever $ do- m <- runTimeout xmppTimeout <~> waitNetPushMessage ReceivePack+ m <- timeout xmppTimeout <~> waitNetPushMessage ReceivePack case m of- (Right (Pushing _ (SendPackOutput b))) ->+ (Just (Pushing _ (SendPackOutput b))) -> liftIO $ writeChunk inh b- (Right _) -> noop- (Left _) -> do+ (Just _) -> noop+ Nothing -> do debug ["timeout waiting for git send-pack output via XMPP"] -- closing the handle will make -- git receive-pack exit@@ -291,5 +291,5 @@ - delayed for running until the timeout is reached, so it should not be - excessive. -}-xmppTimeout :: Seconds-xmppTimeout = Seconds 120+xmppTimeout :: Int+xmppTimeout = 120000000 -- 120 seconds
CHANGELOG view
@@ -1,3 +1,33 @@+git-annex (3.20121126) unstable; urgency=low++ * New webdav and Amazon glacier special remotes.+ * Display a warning when a non-existing file or directory is specified.+ * webapp: Added configurator for Box.com.+ * webapp: Show error messages to user when testing XMPP creds.+ * Fix build of assistant without yesod.+ * webapp: The list of repositiories refreshes when new repositories are+ added, including when new repository configurations are pushed in from+ remotes.+ * OSX: Fix RunAtLoad value in plist file.+ * Getting a file from chunked directory special remotes no longer buffers+ it all in memory.+ * S3: Added progress display for uploading and downloading.+ * directory special remote: Made more efficient and robust.+ * Bugfix: directory special remote could loop forever storing a key + when a too small chunksize was configured.+ * Allow controlling whether login credentials for S3 and webdav are+ committed to the repository, by setting embedcreds=yes|no when running+ initremote.+ * Added smallarchive repository group, that only archives files that are+ in archive directories. Used by default for glacier when set up in the+ webapp.+ * assistant: Fixed handling of toplevel archive directory and+ client repository group.+ * assistant: Apply preferred content settings when a new symlink+ is created, or a symlink gets renamed. Made archive directories work.++ -- Joey Hess <joeyh@debian.org> Mon, 26 Nov 2012 11:37:49 -0400+ git-annex (3.20121112) unstable; urgency=low * assistant: Can use XMPP to notify other nodes about pushes made to other
Command/Drop.hs view
@@ -34,29 +34,32 @@ start from file (key, _) = autoCopiesWith file key (>) $ \numcopies -> stopUnless (checkAuto $ wantDrop (Remote.uuid <$> from) (Just file)) $ case from of- Nothing -> startLocal file numcopies key+ Nothing -> startLocal file numcopies key Nothing Just remote -> do u <- getUUID if Remote.uuid remote == u- then startLocal file numcopies key+ then startLocal file numcopies key Nothing else startRemote file numcopies key remote -startLocal :: FilePath -> Maybe Int -> Key -> CommandStart-startLocal file numcopies key = stopUnless (inAnnex key) $ do+startLocal :: FilePath -> Maybe Int -> Key -> Maybe Remote -> CommandStart+startLocal file numcopies key knownpresentremote = stopUnless (inAnnex key) $ do showStart "drop" file- next $ performLocal key numcopies+ next $ performLocal key numcopies knownpresentremote startRemote :: FilePath -> Maybe Int -> Key -> Remote -> CommandStart startRemote file numcopies key remote = do showStart ("drop " ++ Remote.name remote) file next $ performRemote key numcopies remote -performLocal :: Key -> Maybe Int -> CommandPerform-performLocal key numcopies = lockContent key $ do+performLocal :: Key -> Maybe Int -> Maybe Remote -> CommandPerform+performLocal key numcopies knownpresentremote = lockContent key $ do (remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key+ let trusteduuids' = case knownpresentremote of+ Nothing -> trusteduuids+ Just r -> nub (Remote.uuid r:trusteduuids) untrusteduuids <- trustGet UnTrusted- let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids++untrusteduuids)- stopUnless (canDropKey key numcopies trusteduuids tocheck []) $ do+ let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids'++untrusteduuids)+ stopUnless (canDropKey key numcopies trusteduuids' tocheck []) $ do whenM (inAnnex key) $ removeAnnex key next $ cleanupLocal key
Command/DropUnused.hs view
@@ -34,7 +34,7 @@ showAction $ "from " ++ Remote.name r ok <- Remote.removeKey r key next $ Command.Drop.cleanupRemote key r ok- droplocal = Command.Drop.performLocal key (Just 0) -- force drop+ droplocal = Command.Drop.performLocal key (Just 0) Nothing -- force drop from = Annex.getField $ Option.name Command.Drop.fromOption performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform
Command/Find.hs view
@@ -20,7 +20,7 @@ import qualified Option def :: [Command]-def = [withOptions [formatOption, print0Option] $+def = [noCommit $ withOptions [formatOption, print0Option] $ command "find" paramPaths seek "lists available files"] formatOption :: Option
Command/Fix.hs view
@@ -13,7 +13,7 @@ import Annex.Content def :: [Command]-def = [command "fix" paramPaths seek+def = [noCommit $ command "fix" paramPaths seek "fix up symlinks to point to annexed content"] seek :: [CommandSeek]
Command/SendKey.hs view
@@ -12,7 +12,6 @@ import Annex.Content import Utility.Rsync import Logs.Transfer-import Types.Remote import qualified Fields def :: [Command]
Command/Unannex.hs view
@@ -41,7 +41,6 @@ -- git as a normal, non-annexed file. (s, clean) <- inRepo $ LsFiles.staged [file] when (not $ null s) $ do- showOutput inRepo $ Git.Command.run "commit" [ Param "-q", Params "-m", Param "content removed from git annex",
Command/Uninit.hs view
@@ -36,9 +36,9 @@ [Params "rev-parse --abbrev-ref HEAD"] seek :: [CommandSeek]-seek = [- withFilesNotInGit $ whenAnnexed startCheckIncomplete,- withFilesInGit $ whenAnnexed startUnannex+seek = + [ withFilesNotInGit $ whenAnnexed startCheckIncomplete+ , withFilesInGit $ whenAnnexed startUnannex , withNothing start ]
Config.hs view
@@ -65,6 +65,8 @@ semiCheapRemoteCost = 110 expensiveRemoteCost :: Int expensiveRemoteCost = 200+veryExpensiveRemoteCost :: Int+veryExpensiveRemoteCost = 1000 {- Adjusts a remote's cost to reflect it being encrypted. -} encryptedRemoteCostAdj :: Int
+ Creds.hs view
@@ -0,0 +1,151 @@+{- Credentials storage+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Creds where++import Common.Annex+import Annex.Perms+import Utility.FileMode+import Crypto+import Types.Remote (RemoteConfig, RemoteConfigKey)+import Remote.Helper.Encryptable (remoteCipher, embedCreds)++import System.Environment+import System.Posix.Env (setEnv)+import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.Map as M+import Utility.Base64++type Creds = String -- can be any data+type CredPair = (String, String) -- login, password++{- A CredPair can be stored in a file, or in the environment, or perhaps+ - in a remote's configuration. -}+data CredPairStorage = CredPairStorage+ { credPairFile :: FilePath+ , credPairEnvironment :: (String, String)+ , credPairRemoteKey :: Maybe RemoteConfigKey+ }++{- Stores creds in a remote's configuration, if the remote allows+ - that. Otherwise, caches them locally. -}+setRemoteCredPair :: RemoteConfig -> CredPairStorage -> Annex RemoteConfig+setRemoteCredPair c storage = go =<< getRemoteCredPair' c storage+ where+ go (Just creds)+ | embedCreds c = case credPairRemoteKey storage of+ Nothing -> localcache creds+ Just key -> storeconfig creds key =<< remoteCipher c+ | otherwise = localcache creds+ go Nothing = return c++ localcache creds = do+ writeCacheCredPair creds storage+ return c++ storeconfig creds key (Just cipher) = do+ s <- liftIO $ encrypt cipher+ (feedBytes $ L.pack $ encodeCredPair creds)+ (readBytes $ return . L.unpack)+ return $ M.insert key (toB64 s) c+ storeconfig creds key Nothing =+ return $ M.insert key (toB64 $ encodeCredPair creds) c++{- Gets a remote's credpair, from the environment if set, otherwise+ - from the cache in gitAnnexCredsDir, or failing that, from the+ - value in RemoteConfig. -}+getRemoteCredPair :: String -> RemoteConfig -> CredPairStorage -> Annex (Maybe CredPair)+getRemoteCredPair this c storage = maybe missing (return . Just) =<< getRemoteCredPair' c storage+ where+ (loginvar, passwordvar) = credPairEnvironment storage+ missing = do+ warning $ unwords+ [ "Set both", loginvar+ , "and", passwordvar+ , "to use", this+ ]+ return Nothing++getRemoteCredPair' :: RemoteConfig -> CredPairStorage -> Annex (Maybe CredPair)+getRemoteCredPair' c storage = maybe fromcache (return . Just) =<< fromenv+ where+ fromenv = liftIO $ getEnvCredPair storage+ fromcache = maybe fromconfig (return . Just) =<< readCacheCredPair storage+ fromconfig = case credPairRemoteKey storage of+ Just key -> do+ mcipher <- remoteCipher c+ case (M.lookup key c, mcipher) of+ (Nothing, _) -> return Nothing+ (Just enccreds, Just cipher) -> do+ creds <- liftIO $ decrypt cipher+ (feedBytes $ L.pack $ fromB64 enccreds)+ (readBytes $ return . L.unpack)+ fromcreds creds+ (Just bcreds, Nothing) ->+ fromcreds $ fromB64 bcreds+ Nothing -> return Nothing+ fromcreds creds = case decodeCredPair creds of+ Just credpair -> do+ writeCacheCredPair credpair storage+ return $ Just credpair+ _ -> do error $ "bad creds"++{- Gets a CredPair from the environment. -}+getEnvCredPair :: CredPairStorage -> IO (Maybe CredPair)+getEnvCredPair storage = liftM2 (,)+ <$> get uenv+ <*> get penv+ where+ (uenv, penv) = credPairEnvironment storage+ get = catchMaybeIO . getEnv++++{- Stores a CredPair in the environment. -}+setEnvCredPair :: CredPair -> CredPairStorage -> IO ()+setEnvCredPair (l, p) storage = do+ set uenv l+ set penv p+ where+ (uenv, penv) = credPairEnvironment storage+ set var val = setEnv var val True++writeCacheCredPair :: CredPair -> CredPairStorage -> Annex ()+writeCacheCredPair credpair storage =+ writeCacheCreds (encodeCredPair credpair) (credPairFile storage)++{- Stores the creds in a file inside gitAnnexCredsDir that only the user+ - can read. -}+writeCacheCreds :: Creds -> FilePath -> Annex ()+writeCacheCreds creds file = do+ d <- fromRepo gitAnnexCredsDir+ createAnnexDirectory d+ liftIO $ do+ let f = d </> file+ h <- openFile f WriteMode+ modifyFileMode f $ removeModes+ [groupReadMode, otherReadMode]+ hPutStr h creds+ hClose h++readCacheCredPair :: CredPairStorage -> Annex (Maybe CredPair)+readCacheCredPair storage = maybe Nothing decodeCredPair+ <$> readCacheCreds (credPairFile storage)++readCacheCreds :: FilePath -> Annex (Maybe Creds)+readCacheCreds file = do+ d <- fromRepo gitAnnexCredsDir+ let f = d </> file+ liftIO $ catchMaybeIO $ readFile f++encodeCredPair :: CredPair -> Creds+encodeCredPair (l, p) = unlines [l, p]++decodeCredPair :: Creds -> Maybe CredPair+decodeCredPair creds = case lines creds of+ l:p:[] -> Just (l, p)+ _ -> Nothing
Crypto.hs view
@@ -18,10 +18,11 @@ describeCipher, decryptCipher, encryptKey,- withEncryptedHandle,- withDecryptedHandle,- withEncryptedContent,- withDecryptedContent,+ feedFile,+ feedBytes,+ readBytes,+ encrypt,+ decrypt, prop_hmacWithCipher_sane ) where@@ -90,10 +91,9 @@ encryptCipher :: Cipher -> KeyIds -> IO StorableCipher encryptCipher (Cipher c) (KeyIds ks) = do let ks' = nub $ sort ks -- gpg complains about duplicate recipient keyids- encipher <- Gpg.pipeStrict (encrypt++recipients ks') c+ encipher <- Gpg.pipeStrict ([ Params "--encrypt" ] ++ recipients ks') c return $ EncryptedCipher encipher (KeyIds ks') where- encrypt = [ Params "--encrypt" ] recipients l = force_recipients : concatMap (\k -> [Param "--recipient", Param k]) l -- Force gpg to only encrypt to the specified@@ -103,9 +103,7 @@ {- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -} decryptCipher :: StorableCipher -> IO Cipher decryptCipher (SharedCipher t) = return $ Cipher t-decryptCipher (EncryptedCipher t _) = Cipher <$> Gpg.pipeStrict decrypt t- where- decrypt = [ Param "--decrypt" ]+decryptCipher (EncryptedCipher t _) = Cipher <$> Gpg.pipeStrict [ Param "--decrypt" ] t {- Generates an encrypted form of a Key. The encryption does not need to be - reversable, nor does it need to be the same type of encryption used@@ -118,31 +116,27 @@ , keyMtime = Nothing -- to avoid leaking data } -{- Runs an action, passing it a handle from which it can - - stream encrypted content. -}-withEncryptedHandle :: Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a-withEncryptedHandle = Gpg.passphraseHandle [Params "--symmetric --force-mdc"] . cipherPassphrase+type Feeder = Handle -> IO ()+type Reader a = Handle -> IO a -{- Runs an action, passing it a handle from which it can- - stream decrypted content. -}-withDecryptedHandle :: Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a-withDecryptedHandle = Gpg.passphraseHandle [Param "--decrypt"] . cipherPassphrase+feedFile :: FilePath -> Feeder+feedFile f h = L.hPut h =<< L.readFile f -{- Streams encrypted content to an action. -}-withEncryptedContent :: Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a-withEncryptedContent = pass withEncryptedHandle+feedBytes :: L.ByteString -> Feeder+feedBytes = flip L.hPut -{- Streams decrypted content to an action. -}-withDecryptedContent :: Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a-withDecryptedContent = pass withDecryptedHandle+readBytes :: (L.ByteString -> IO a) -> Reader a+readBytes a h = L.hGetContents h >>= a -pass- :: (Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a) - -> Cipher- -> IO L.ByteString- -> (L.ByteString -> IO a)- -> IO a-pass to n s a = to n s $ a <=< L.hGetContents+{- Runs a Feeder action, that generates content that is encrypted with the+ - Cipher, and read by the Reader action. -}+encrypt :: Cipher -> Feeder -> Reader a -> IO a+encrypt = Gpg.feedRead [Params "--symmetric --force-mdc"] . cipherPassphrase++{- Runs a Feeder action, that generates content that is decrypted with the+ - Cipher, and read by the Reader action. -}+decrypt :: Cipher -> Feeder -> Reader a -> IO a+decrypt = Gpg.feedRead [Param "--decrypt"] . cipherPassphrase hmacWithCipher :: Cipher -> String -> String hmacWithCipher c = hmacWithCipher' (cipherHmac c)
GitAnnex.hs view
@@ -163,6 +163,7 @@ "skip files smaller than a size" , Option ['T'] ["time-limit"] (ReqArg Limit.addTimeLimit paramTime) "stop after the specified amount of time"+ , Option [] ["trust-glacier"] (NoArg (Annex.setFlag "trustglacier")) "Trust Amazon Glacier inventory" ] ++ Option.matcher where setnumcopies v = Annex.changeState $
Locations.hs view
@@ -9,8 +9,10 @@ keyFile, fileKey, keyPaths,+ keyPath, gitAnnexLocation, annexLocations,+ annexLocation, gitAnnexDir, gitAnnexObjectDir, gitAnnexTmpDir,
Makefile view
@@ -7,7 +7,7 @@ # # If you're using an old version of yesod, enable -DWITH_OLD_YESOD # Or with an old version of the uri library, enable -DWITH_OLD_URI-FEATURES?=$(GIT_ANNEX_LOCAL_FEATURES) -DWITH_ASSISTANT -DWITH_S3 -DWITH_WEBAPP -DWITH_PAIRING -DWITH_XMPP -DWITH_DNS+FEATURES?=$(GIT_ANNEX_LOCAL_FEATURES) -DWITH_ASSISTANT -DWITH_S3 -DWITH_WEBDAV -DWITH_WEBAPP -DWITH_PAIRING -DWITH_XMPP -DWITH_DNS bins=git-annex mans=git-annex.1 git-annex-shell.1
Messages.hs view
@@ -11,6 +11,7 @@ showAction, showProgress, metered,+ meteredBytes, showSideAction, doSideAction, doQuietSideAction,@@ -22,6 +23,7 @@ showEndResult, showErr, warning,+ fileNotFound, indent, maybeShowJSON, showFullJSON,@@ -41,9 +43,9 @@ import Types import Types.Messages import Types.Key-import Types.Remote import qualified Annex import qualified Messages.JSON as JSON+import qualified Data.Set as S showStart :: String -> String -> Annex () showStart command file = handle (JSON.start command $ Just file) $@@ -64,29 +66,38 @@ {- Shows a progress meter while performing a transfer of a key. - The action is passed a callback to use to update the meter. -} metered :: (Maybe MeterUpdate) -> Key -> (MeterUpdate -> Annex a) -> Annex a-metered combinemeterupdate key a = withOutputType $ go (keySize key)+metered combinemeterupdate key a = go (keySize key) where- go (Just size) NormalOutput = do+ go (Just size) = meteredBytes combinemeterupdate size a+ go _ = a (const noop)++{- Shows a progress meter while performing an action on a given number+ - of bytes. -}+meteredBytes :: (Maybe MeterUpdate) -> Integer -> (MeterUpdate -> Annex a) -> Annex a+meteredBytes combinemeterupdate size a = withOutputType go+ where+ go NormalOutput = do progress <- liftIO $ newProgress "" size meter <- liftIO $ newMeter progress "B" 25 (renderNums binaryOpts 1) showOutput- liftIO $ displayMeter stdout meter r <- a $ \n -> liftIO $ do incrP progress n displayMeter stdout meter maybe noop (\m -> m n) combinemeterupdate liftIO $ clearMeter stdout meter return r- go _ _ = a (const noop)+ go _ = a (const noop) showSideAction :: String -> Annex () showSideAction m = Annex.getState Annex.output >>= go where- go (MessageState v StartBlock) = do- p- Annex.changeState $ \s -> s { Annex.output = MessageState v InBlock }- go (MessageState _ InBlock) = return ()- go _ = p+ go st+ | sideActionBlock st == StartBlock = do+ p+ let st' = st { sideActionBlock = InBlock }+ Annex.changeState $ \s -> s { Annex.output = st' }+ | sideActionBlock st == InBlock = return ()+ | otherwise = p p = handle q $ putStrLn $ "(" ++ m ++ "...)" showStoringStateAction :: Annex ()@@ -142,6 +153,18 @@ liftIO $ do hFlush stdout hPutStrLn stderr w++{- Displays a warning one time about a file the user specified not existing. -}+fileNotFound :: FilePath -> Annex ()+fileNotFound file = do+ st <- Annex.getState Annex.output+ let shown = fileNotFoundShown st+ when (S.notMember file shown) $ do+ let shown' = S.insert file shown+ let st' = st { fileNotFoundShown = shown' }+ Annex.changeState $ \s -> s { Annex.output = st' }+ liftIO $ hPutStrLn stderr $ unwords+ [ "git-annex:", file, "not found" ] indent :: String -> String indent = join "\n" . map (\l -> " " ++ l) . lines
+ Meters.hs view
@@ -0,0 +1,40 @@+{- git-annex meters+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Meters where++import Common+import Types.Meters+import Utility.Observed++import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as S++{- Sends the content of a file to an action, updating the meter as it's+ - consumed. -}+withMeteredFile :: FilePath -> MeterUpdate -> (L.ByteString -> IO a) -> IO a+withMeteredFile f meterupdate a = withBinaryFile f ReadMode $ \h ->+ hGetContentsObserved h (meterupdate . toInteger) >>= a++{- Sends the content of a file to a Handle, updating the meter as it's+ - written. -}+streamMeteredFile :: FilePath -> MeterUpdate -> Handle -> IO ()+streamMeteredFile f meterupdate h = withMeteredFile f meterupdate $ L.hPut h++{- Writes a ByteString to a Handle, updating a meter as it's written. -}+meteredWrite :: MeterUpdate -> Handle -> L.ByteString -> IO ()+meteredWrite meterupdate h = go . L.toChunks + where+ go [] = return ()+ go (c:cs) = do+ S.hPut h c+ meterupdate $ toInteger $ S.length c+ go cs++meteredWriteFile :: MeterUpdate -> FilePath -> L.ByteString -> IO ()+meteredWriteFile meterupdate f b = withBinaryFile f WriteMode $ \h ->+ meteredWrite meterupdate h b
Remote/Bup.hs view
@@ -125,7 +125,7 @@ src <- inRepo $ gitAnnexLocation k params <- bupSplitParams r buprepo enck [] liftIO $ catchBoolIO $- withEncryptedHandle cipher (L.readFile src) $ \h ->+ encrypt cipher (feedFile src) $ \h -> pipeBup params (Just h) Nothing retrieve :: BupRepo -> Key -> AssociatedFile -> FilePath -> Annex Bool@@ -141,7 +141,8 @@ retrieveEncrypted :: BupRepo -> (Cipher, Key) -> Key -> FilePath -> Annex Bool retrieveEncrypted buprepo (cipher, enck) _ f = liftIO $ catchBoolIO $ withHandle StdoutHandle createProcessSuccess p $ \h -> do- withDecryptedContent cipher (L.hGetContents h) $ L.writeFile f+ decrypt cipher (\toh -> L.hPut toh =<< L.hGetContents h) $+ readBytes $ L.writeFile f return True where params = bupParams "join" buprepo [Param $ bupRef enck]
Remote/Directory.hs view
@@ -11,6 +11,7 @@ import qualified Data.ByteString as S import qualified Data.Map as M import qualified Control.Exception as E+import Data.Int import Common.Annex import Types.Remote@@ -19,10 +20,10 @@ import Utility.FileMode import Remote.Helper.Special import Remote.Helper.Encryptable+import Remote.Helper.Chunked import Crypto-import Utility.DataUnits-import Data.Int import Annex.Content+import Meters remote :: RemoteType remote = RemoteType {@@ -47,7 +48,7 @@ storeKey = store dir chunksize, retrieveKeyFile = retrieve dir chunksize, retrieveKeyFileCheap = retrieveCheap dir chunksize,- removeKey = remove dir chunksize,+ removeKey = remove dir, hasKey = checkPresent dir chunksize, hasKeyCheap = True, whereisKey = Nothing,@@ -58,19 +59,6 @@ remotetype = remote } -type ChunkSize = Maybe Int64--chunkSize :: Maybe RemoteConfig -> ChunkSize-chunkSize Nothing = Nothing-chunkSize (Just m) =- case M.lookup "chunksize" m of- Nothing -> Nothing- Just v -> case readSize dataUnits v of- Nothing -> error "bad chunksize"- Just size- | size <= 0 -> error "bad chunksize"- | otherwise -> Just $ fromInteger size- directorySetup :: UUID -> RemoteConfig -> Annex RemoteConfig directorySetup u c = do -- verify configuration is sane@@ -85,17 +73,18 @@ gitConfigSpecialRemote u c' "directory" dir return $ M.delete "directory" c' -{- Locations to try to access a given Key in the Directory. -}+{- Locations to try to access a given Key in the Directory.+ - We try more than since we used to write to different hash directories. -} locations :: FilePath -> Key -> [FilePath] locations d k = map (d </>) (keyPaths k) -{- An infinite stream of chunks to use for a given file. -}-chunkStream :: FilePath -> [FilePath]-chunkStream f = map (\n -> f ++ ".chunk" ++ show n) [1 :: Integer ..]+{- Directory where the file(s) for a key are stored. -}+storeDir :: FilePath -> Key -> FilePath+storeDir d k = addTrailingPathSeparator $ d </> hashDirLower k </> keyFile k -{- A file that records the number of chunks used. -}-chunkCount :: FilePath -> FilePath-chunkCount f = f ++ ".chunkcount"+{- Where we store temporary data for a key as it's being uploaded. -}+tmpDir :: FilePath -> Key -> FilePath+tmpDir d k = addTrailingPathSeparator $ d </> "tmp" </> keyFile k withCheckedFiles :: (FilePath -> IO Bool) -> ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool withCheckedFiles _ _ [] _ _ = return False@@ -107,18 +96,14 @@ where go [] = return False go (f:fs) = do- let chunkcount = chunkCount f+ let chunkcount = f ++ chunkCount ifM (check chunkcount) ( do- count <- readcount chunkcount- let chunks = take count $ chunkStream f+ chunks <- listChunks f <$> readFile chunkcount ifM (all id <$> mapM check chunks) ( a chunks , return False ) , go fs )- readcount f = fromMaybe (error $ "cannot parse " ++ f)- . (readish :: String -> Maybe Int)- <$> readFile f withStoredFiles :: ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool withStoredFiles = withCheckedFiles doesFileExist@@ -143,13 +128,13 @@ src <- inRepo $ gitAnnexLocation k metered (Just p) k $ \meterupdate -> storeHelper d chunksize enck $ \dests ->- withEncryptedContent cipher (L.readFile src) $ \s ->+ encrypt cipher (feedFile src) $ readBytes $ \b -> case chunksize of Nothing -> do let dest = Prelude.head dests- meteredWriteFile meterupdate dest s+ meteredWriteFile meterupdate dest b return [dest]- Just _ -> storeSplit meterupdate chunksize dests s+ Just _ -> storeSplit meterupdate chunksize dests b {- Splits a ByteString into chunks and writes to dests, obeying configured - chunk size (not to be confused with the L.ByteString chunk size).@@ -173,95 +158,54 @@ feed _ [] _ = return [] feed sz (l:ls) h = do let s = fromIntegral $ S.length l- if s <= sz+ if s <= sz || sz == chunksize then do S.hPut h l meterupdate $ toInteger s feed (sz - s) ls h else return (l:ls) -{- Write a L.ByteString to a file, updating a progress meter- - after each chunk of the L.ByteString, typically every 64 kb or so. -}-meteredWriteFile :: MeterUpdate -> FilePath -> L.ByteString -> IO ()-meteredWriteFile meterupdate dest b =- meteredWriteFile' meterupdate dest (L.toChunks b) feeder- where- feeder chunks = return ([], chunks)--{- Writes a series of S.ByteString chunks to a file, updating a progress- - meter after each chunk. The feeder is called to get more chunks. -}-meteredWriteFile' :: MeterUpdate -> FilePath -> s -> (s -> IO (s, [S.ByteString])) -> IO ()-meteredWriteFile' meterupdate dest startstate feeder =- E.bracket (openFile dest WriteMode) hClose (feed startstate [])- where- feed state [] h = do- (state', cs) <- feeder state- unless (null cs) $- feed state' cs h- feed state (c:cs) h = do- S.hPut h c- meterupdate $ toInteger $ S.length c- feed state cs h--{- Generates a list of destinations to write to in order to store a key.- - When chunksize is specified, this list will be a list of chunks.- - The action should store the file, and return a list of the destinations- - it stored it to, or [] on error.- - The stored files are only put into their final place once storage is- - complete.- -} storeHelper :: FilePath -> ChunkSize -> Key -> ([FilePath] -> IO [FilePath]) -> Annex Bool-storeHelper d chunksize key a = prep <&&> check <&&> go+storeHelper d chunksize key storer = check <&&> go where- desttemplate = Prelude.head $ locations d key- dir = parentDir desttemplate- tmpdests = case chunksize of- Nothing -> [desttemplate ++ tmpprefix]- Just _ -> map (++ tmpprefix) (chunkStream desttemplate)- tmpprefix = ".tmp"- detmpprefix f = take (length f - tmpprefixlen) f- tmpprefixlen = length tmpprefix- prep = liftIO $ catchBoolIO $ do- createDirectoryIfMissing True dir- allowWrite dir- return True+ tmpdir = tmpDir d key+ destdir = storeDir d key {- The size is not exactly known when encrypting the key; - this assumes that at least the size of the key is - needed as free space. -}- check = checkDiskSpace (Just dir) key 0- go = liftIO $ catchBoolIO $ do- stored <- a tmpdests- forM_ stored $ \f -> do- let dest = detmpprefix f- renameFile f dest- preventWrite dest- when (chunksize /= Nothing) $ do- let chunkcount = chunkCount desttemplate- _ <- tryIO $ allowWrite chunkcount- writeFile chunkcount (show $ length stored)- preventWrite chunkcount- preventWrite dir- return (not $ null stored)+ check = do+ liftIO $ createDirectoryIfMissing True tmpdir+ checkDiskSpace (Just tmpdir) key 0+ go = liftIO $ catchBoolIO $+ storeChunks key tmpdir destdir chunksize storer recorder finalizer+ finalizer tmp dest = do+ void $ tryIO $ allowWrite dest -- may already exist+ void $ tryIO $ removeDirectoryRecursive dest -- or not exist+ createDirectoryIfMissing True (parentDir dest)+ renameDirectory tmp dest+ mapM_ preventWrite =<< dirContents dest+ preventWrite dest+ recorder f s = do+ void $ tryIO $ allowWrite f+ writeFile f s+ preventWrite f retrieve :: FilePath -> ChunkSize -> Key -> AssociatedFile -> FilePath -> Annex Bool retrieve d chunksize k _ f = metered Nothing k $ \meterupdate -> liftIO $ withStoredFiles chunksize d k $ \files -> catchBoolIO $ do- meteredWriteFile' meterupdate f files feeder+ meteredWriteFileChunks meterupdate f files $ L.readFile return True- where- feeder [] = return ([], [])- feeder (x:xs) = do- chunks <- L.toChunks <$> L.readFile x- return (xs, chunks) retrieveEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> FilePath -> Annex Bool retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupdate -> liftIO $ withStoredFiles chunksize d enck $ \files -> catchBoolIO $ do- withDecryptedContent cipher (L.concat <$> mapM L.readFile files) $- meteredWriteFile meterupdate f+ decrypt cipher (feeder files) $+ readBytes $ meteredWriteFile meterupdate f return True+ where+ feeder files h = forM_ files $ \file -> L.hPut h =<< L.readFile file retrieveCheap :: FilePath -> ChunkSize -> Key -> FilePath -> Annex Bool retrieveCheap _ (Just _) _ _ = return False -- no cheap retrieval for chunks@@ -270,16 +214,13 @@ go [file] = catchBoolIO $ createSymbolicLink file f >> return True go _files = return False -remove :: FilePath -> ChunkSize -> Key -> Annex Bool-remove d chunksize k = liftIO $ withStoredFiles chunksize d k go+remove :: FilePath -> Key -> Annex Bool+remove d k = liftIO $ catchBoolIO $ do+ allowWrite dir+ removeDirectoryRecursive dir+ return True where- go = all id <$$> mapM removefile- removefile file = catchBoolIO $ do- let dir = parentDir file- allowWrite dir- removeFile file- _ <- tryIO $ removeDirectory dir- return True+ dir = storeDir d k checkPresent :: FilePath -> ChunkSize -> Key -> Annex (Either String Bool) checkPresent d chunksize k = liftIO $ catchMsgIO $ withStoredFiles chunksize d k $
+ Remote/Glacier.hs view
@@ -0,0 +1,254 @@+{- Amazon Glacier remotes.+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Remote.Glacier (remote) where++import qualified Data.Map as M+import System.Environment++import Common.Annex+import Types.Remote+import Types.Key+import qualified Git+import Config+import Remote.Helper.Special+import Remote.Helper.Encryptable+import qualified Remote.Helper.AWS as AWS+import Crypto+import Creds+import Meters+import qualified Annex++import System.Process++type Vault = String+type Archive = FilePath++remote :: RemoteType+remote = RemoteType {+ typename = "glacier",+ enumerate = findSpecialRemotes "glacier",+ generate = gen,+ setup = glacierSetup+}++gen :: Git.Repo -> UUID -> Maybe RemoteConfig -> Annex Remote+gen r u c = do+ cst <- remoteCost r veryExpensiveRemoteCost+ return $ gen' r u c cst+gen' :: Git.Repo -> UUID -> Maybe RemoteConfig -> Int -> Remote+gen' r u c cst =+ encryptableRemote c+ (storeEncrypted this)+ (retrieveEncrypted this)+ this+ where+ this = Remote {+ uuid = u,+ cost = cst,+ name = Git.repoDescribe r,+ storeKey = store this,+ retrieveKeyFile = retrieve this,+ retrieveKeyFileCheap = retrieveCheap this,+ removeKey = remove this,+ hasKey = checkPresent this,+ hasKeyCheap = False,+ whereisKey = Nothing,+ config = c,+ repo = r,+ localpath = Nothing,+ readonly = False,+ remotetype = remote+ }++glacierSetup :: UUID -> RemoteConfig -> Annex RemoteConfig+glacierSetup u c = do+ c' <- encryptionSetup c+ let fullconfig = c' `M.union` defaults+ genVault fullconfig u+ gitConfigSpecialRemote u fullconfig "glacier" "true"+ setRemoteCredPair fullconfig (AWS.creds u)+ where+ remotename = fromJust (M.lookup "name" c)+ defvault = remotename ++ "-" ++ fromUUID u+ defaults = M.fromList+ [ ("datacenter", "us-east-1")+ , ("vault", defvault)+ ]++store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool+store r k _f m+ | keySize k == Just 0 = do+ warning "Cannot store empty files in Glacier."+ return False+ | otherwise = do+ src <- inRepo $ gitAnnexLocation k+ metered (Just m) k $ \meterupdate ->+ storeHelper r k $ streamMeteredFile src meterupdate++storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool+storeEncrypted r (cipher, enck) k m = do+ f <- inRepo $ gitAnnexLocation k+ metered (Just m) k $ \meterupdate ->+ storeHelper r enck $ \h ->+ encrypt cipher (feedFile f)+ (readBytes $ meteredWrite meterupdate h)++retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool+retrieve r k _f d = metered Nothing k $ \meterupdate ->+ retrieveHelper r k $+ readBytes $ meteredWriteFile meterupdate d++retrieveCheap :: Remote -> Key -> FilePath -> Annex Bool+retrieveCheap _ _ _ = return False++retrieveEncrypted :: Remote -> (Cipher, Key) -> Key -> FilePath -> Annex Bool+retrieveEncrypted r (cipher, enck) k d = metered Nothing k $ \meterupdate ->+ retrieveHelper r enck $ readBytes $ \b ->+ decrypt cipher (feedBytes b) $+ readBytes $ meteredWriteFile meterupdate d++storeHelper :: Remote -> Key -> (Handle -> IO ()) -> Annex Bool+storeHelper r k feeder = go =<< glacierEnv c u+ where+ c = fromJust $ config r+ u = uuid r+ params = glacierParams c+ [ Param "archive"+ , Param "upload"+ , Param "--name", Param $ archive r k+ , Param $ remoteVault r+ , Param "-"+ ]+ go Nothing = return False+ go (Just e) = do+ let p = (proc "glacier" (toCommand params)) { env = Just e }+ liftIO $ catchBoolIO $+ withHandle StdinHandle createProcessSuccess p $ \h -> do+ feeder h+ return True++retrieveHelper :: Remote -> Key -> (Handle -> IO ()) -> Annex Bool+retrieveHelper r k reader = go =<< glacierEnv c u+ where+ c = fromJust $ config r+ u = uuid r+ params = glacierParams c+ [ Param "archive"+ , Param "retrieve"+ , Param "-o-"+ , Param $ remoteVault r+ , Param $ archive r k+ ]+ go Nothing = return False+ go (Just e) = do+ let p = (proc "glacier" (toCommand params)) { env = Just e }+ ok <- liftIO $ catchBoolIO $+ withHandle StdoutHandle createProcessSuccess p $ \h ->+ ifM (hIsEOF h)+ ( return False+ , do+ reader h+ return True+ )+ unless ok later+ return ok+ later = showLongNote "Recommend you wait up to 4 hours, and then run this command again."++remove :: Remote -> Key -> Annex Bool+remove r k = glacierAction r+ [ Param "archive"+ , Param "delete"+ , Param $ remoteVault r+ , Param $ archive r k+ ]++checkPresent :: Remote -> Key -> Annex (Either String Bool)+checkPresent r k = do+ showAction $ "checking " ++ name r+ go =<< glacierEnv (fromJust $ config r) (uuid r)+ where+ go Nothing = return $ Left "cannot check glacier"+ go (Just e) = do+ {- glacier checkpresent outputs the archive name to stdout if+ - it's present. -}+ v <- liftIO $ catchMsgIO $ + readProcessEnv "glacier" (toCommand params) (Just e)+ case v of+ Right s -> do+ let probablypresent = key2file k `elem` lines s+ if probablypresent+ then ifM (Annex.getFlag "trustglacier")+ ( return $ Right True, untrusted )+ else return $ Right False+ Left err -> return $ Left err++ params =+ [ Param "archive"+ , Param "checkpresent"+ , Param $ remoteVault r+ , Param "--quiet"+ , Param $ archive r k+ ]++ untrusted = do+ showLongNote $ unlines+ [ "Glacier's inventory says it has a copy."+ , "However, the inventory could be out of date, if it was recently removed."+ , "(Use --trust-glacier if you're sure it's still in Glacier.)"+ , ""+ ]+ return $ Right False++glacierAction :: Remote -> [CommandParam] -> Annex Bool+glacierAction r params = runGlacier (fromJust $ config r) (uuid r) params++runGlacier :: RemoteConfig -> UUID -> [CommandParam] -> Annex Bool+runGlacier c u params = go =<< glacierEnv c u+ where+ go Nothing = return False+ go (Just e) = liftIO $+ boolSystemEnv "glacier" (glacierParams c params) (Just e)++glacierParams :: RemoteConfig -> [CommandParam] -> [CommandParam]+glacierParams c params = datacenter:params+ where+ datacenter = Param $ "--region=" +++ (fromJust $ M.lookup "datacenter" c)++glacierEnv :: RemoteConfig -> UUID -> Annex (Maybe [(String, String)])+glacierEnv c u = go =<< getRemoteCredPair "glacier" c creds+ where+ go Nothing = return Nothing+ go (Just (user, pass)) = do+ e <- liftIO getEnvironment+ return $ Just $ (uk, user):(pk, pass):e++ creds = AWS.creds u+ (uk, pk) = credPairEnvironment creds++remoteVault :: Remote -> Vault+remoteVault = vault . fromJust . config++vault :: RemoteConfig -> Vault+vault = fromJust . M.lookup "vault"++archive :: Remote -> Key -> Archive+archive r k = fileprefix ++ key2file k+ where+ fileprefix = M.findWithDefault "" "fileprefix" $ fromJust $ config r++-- glacier vault create will succeed even if the vault already exists.+genVault :: RemoteConfig -> UUID -> Annex ()+genVault c u = unlessM (runGlacier c u params) $+ error "Failed creating glacier vault."+ where+ params = + [ Param "vault"+ , Param "create"+ , Param $ vault c+ ]
+ Remote/Helper/AWS.hs view
@@ -0,0 +1,21 @@+{- Amazon Web Services common infrastructure.+ -+ - Copyright 2011,2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Remote.Helper.AWS where++import Common.Annex+import Creds++creds :: UUID -> CredPairStorage+creds u = CredPairStorage+ { credPairFile = fromUUID u+ , credPairEnvironment = ("AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY")+ , credPairRemoteKey = Just "s3creds"+ }++setCredsEnv :: CredPair -> IO ()+setCredsEnv p = setEnvCredPair p $ creds undefined
+ Remote/Helper/Chunked.hs view
@@ -0,0 +1,125 @@+{- git-annex chunked remotes+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Remote.Helper.Chunked where++import Common.Annex+import Utility.DataUnits+import Types.Remote+import Meters++import qualified Data.Map as M+import qualified Data.ByteString.Lazy as L+import Data.Int+import qualified Control.Exception as E++type ChunkSize = Maybe Int64++{- Gets a remote's configured chunk size. -}+chunkSize :: Maybe RemoteConfig -> ChunkSize+chunkSize Nothing = Nothing+chunkSize (Just m) =+ case M.lookup "chunksize" m of+ Nothing -> Nothing+ Just v -> case readSize dataUnits v of+ Nothing -> error "bad chunksize"+ Just size+ | size <= 0 -> error "bad chunksize"+ | otherwise -> Just $ fromInteger size++{- This is an extension that's added to the usual file (or whatever)+ - where the remote stores a key. -}+type ChunkExt = String++{- A record of the number of chunks used.+ -+ - While this can be guessed at based on the size of the key, encryption+ - makes that larger. Also, using this helps deal with changes to chunksize+ - over the life of a remote.+ -}+chunkCount :: ChunkExt+chunkCount = ".chunkcount"++{- Parses the String from the chunkCount file, and returns the files that+ - are used to store the chunks. -}+listChunks :: FilePath -> String -> [FilePath]+listChunks basedest chunkcount = take count $ map (basedest ++) chunkStream+ where+ count = fromMaybe 0 $ readish chunkcount++{- An infinite stream of extensions to use for chunks. -}+chunkStream :: [ChunkExt]+chunkStream = map (\n -> ".chunk" ++ show n) [1 :: Integer ..]++{- Given the base destination to use to store a value,+ - generates a stream of temporary destinations (just one when not chunking)+ - and passes it to an action, which should chunk and store the data,+ - and return the destinations it stored to, or [] on error. Then+ - calls the storer to write the chunk count (if chunking). Finally, the+ - fianlizer is called to rename the tmp into the dest + - (and do any other cleanup).+ -}+storeChunks :: Key -> FilePath -> FilePath -> ChunkSize -> ([FilePath] -> IO [FilePath]) -> (FilePath -> String -> IO ()) -> (FilePath -> FilePath -> IO ()) -> IO Bool+storeChunks key tmp dest chunksize storer recorder finalizer =+ either (const $ return False) return+ =<< (E.try go :: IO (Either E.SomeException Bool))+ where+ go = do+ stored <- storer tmpdests+ when (chunksize /= Nothing) $ do+ let chunkcount = basef ++ chunkCount+ recorder chunkcount (show $ length stored)+ finalizer tmp dest+ return (not $ null stored)++ basef = tmp ++ keyFile key+ tmpdests+ | chunksize == Nothing = [basef]+ | otherwise = map (basef ++ ) chunkStream++{- Given a list of destinations to use, chunks the data according to the+ - ChunkSize, and runs the storer action to store each chunk. Returns+ - the destinations where data was stored, or [] on error.+ -+ - This buffers each chunk in memory.+ - More optimal versions of this can be written, that rely+ - on L.toChunks to split the lazy bytestring into chunks (typically+ - smaller than the ChunkSize), and eg, write those chunks to a Handle.+ - But this is the best that can be done with the storer interface that+ - writes a whole L.ByteString at a time.+ -}+storeChunked :: ChunkSize -> [FilePath] -> (FilePath -> L.ByteString -> IO ()) -> L.ByteString -> IO [FilePath]+storeChunked chunksize dests storer content =+ either (const $ return []) return+ =<< (E.try (go chunksize dests) :: IO (Either E.SomeException [FilePath]))+ where+ go _ [] = return [] -- no dests!?++ go Nothing (d:_) = do+ storer d content+ return [d]++ go (Just sz) _+ -- always write a chunk, even if the data is 0 bytes+ | L.null content = go Nothing dests+ | otherwise = storechunks sz [] dests content+ + storechunks _ _ [] _ = return [] -- ran out of dests+ storechunks sz useddests (d:ds) b+ | L.null b = return $ reverse useddests+ | otherwise = do+ let (chunk, b') = L.splitAt sz b+ storer d chunk+ storechunks sz (d:useddests) ds b'++{- Writes a series of chunks to a file. The feeder is called to get+ - each chunk. -}+meteredWriteFileChunks :: MeterUpdate -> FilePath -> [v] -> (v -> IO L.ByteString) -> IO ()+meteredWriteFileChunks meterupdate dest chunks feeder =+ withBinaryFile dest WriteMode $ \h ->+ forM_ chunks $ \c ->+ meteredWrite meterupdate h =<< feeder c
Remote/Helper/Encryptable.hs view
@@ -81,24 +81,33 @@ cache <- Annex.getState Annex.ciphers case M.lookup encipher cache of Just cipher -> return $ Just cipher- Nothing -> decrypt encipher cache- decrypt encipher cache = do- showNote "gpg"- cipher <- liftIO $ decryptCipher encipher- Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })- return $ Just cipher+ Nothing -> do+ showNote "gpg"+ cipher <- liftIO $ decryptCipher encipher+ Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })+ return $ Just cipher -{- Checks if there is a trusted (non-shared) cipher. -}-isTrustedCipher :: RemoteConfig -> Bool-isTrustedCipher c = - isJust (M.lookup "cipherkeys" c) && isJust (M.lookup "cipher" c)+{- Checks if the remote's config allows storing creds in the remote's config.+ - + - embedcreds=yes allows this, and embedcreds=no prevents it.+ -+ - If not set, the default is to only store creds when it's surely safe:+ - When gpg encryption is used, in which case the creds will be encrypted+ - using it. Not when a shared cipher is used.+ -}+embedCreds :: RemoteConfig -> Bool+embedCreds c+ | M.lookup "embedcreds" c == Just "yes" = True+ | M.lookup "embedcreds" c == Just "no" = False+ | isJust (M.lookup "cipherkeys" c) && isJust (M.lookup "cipher" c) = True+ | otherwise = False {- Gets encryption Cipher, and encrypted version of Key. -} cipherKey :: Maybe RemoteConfig -> Key -> Annex (Maybe (Cipher, Key)) cipherKey Nothing _ = return Nothing-cipherKey (Just c) k = maybe Nothing encrypt <$> remoteCipher c+cipherKey (Just c) k = maybe Nothing make <$> remoteCipher c where- encrypt ciphertext = Just (ciphertext, encryptKey ciphertext k)+ make ciphertext = Just (ciphertext, encryptKey ciphertext k) {- Stores an StorableCipher in a remote's configuration. -} storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig
Remote/Hook.hs view
@@ -108,7 +108,8 @@ storeEncrypted :: String -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted h (cipher, enck) k _p = withTmp enck $ \tmp -> do src <- inRepo $ gitAnnexLocation k- liftIO $ withEncryptedContent cipher (L.readFile src) $ L.writeFile tmp+ liftIO $ encrypt cipher (feedFile src) $+ readBytes $ L.writeFile tmp runHook h "store" enck (Just tmp) $ return True retrieve :: String -> Key -> AssociatedFile -> FilePath -> Annex Bool@@ -120,7 +121,8 @@ retrieveEncrypted :: String -> (Cipher, Key) -> Key -> FilePath -> Annex Bool retrieveEncrypted h (cipher, enck) _ f = withTmp enck $ \tmp -> runHook h "retrieve" enck (Just tmp) $ liftIO $ catchBoolIO $ do- withDecryptedContent cipher (L.readFile tmp) $ L.writeFile f+ decrypt cipher (feedFile tmp) $+ readBytes $ L.writeFile f return True remove :: String -> Key -> Annex Bool
Remote/List.hs view
@@ -29,6 +29,10 @@ import qualified Remote.Directory import qualified Remote.Rsync import qualified Remote.Web+#ifdef WITH_WEBDAV+import qualified Remote.WebDAV+#endif+import qualified Remote.Glacier import qualified Remote.Hook remoteTypes :: [RemoteType]@@ -41,6 +45,10 @@ , Remote.Directory.remote , Remote.Rsync.remote , Remote.Web.remote+#ifdef WITH_WEBDAV+ , Remote.WebDAV.remote+#endif+ , Remote.Glacier.remote , Remote.Hook.remote ]
Remote/Rsync.hs view
@@ -110,7 +110,8 @@ storeEncrypted :: RsyncOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted o (cipher, enck) k p = withTmp enck $ \tmp -> do src <- inRepo $ gitAnnexLocation k- liftIO $ withEncryptedContent cipher (L.readFile src) $ L.writeFile tmp+ liftIO $ decrypt cipher (feedFile src) $+ readBytes $ L.writeFile tmp rsyncSend o p enck tmp retrieve :: RsyncOpts -> Key -> AssociatedFile -> FilePath -> Annex Bool@@ -128,7 +129,8 @@ retrieveEncrypted o (cipher, enck) _ f = withTmp enck $ \tmp -> do ifM (retrieve o enck undefined tmp) ( liftIO $ catchBoolIO $ do- withDecryptedContent cipher (L.readFile tmp) $ L.writeFile f+ decrypt cipher (feedFile tmp) $+ readBytes $ L.writeFile f return True , return False )
Remote/S3.hs view
@@ -5,7 +5,7 @@ - Licensed under the GNU GPL version 3 or higher. -} -module Remote.S3 (remote, s3SetCredsEnv) where+module Remote.S3 (remote) where import Network.AWS.AWSConnection import Network.AWS.S3Object@@ -14,8 +14,6 @@ import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.Map as M import Data.Char-import System.Environment-import System.Posix.Env (setEnv) import Common.Annex import Types.Remote@@ -24,11 +22,11 @@ import Config import Remote.Helper.Special import Remote.Helper.Encryptable+import qualified Remote.Helper.AWS as AWS import Crypto+import Creds+import Meters import Annex.Content-import Utility.Base64-import Annex.Perms-import Utility.FileMode remote :: RemoteType remote = RemoteType {@@ -87,7 +85,7 @@ use fullconfig = do gitConfigSpecialRemote u fullconfig "s3" "true"- s3SetCreds fullconfig u+ setRemoteCredPair fullconfig (AWS.creds u) defaulthost = do c' <- encryptionSetup c@@ -115,62 +113,69 @@ M.delete "bucket" defaults store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool-store r k _f _p = s3Action r False $ \(conn, bucket) -> do- dest <- inRepo $ gitAnnexLocation k- res <- liftIO $ storeHelper (conn, bucket) r k dest+store r k _f p = s3Action r False $ \(conn, bucket) -> do+ src <- inRepo $ gitAnnexLocation k+ res <- storeHelper (conn, bucket) r k p src s3Bool res storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool-storeEncrypted r (cipher, enck) k _p = s3Action r False $ \(conn, bucket) -> +storeEncrypted r (cipher, enck) k p = s3Action r False $ \(conn, bucket) -> -- To get file size of the encrypted content, have to use a temp file. -- (An alternative would be chunking to to a constant size.) withTmp enck $ \tmp -> do f <- inRepo $ gitAnnexLocation k- liftIO $ withEncryptedContent cipher (L.readFile f) $ \s -> L.writeFile tmp s- res <- liftIO $ storeHelper (conn, bucket) r enck tmp+ liftIO $ encrypt cipher (feedFile f) $+ readBytes $ L.writeFile tmp+ res <- storeHelper (conn, bucket) r enck p tmp s3Bool res -storeHelper :: (AWSConnection, String) -> Remote -> Key -> FilePath -> IO (AWSResult ())-storeHelper (conn, bucket) r k file = do- content <- liftIO $ L.readFile file- -- size is provided to S3 so the whole content does not need to be- -- buffered to calculate it+storeHelper :: (AWSConnection, String) -> Remote -> Key -> MeterUpdate -> FilePath -> Annex (AWSResult ())+storeHelper (conn, bucket) r k p file = do size <- maybe getsize (return . fromIntegral) $ keySize k- let object = setStorageClass storageclass $ - S3Object bucket (bucketFile r k) ""- (("Content-Length", show size) : xheaders) content- sendObject conn object+ meteredBytes (Just p) size $ \meterupdate ->+ liftIO $ withMeteredFile file meterupdate $ \content -> do+ -- size is provided to S3 so the whole content+ -- does not need to be buffered to calculate it+ let object = setStorageClass storageclass $ S3Object+ bucket (bucketFile r k) ""+ (("Content-Length", show size) : xheaders)+ content+ sendObject conn object where storageclass = case fromJust $ M.lookup "storageclass" $ fromJust $ config r of "REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY _ -> STANDARD- getsize = fileSize <$> (liftIO $ getFileStatus file)++ getsize = liftIO $ fromIntegral . fileSize <$> getFileStatus file xheaders = filter isxheader $ M.assocs $ fromJust $ config r isxheader (h, _) = "x-amz-" `isPrefixOf` h retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool-retrieve r k _f d = s3Action r False $ \(conn, bucket) -> do- res <- liftIO $ getObject conn $ bucketKey r bucket k- case res of- Right o -> do- liftIO $ L.writeFile d $ obj_data o- return True- Left e -> s3Warning e+retrieve r k _f d = s3Action r False $ \(conn, bucket) ->+ metered Nothing k $ \meterupdate -> do+ res <- liftIO $ getObject conn $ bucketKey r bucket k+ case res of+ Right o -> do+ liftIO $ meteredWriteFile meterupdate d $+ obj_data o+ return True+ Left e -> s3Warning e retrieveCheap :: Remote -> Key -> FilePath -> Annex Bool retrieveCheap _ _ _ = return False retrieveEncrypted :: Remote -> (Cipher, Key) -> Key -> FilePath -> Annex Bool-retrieveEncrypted r (cipher, enck) _ f = s3Action r False $ \(conn, bucket) -> do- res <- liftIO $ getObject conn $ bucketKey r bucket enck- case res of- Right o -> liftIO $ - withDecryptedContent cipher (return $ obj_data o) $ \content -> do- L.writeFile f content- return True- Left e -> s3Warning e+retrieveEncrypted r (cipher, enck) k d = s3Action r False $ \(conn, bucket) ->+ metered Nothing k $ \meterupdate -> do+ res <- liftIO $ getObject conn $ bucketKey r bucket enck+ case res of+ Right o -> liftIO $ decrypt cipher (\h -> meteredWrite meterupdate h $ obj_data o) $ + readBytes $ \content -> do+ L.writeFile d content+ return True+ Left e -> s3Warning e remove :: Remote -> Key -> Annex Bool remove r k = s3Action r False $ \(conn, bucket) -> do@@ -257,93 +262,13 @@ maybe (error "Cannot connect to S3") return =<< s3Connection c u s3Connection :: RemoteConfig -> UUID -> Annex (Maybe AWSConnection)-s3Connection c u = do- creds <- s3GetCreds c u- case creds of- Just (ak, sk) -> return $ Just $ AWSConnection host port ak sk- _ -> do- warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey ++ " to use S3"- return Nothing+s3Connection c u = go =<< getRemoteCredPair "S3" c (AWS.creds u) where+ go Nothing = return Nothing+ go (Just (ak, sk)) = return $ Just $ AWSConnection host port ak sk+ host = fromJust $ M.lookup "host" c port = let s = fromJust $ M.lookup "port" c in case reads s of [(p, _)] -> p _ -> error $ "bad S3 port value: " ++ s--{- S3 creds come from the environment if set, otherwise from the cache- - in gitAnnexCredsDir, or failing that, might be stored encrypted in- - the remote's config. -}-s3GetCreds :: RemoteConfig -> UUID -> Annex (Maybe (String, String))-s3GetCreds c u = maybe fromcache (return . Just) =<< liftIO getenv- where- getenv = liftM2 (,)- <$> get s3AccessKey- <*> get s3SecretKey- where- get = catchMaybeIO . getEnv- fromcache = do- d <- fromRepo gitAnnexCredsDir- let f = d </> fromUUID u- v <- liftIO $ catchMaybeIO $ readFile f- case lines <$> v of- Just (ak:sk:[]) -> return $ Just (ak, sk)- _ -> fromconfig- fromconfig = do- mcipher <- remoteCipher c- case (M.lookup "s3creds" c, mcipher) of- (Just s3creds, Just cipher) -> do- creds <- liftIO $ decrypt s3creds cipher- case creds of- [ak, sk] -> do- s3CacheCreds (ak, sk) u- return $ Just (ak, sk)- _ -> do error "bad s3creds" - _ -> return Nothing- decrypt s3creds cipher = lines - <$> withDecryptedContent cipher- (return $ L.pack $ fromB64 s3creds)- (return . L.unpack)--{- Stores S3 creds encrypted in the remote's config if possible to do so- - securely, and otherwise locally in gitAnnexCredsDir. -}-s3SetCreds :: RemoteConfig -> UUID -> Annex RemoteConfig-s3SetCreds c u = do- creds <- s3GetCreds c u- case creds of- Just (ak, sk) -> do- mcipher <- remoteCipher c- case mcipher of- Just cipher | isTrustedCipher c -> do- s <- liftIO $ withEncryptedContent cipher- (return $ L.pack $ unlines [ak, sk])- (return . L.unpack)- return $ M.insert "s3creds" (toB64 s) c- _ -> do- s3CacheCreds (ak, sk) u- return c- _ -> return c--{- The S3 creds are cached in gitAnnexCredsDir. -}-s3CacheCreds :: (String, String) -> UUID -> Annex ()-s3CacheCreds (ak, sk) u = do- d <- fromRepo gitAnnexCredsDir- createAnnexDirectory d- liftIO $ do- let f = d </> fromUUID u- h <- openFile f WriteMode- modifyFileMode f $ removeModes- [groupReadMode, otherReadMode]- hPutStr h $ unlines [ak, sk]- hClose h--{- Sets the S3 creds in the environment. -}-s3SetCredsEnv :: (String, String) -> IO ()-s3SetCredsEnv (ak, sk) = do- setEnv s3AccessKey ak True- setEnv s3SecretKey sk True--s3AccessKey :: String-s3AccessKey = "AWS_ACCESS_KEY_ID"-s3SecretKey :: String-s3SecretKey = "AWS_SECRET_ACCESS_KEY"
+ Remote/WebDAV.hs view
@@ -0,0 +1,334 @@+{- WebDAV remotes.+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++{-# LANGUAGE ScopedTypeVariables #-}++module Remote.WebDAV (remote, setCredsEnv) where++import Network.Protocol.HTTP.DAV+import qualified Data.Map as M+import qualified Data.ByteString.UTF8 as B8+import qualified Data.ByteString.Lazy.UTF8 as L8+import qualified Data.ByteString.Lazy as L+import qualified Data.Text.Lazy as LT+import qualified Text.XML as XML+import Network.URI (normalizePathSegments)+import qualified Control.Exception as E+import Network.HTTP.Conduit (HttpException(..))+import Network.HTTP.Types+import System.IO.Error++import Common.Annex+import Types.Remote+import qualified Git+import Config+import Remote.Helper.Special+import Remote.Helper.Encryptable+import Remote.Helper.Chunked+import Crypto+import Creds+import Meters++type DavUrl = String+type DavUser = B8.ByteString+type DavPass = B8.ByteString++remote :: RemoteType+remote = RemoteType {+ typename = "webdav",+ enumerate = findSpecialRemotes "webdav",+ generate = gen,+ setup = webdavSetup+}++gen :: Git.Repo -> UUID -> Maybe RemoteConfig -> Annex Remote+gen r u c = do+ cst <- remoteCost r expensiveRemoteCost+ return $ gen' r u c cst+gen' :: Git.Repo -> UUID -> Maybe RemoteConfig -> Int -> Remote+gen' r u c cst =+ encryptableRemote c+ (storeEncrypted this)+ (retrieveEncrypted this)+ this+ where+ this = Remote {+ uuid = u,+ cost = cst,+ name = Git.repoDescribe r,+ storeKey = store this,+ retrieveKeyFile = retrieve this,+ retrieveKeyFileCheap = retrieveCheap this,+ removeKey = remove this,+ hasKey = checkPresent this,+ hasKeyCheap = False,+ whereisKey = Nothing,+ config = c,+ repo = r,+ localpath = Nothing,+ readonly = False,+ remotetype = remote+ }++webdavSetup :: UUID -> RemoteConfig -> Annex RemoteConfig+webdavSetup u c = do+ let url = fromMaybe (error "Specify url=") $+ M.lookup "url" c+ c' <- encryptionSetup c+ creds <- getCreds c' u+ testDav url creds+ gitConfigSpecialRemote u c' "webdav" "true"+ setRemoteCredPair c' (davCreds u)++store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool+store r k _f p = metered (Just p) k $ \meterupdate ->+ davAction r False $ \(baseurl, user, pass) -> do+ f <- inRepo $ gitAnnexLocation k+ liftIO $ withMeteredFile f meterupdate $+ storeHelper r k baseurl user pass++storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool+storeEncrypted r (cipher, enck) k p = metered (Just p) k $ \meterupdate ->+ davAction r False $ \(baseurl, user, pass) -> do+ f <- inRepo $ gitAnnexLocation k+ liftIO $ encrypt cipher (streamMeteredFile f meterupdate) $+ readBytes $ storeHelper r enck baseurl user pass++storeHelper :: Remote -> Key -> DavUrl -> DavUser -> DavPass -> L.ByteString -> IO Bool+storeHelper r k baseurl user pass b = catchBoolIO $ do+ davMkdir tmpurl user pass+ storeChunks k tmpurl keyurl chunksize storer recorder finalizer+ where+ tmpurl = tmpLocation baseurl k+ keyurl = davLocation baseurl k+ chunksize = chunkSize $ config r+ storer urls = storeChunked chunksize urls storehttp b+ recorder url s = storehttp url (L8.fromString s)+ finalizer srcurl desturl = do+ void $ catchMaybeHttp (deleteContent desturl user pass)+ davMkdir (urlParent desturl) user pass+ moveContent srcurl (B8.fromString desturl) user pass+ storehttp url v = putContentAndProps url user pass+ (noProps, (contentType, v))++retrieveCheap :: Remote -> Key -> FilePath -> Annex Bool+retrieveCheap _ _ _ = return False++retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool+retrieve r k _f d = metered Nothing k $ \meterupdate ->+ davAction r False $ \(baseurl, user, pass) -> liftIO $ catchBoolIO $+ withStoredFiles r k baseurl user pass onerr $ \urls -> do+ meteredWriteFileChunks meterupdate d urls $ \url -> do+ mb <- davGetUrlContent url user pass+ case mb of+ Nothing -> throwIO "download failed"+ Just b -> return b+ return True+ where+ onerr _ = return False++retrieveEncrypted :: Remote -> (Cipher, Key) -> Key -> FilePath -> Annex Bool+retrieveEncrypted r (cipher, enck) k d = metered Nothing k $ \meterupdate ->+ davAction r False $ \(baseurl, user, pass) -> liftIO $ catchBoolIO $+ withStoredFiles r enck baseurl user pass onerr $ \urls -> do+ decrypt cipher (feeder user pass urls) $+ readBytes $ meteredWriteFile meterupdate d+ return True+ where+ onerr _ = return False++ feeder _ _ [] _ = noop+ feeder user pass (url:urls) h = do+ mb <- davGetUrlContent url user pass+ case mb of+ Nothing -> throwIO "download failed"+ Just b -> do+ L.hPut h b+ feeder user pass urls h++remove :: Remote -> Key -> Annex Bool+remove r k = davAction r False $ \(baseurl, user, pass) -> liftIO $ do+ -- Delete the key's whole directory, including any chunked+ -- files, etc, in a single action.+ let url = davLocation baseurl k+ isJust <$> catchMaybeHttp (deleteContent url user pass)++checkPresent :: Remote -> Key -> Annex (Either String Bool)+checkPresent r k = davAction r noconn go+ where+ noconn = Left $ error $ name r ++ " not configured"++ go (baseurl, user, pass) = do+ showAction $ "checking " ++ name r+ liftIO $ withStoredFiles r k baseurl user pass onerr check+ where+ check [] = return $ Right True+ check (url:urls) = do+ v <- davUrlExists url user pass+ if v == Right True+ then check urls+ else return v++ {- Failed to read the chunkcount file; see if it's missing,+ - or if there's a problem accessing it,+ - or perhaps this was an intermittent error. -}+ onerr url = do+ v <- davUrlExists url user pass+ if v == Right True+ then return $ Left $ "failed to read " ++ url+ else return v++withStoredFiles+ :: Remote+ -> Key+ -> DavUrl+ -> DavUser+ -> DavPass+ -> (DavUrl -> IO a)+ -> ([DavUrl] -> IO a)+ -> IO a+withStoredFiles r k baseurl user pass onerr a+ | isJust $ chunkSize $ config r = do+ let chunkcount = keyurl ++ chunkCount+ maybe (onerr chunkcount) (a . listChunks keyurl . L8.toString)+ =<< davGetUrlContent chunkcount user pass+ | otherwise = a [keyurl]+ where+ keyurl = davLocation baseurl k ++ keyFile k++davAction :: Remote -> a -> ((DavUrl, DavUser, DavPass) -> Annex a) -> Annex a+davAction r unconfigured action = case config r of+ Nothing -> return unconfigured+ Just c -> do+ mcreds <- getCreds c (uuid r)+ case (mcreds, M.lookup "url" c) of+ (Just (user, pass), Just url) ->+ action (url, toDavUser user, toDavPass pass)+ _ -> return unconfigured++toDavUser :: String -> DavUser+toDavUser = B8.fromString++toDavPass :: String -> DavPass+toDavPass = B8.fromString++{- The directory where files(s) for a key are stored. -}+davLocation :: DavUrl -> Key -> DavUrl+davLocation baseurl k = addTrailingPathSeparator $+ davUrl baseurl $ hashDirLower k </> keyFile k++{- Where we store temporary data for a key as it's being uploaded. -}+tmpLocation :: DavUrl -> Key -> DavUrl+tmpLocation baseurl k = addTrailingPathSeparator $+ davUrl baseurl $ "tmp" </> keyFile k++davUrl :: DavUrl -> FilePath -> DavUrl+davUrl baseurl file = baseurl </> file++davUrlExists :: DavUrl -> DavUser -> DavPass -> IO (Either String Bool)+davUrlExists url user pass = decode <$> catchHttp (getProps url user pass)+ where+ decode (Right _) = Right True+ decode (Left (Left (StatusCodeException status _)))+ | statusCode status == statusCode notFound404 = Right False+ decode (Left e) = Left $ showEitherException e++davGetUrlContent :: DavUrl -> DavUser -> DavPass -> IO (Maybe L.ByteString)+davGetUrlContent url user pass = fmap (snd . snd) <$>+ catchMaybeHttp (getPropsAndContent url user pass)++{- Creates a directory in WebDAV, if not already present; also creating+ - any missing parent directories. -}+davMkdir :: DavUrl -> DavUser -> DavPass -> IO ()+davMkdir url user pass = go url+ where+ make u = makeCollection u user pass++ go u = do+ r <- E.try (make u) :: IO (Either E.SomeException Bool)+ case r of+ {- Parent directory is missing. Recurse to create+ - it, and try once more to create the directory. -}+ Right False -> do+ go (urlParent u)+ void $ make u+ {- Directory created successfully -}+ Right True -> return ()+ {- Directory already exists, or some other error+ - occurred. In the latter case, whatever wanted+ - to use this directory will fail. -}+ Left _ -> return ()++{- Catches HTTP and IO exceptions. -}+catchMaybeHttp :: IO a -> IO (Maybe a)+catchMaybeHttp a = (Just <$> a) `E.catches`+ [ E.Handler $ \(_e :: HttpException) -> return Nothing+ , E.Handler $ \(_e :: E.IOException) -> return Nothing+ ]++{- Catches HTTP and IO exceptions -}+catchHttp :: IO a -> IO (Either EitherException a)+catchHttp a = (Right <$> a) `E.catches`+ [ E.Handler $ \(e :: HttpException) -> return $ Left $ Left e+ , E.Handler $ \(e :: E.IOException) -> return $ Left $ Right e+ ]++type EitherException = Either HttpException E.IOException++showEitherException :: EitherException -> String+showEitherException (Left (StatusCodeException status _)) = show $ statusMessage status+showEitherException (Left httpexception) = show httpexception+showEitherException (Right ioexception) = show ioexception++throwIO :: String -> IO a+throwIO msg = ioError $ mkIOError userErrorType msg Nothing Nothing++urlParent :: DavUrl -> DavUrl+urlParent url = dropTrailingPathSeparator $+ normalizePathSegments (dropTrailingPathSeparator url ++ "/..")+ where++{- Test if a WebDAV store is usable, by writing to a test file, and then+ - deleting the file. Exits with an IO error if not. -}+testDav :: String -> Maybe CredPair -> Annex ()+testDav baseurl (Just (u, p)) = do+ showSideAction "testing WebDAV server"+ liftIO $ either (throwIO . showEitherException) (const noop)+ =<< catchHttp go+ where+ go = do+ davMkdir baseurl user pass+ putContentAndProps testurl user pass+ (noProps, (contentType, L.empty))+ deleteContent testurl user pass++ user = toDavUser u+ pass = toDavPass p+ testurl = davUrl baseurl "git-annex-test"+testDav _ Nothing = error "Need to configure webdav username and password."++{- Content-Type to use for files uploaded to WebDAV. -}+contentType :: Maybe B8.ByteString+contentType = Just $ B8.fromString "application/octet-stream"++{- The DAV library requires that properties be specified when storing a file.+ - This just omits any real properties. -}+noProps :: XML.Document+noProps = XML.parseText_ XML.def $ LT.pack "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<propertyupdate/>"++getCreds :: RemoteConfig -> UUID -> Annex (Maybe CredPair)+getCreds c u = getRemoteCredPair "webdav" c (davCreds u)++davCreds :: UUID -> CredPairStorage+davCreds u = CredPairStorage+ { credPairFile = fromUUID u+ , credPairEnvironment = ("WEBDAV_USERNAME", "WEBDAV_PASSWORD")+ , credPairRemoteKey = Just "davcreds"+ }++setCredsEnv :: (String, String) -> IO ()+setCredsEnv creds = setEnvCredPair creds $ davCreds undefined
Seek.hs view
@@ -22,8 +22,14 @@ import qualified Option seekHelper :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> [FilePath] -> Annex [FilePath]-seekHelper a params = inRepo $ \g ->- runPreserveOrder (\fs -> Git.Command.leaveZombie <$> a fs g) params+seekHelper a params = do+ ll <- inRepo $ \g ->+ runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) params+ {- Show warnings only for files/directories that do not exist. -}+ forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p ->+ unlessM (liftIO $ doesFileExist p <||> doesDirectoryExist p) $+ fileNotFound p+ return $ concat ll withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesInGit a params = prepFiltered a $ seekHelper LsFiles.inRepo params@@ -34,7 +40,7 @@ files <- filter (not . dotfile) <$> seekunless (null ps && not (null params)) ps dotfiles <- seekunless (null dotps) dotps- prepFiltered a $ return $ preserveOrder params (files++dotfiles)+ prepFiltered a $ return $ concat $ segmentPaths params (files++dotfiles) where (dotps, ps) = partition dotfile params seekunless True _ = return []
Types.hs view
@@ -12,7 +12,8 @@ UUID(..), Remote, RemoteType,- Option+ Option,+ MeterUpdate ) where import Annex@@ -21,6 +22,7 @@ import Types.UUID import Types.Remote import Types.Option+import Types.Meters type Backend = BackendA Annex type Remote = RemoteA Annex
Types/Messages.hs view
@@ -7,14 +7,18 @@ module Types.Messages where +import qualified Data.Set as S+ data OutputType = NormalOutput | QuietOutput | JSONOutput data SideActionBlock = NoBlock | StartBlock | InBlock+ deriving (Eq) data MessageState = MessageState { outputType :: OutputType , sideActionBlock :: SideActionBlock+ , fileNotFoundShown :: S.Set FilePath } defaultMessageState :: MessageState-defaultMessageState = MessageState NormalOutput NoBlock+defaultMessageState = MessageState NormalOutput NoBlock S.empty
+ Types/Meters.hs view
@@ -0,0 +1,12 @@+{- git-annex meter types+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Types.Meters where++{- An action that can be run repeatedly, feeding it the number of+ - bytes sent or retrieved so far. -}+type MeterUpdate = (Integer -> IO ())
Types/Remote.hs view
@@ -15,8 +15,10 @@ import qualified Git import Types.Key import Types.UUID+import Types.Meters -type RemoteConfig = M.Map String String+type RemoteConfigKey = String+type RemoteConfig = M.Map RemoteConfigKey String {- There are different types of remotes. -} data RemoteTypeA a = RemoteType {@@ -35,10 +37,6 @@ {- A filename associated with a Key, for display to user. -} type AssociatedFile = Maybe FilePath--{- An action that can be run repeatedly, feeding it the number of- - bytes sent or retrieved so far. -}-type MeterUpdate = (Integer -> IO ()) {- An individual remote. -} data RemoteA a = Remote {
Types/StandardGroups.hs view
@@ -7,31 +7,35 @@ module Types.StandardGroups where -data StandardGroup = ClientGroup | TransferGroup | ArchiveGroup | BackupGroup+data StandardGroup = ClientGroup | TransferGroup | BackupGroup | SmallArchiveGroup | FullArchiveGroup deriving (Eq, Ord, Enum, Bounded, Show) fromStandardGroup :: StandardGroup -> String fromStandardGroup ClientGroup = "client" fromStandardGroup TransferGroup = "transfer"-fromStandardGroup ArchiveGroup = "archive" fromStandardGroup BackupGroup = "backup"+fromStandardGroup SmallArchiveGroup = "smallarchive"+fromStandardGroup FullArchiveGroup = "archive" toStandardGroup :: String -> Maybe StandardGroup toStandardGroup "client" = Just ClientGroup toStandardGroup "transfer" = Just TransferGroup-toStandardGroup "archive" = Just ArchiveGroup toStandardGroup "backup" = Just BackupGroup+toStandardGroup "smallarchive" = Just SmallArchiveGroup+toStandardGroup "archive" = Just FullArchiveGroup toStandardGroup _ = Nothing descStandardGroup :: StandardGroup -> String descStandardGroup ClientGroup = "client: a repository on your computer" descStandardGroup TransferGroup = "transfer: distributes files to clients"-descStandardGroup ArchiveGroup = "archive: collects files that are not archived elsewhere"-descStandardGroup BackupGroup = "backup: collects all files"+descStandardGroup BackupGroup = "backup: backs up all files"+descStandardGroup SmallArchiveGroup = "small archive: archives files located in \"archive\" directories"+descStandardGroup FullArchiveGroup = "full archive: archives all files not archived elsewhere" {- See doc/preferred_content.mdwn for explanations of these expressions. -} preferredContent :: StandardGroup -> String-preferredContent ClientGroup = "exclude=*/archive/*"+preferredContent ClientGroup = "exclude=*/archive/* and exclude=archive/*" preferredContent TransferGroup = "not (inallgroup=client and copies=client:2) and " ++ preferredContent ClientGroup-preferredContent ArchiveGroup = "not copies=archive:1" preferredContent BackupGroup = "" -- all content is preferred+preferredContent SmallArchiveGroup = "(include=*/archive/* or include=archive/*) and " ++ preferredContent FullArchiveGroup+preferredContent FullArchiveGroup = "not (copies=archive:1 or copies=smallarchive:1)"
Utility/Gpg.hs view
@@ -7,7 +7,6 @@ module Utility.Gpg where -import qualified Data.ByteString.Lazy as L import System.Posix.Types import Control.Applicative import Control.Concurrent@@ -54,14 +53,15 @@ hClose to hGetContentsStrict from -{- Runs gpg with some parameters, first feeding it a passphrase via- - --passphrase-fd, then feeding it an input, and passing a handle- - to its output to an action.+{- Runs gpg with some parameters. First sends it a passphrase via+ - --passphrase-fd. Then runs a feeder action that is passed a handle and+ - should write to it all the data to input to gpg. Finally, runs+ - a reader action that is passed a handle to gpg's output. - - Note that to avoid deadlock with the cleanup stage,- - the action must fully consume gpg's input before returning. -}-passphraseHandle :: [CommandParam] -> String -> IO L.ByteString -> (Handle -> IO a) -> IO a-passphraseHandle params passphrase a b = do+ - the reader must fully consume gpg's input before returning. -}+feedRead :: [CommandParam] -> String -> (Handle -> IO ()) -> (Handle -> IO a) -> IO a+feedRead params passphrase feeder reader = do -- pipe the passphrase into gpg on a fd (frompipe, topipe) <- createPipe void $ forkIO $ do@@ -77,9 +77,9 @@ where go (to, from) = do void $ forkIO $ do- L.hPut to =<< a+ feeder to hClose to- b from+ reader from {- Finds gpg public keys matching some string. (Could be an email address, - a key id, or a name. -}
Utility/OSX.hs view
@@ -38,6 +38,7 @@ , unlines $ map (\v -> "<string>" ++ v ++ "</string>") (command:params) , "</array>" , "<key>RunAtLoad</key>"+ , "<true/>" , "</dict>" , "</plist>" ]
+ Utility/Observed.hs view
@@ -0,0 +1,43 @@+module Utility.Observed where++import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as S+import System.IO+import System.IO.Unsafe+import Foreign.Storable (Storable(sizeOf))++{- This is like L.hGetContents, but after each chunk is read, an action+ - is run to observe the size of the chunk.+ -+ - Note that the observer is run in unsafeInterleaveIO, which means that+ - it can be run at any time. It's even possible for observers to run out+ - of order, as different parts of the ByteString are consumed.+ -+ - All the usual caveats about using unsafeInterleaveIO apply to the observers,+ - so use caution.+ -}+hGetContentsObserved :: Handle -> (Int -> IO ()) -> IO L.ByteString+hGetContentsObserved h observe = lazyRead+ where+ lazyRead = unsafeInterleaveIO loop++ loop = do+ c <- S.hGetSome h defaultChunkSize+ if S.null c+ then do+ hClose h+ return $ L.empty+ else do+ observe $ S.length c+ {- unsafeInterleaveIO causes this to be+ - deferred until the data is read from the+ - ByteString. -}+ cs <- lazyRead+ return $ L.append (L.fromChunks [c]) cs++{- Same default chunk size Lazy ByteStrings use. -}+defaultChunkSize :: Int+defaultChunkSize = 32 * k - chunkOverhead+ where+ k = 1024+ chunkOverhead = 2 * sizeOf (undefined :: Int) -- GHC specific
Utility/Path.hs view
@@ -104,29 +104,25 @@ same_dir_shortcurcuits_at_difference = relPathDirToFile "/tmp/r/lll/xxx/yyy/18" "/tmp/r/.git/annex/objects/18/gk/SHA256-foo/SHA256-foo" == "../../../../.git/annex/objects/18/gk/SHA256-foo/SHA256-foo" -{- Given an original list of files, and an expanded list derived from it,- - ensures that the original list's ordering is preserved. - -- - The input list may contain a directory, like "dir" or "dir/". Any- - items in the expanded list that are contained in that directory will- - appear at the same position as it did in the input list.+{- Given an original list of paths, and an expanded list derived from it,+ - generates a list of lists, where each sublist corresponds to one of the+ - original paths. When the original path is a direcotry, any items+ - in the expanded list that are contained in that directory will appear in+ - its segment. -}-preserveOrder :: [FilePath] -> [FilePath] -> [FilePath]-preserveOrder [] new = new-preserveOrder [_] new = new -- optimisation-preserveOrder (l:ls) new = found ++ preserveOrder ls rest+segmentPaths :: [FilePath] -> [FilePath] -> [[FilePath]]+segmentPaths [] new = [new]+segmentPaths [_] new = [new] -- optimisation+segmentPaths (l:ls) new = [found] ++ segmentPaths ls rest where (found, rest)=partition (l `dirContains`) new -{- Runs an action that takes a list of FilePaths, and ensures that - - its return list preserves order.- -- - This assumes that it's cheaper to call preserveOrder on the result,- - than it would be to run the action separately with each param. In the case- - of git file list commands, that assumption tends to hold.+{- This assumes that it's cheaper to call segmentPaths on the result,+ - than it would be to run the action separately with each path. In+ - the case of git file list commands, that assumption tends to hold. -}-runPreserveOrder :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [FilePath]-runPreserveOrder a files = preserveOrder files <$> a files+runSegmentPaths :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [[FilePath]]+runSegmentPaths a paths = segmentPaths paths <$> a paths {- Converts paths in the home directory to use ~/ -} relHome :: FilePath -> IO String
Utility/Process.hs view
@@ -65,7 +65,7 @@ , env = environ } -{- Writes a string to a process on its stdout, +{- Writes a string to a process on its stdin, - returns its output, and also allows specifying the environment. -} writeReadProcessEnv
Utility/TempFile.hs view
@@ -32,27 +32,27 @@ {- Runs an action with a temp file, then removes the file. -} withTempFile :: Template -> (FilePath -> Handle -> IO a) -> IO a withTempFile template a = bracket create remove use- where- create = do- tmpdir <- catchDefaultIO "." getTemporaryDirectory- openTempFile tmpdir template- remove (name, handle) = do- hClose handle- catchBoolIO (removeFile name >> return True)- use (name, handle) = a name handle+ where+ create = do+ tmpdir <- catchDefaultIO "." getTemporaryDirectory+ openTempFile tmpdir template+ remove (name, handle) = do+ hClose handle+ catchBoolIO (removeFile name >> return True)+ use (name, handle) = a name handle {- Runs an action with a temp directory, then removes the directory and - all its contents. -} withTempDir :: Template -> (FilePath -> IO a) -> IO a withTempDir template = bracket create remove- where- remove = removeDirectoryRecursive- create = do- tmpdir <- catchDefaultIO "." getTemporaryDirectory- createDirectoryIfMissing True tmpdir- pid <- getProcessID- makedir tmpdir (template ++ show pid) (0 :: Int)- makedir tmpdir t n = do- let dir = tmpdir </> t ++ "." ++ show n- r <- tryIO $ createDirectory dir- either (const $ makedir tmpdir t $ n + 1) (const $ return dir) r+ where+ remove = removeDirectoryRecursive+ create = do+ tmpdir <- catchDefaultIO "." getTemporaryDirectory+ createDirectoryIfMissing True tmpdir+ pid <- getProcessID+ makedir tmpdir (template ++ show pid) (0 :: Int)+ makedir tmpdir t n = do+ let dir = tmpdir </> t ++ "." ++ show n+ r <- tryIO $ createDirectory dir+ either (const $ makedir tmpdir t $ n + 1) (const $ return dir) r
Utility/ThreadScheduler.hs view
@@ -11,8 +11,6 @@ import Common import Control.Concurrent-import Control.Exception-import Control.Concurrent.Async import System.Posix.Terminal import System.Posix.Signals @@ -45,19 +43,6 @@ let maxWait = min time $ toInteger (maxBound :: Int) threadDelay $ fromInteger maxWait when (maxWait /= time) $ unboundDelay (time - maxWait)--{- Runs an action until a timeout is reached. If it fails to complete in- - time, or throws an exception, returns a Left value.- -- - Note that if the action runs an unsafe foreign call, the signal to- - cancel it may not arrive until the call returns. -}-runTimeout :: Seconds -> IO a -> IO (Either SomeException a)-runTimeout secs a = do- runner <- async a- controller <- async $ do- threadDelaySeconds secs- cancel runner- cancel controller `after` waitCatch runner {- Pauses the main thread, letting children run until program termination. -} waitForTermination :: IO ()
debian/changelog view
@@ -1,3 +1,33 @@+git-annex (3.20121126) unstable; urgency=low++ * New webdav and Amazon glacier special remotes.+ * Display a warning when a non-existing file or directory is specified.+ * webapp: Added configurator for Box.com.+ * webapp: Show error messages to user when testing XMPP creds.+ * Fix build of assistant without yesod.+ * webapp: The list of repositiories refreshes when new repositories are+ added, including when new repository configurations are pushed in from+ remotes.+ * OSX: Fix RunAtLoad value in plist file.+ * Getting a file from chunked directory special remotes no longer buffers+ it all in memory.+ * S3: Added progress display for uploading and downloading.+ * directory special remote: Made more efficient and robust.+ * Bugfix: directory special remote could loop forever storing a key + when a too small chunksize was configured.+ * Allow controlling whether login credentials for S3 and webdav are+ committed to the repository, by setting embedcreds=yes|no when running+ initremote.+ * Added smallarchive repository group, that only archives files that are+ in archive directories. Used by default for glacier when set up in the+ webapp.+ * assistant: Fixed handling of toplevel archive directory and+ client repository group.+ * assistant: Apply preferred content settings when a new symlink+ is created, or a symlink gets renamed. Made archive directories work.++ -- Joey Hess <joeyh@debian.org> Mon, 26 Nov 2012 11:37:49 -0400+ git-annex (3.20121112) unstable; urgency=low * assistant: Can use XMPP to notify other nodes about pushes made to other
debian/control view
@@ -4,6 +4,7 @@ Build-Depends: debhelper (>= 9), ghc (>= 7.4),+ libghc-mtl-dev (>= 2.1.1), libghc-missingh-dev, libghc-hslogger-dev, libghc-pcre-light-dev,@@ -12,6 +13,7 @@ libghc-http-dev, libghc-utf8-string-dev, libghc-hs3-dev (>= 0.5.6),+ libghc-dav-dev (>= 0.2), libghc-testpack-dev, libghc-quickcheck2-dev, libghc-monad-control-dev (>= 0.3),@@ -39,10 +41,9 @@ libghc-network-multicast-dev, libghc-network-info-dev, libghc-safesemaphore-dev,- libghc-network-protocol-xmpp-dev (>= 0.4.3-2),+ libghc-network-protocol-xmpp-dev (>= 0.4.3-1+b1), libghc-gnutls-dev (>= 0.1.4), libghc-xml-types-dev,- libghc-async-dev, ikiwiki, perlmagick, git,
doc/assistant.mdwn view
@@ -32,9 +32,11 @@ ## documentation * Want to make two nearby computers share the same synchronised folder?- Follow the [[local_pairing_walkthrough]].+ Follow the [[pairing_walkthrough]]. * Want to share a synchronised folder with a friend? Follow the [[share_with_a_friend_walkthrough]].+* Want to archive data to a drive or the cloud?+ Follow the [[archival_walkthrough]] ## command line startup
+ doc/assistant/archival_walkthrough.mdwn view
@@ -0,0 +1,28 @@+Normally, the git-annex assistant makes your files be available+wherever you use it, and so a copy of each file is stored in each repository.+That's perfect for files you're using right now, but what about files you're+not using any more?++You could just delete those files, but it's better to archive them, so+you can access them later. All you need to get started archiving your old+files is a USB drive, or an [Amazon Glacier](http://aws.amazon.com/glacier/)+account.++The webapp makes it easy to make a repository on either a USB drive,+or on Amazon Glacier. Once the repository is created, be sure to+put it in either the small archive, or full archive repository group.++[[!img repogroup.png]]++Now when you're done with a file, just move it into a directory named+"archive". The assistant will notice you put it there, and next time it+has the opportunity (when you plug in the USB drive, or when it can+talk to Amazon Glacier over the network), will move the file's+content to your archive repository.++You'll no longer be able to open the file once it's been archived.+If you later want to access it, you can just copy or move it out+of the archive directory, and the assistant will retrieve its+content from the archive.++Note that retrieving data from Amazon Glacier takes 4 to 5 hours.
doc/assistant/release_notes.mdwn view
@@ -1,4 +1,4 @@-## version 3.20121012+## version 3.20121112 This is a major upgrade of the git-annex assistant, which is still in beta.
+ doc/assistant/repogroup.png view
binary file changed (absent → 10986 bytes)
+ doc/bugs/3.20121112:_build_error_in_assistant.mdwn view
@@ -0,0 +1,432 @@+Git-annex stopped compiling with GHC 7.4.2 after updating Yesod and friends to the respective latest version. The complete build log is attached below. I hope this helps. Further build logs are available at <http://hydra.nixos.org/job/nixpkgs/trunk/gitAndTools.gitAnnex>, too.++ building+ make flags: PREFIX=/nix/store/9az61h33v1j6fkdmwdfy7gi0rhspsb9k-git-annex-3.20121112+ building Build/SysConfig.hs+ ghc -O2 -Wall -outputdir tmp -IUtility -DWITH_ASSISTANT -DWITH_S3 -DWITH_WEBAPP -DWITH_PAIRING -DWITH_XMPP -DWITH_DNS -DWITH_INOTIFY -DWITH_DBUS -threaded --make configure[b+ [1 of 7] Compiling Utility.Exception ( Utility/Exception.hs, tmp/Utility/Exception.o )+ [2 of 7] Compiling Utility.Misc ( Utility/Misc.hs, tmp/Utility/Misc.o )+ [3 of 7] Compiling Utility.Process ( Utility/Process.hs, tmp/Utility/Process.o )+ [4 of 7] Compiling Utility.SafeCommand ( Utility/SafeCommand.hs, tmp/Utility/SafeCommand.o )+ [5 of 7] Compiling Build.TestConfig ( Build/TestConfig.hs, tmp/Build/TestConfig.o )+ [6 of 7] Compiling Build.Configure ( Build/Configure.hs, tmp/Build/Configure.o )+ [7 of 7] Compiling Main ( configure.hs, tmp/Main.o )+ Linking configure ...+ ./configure+ checking version... 3.20121112+ checking git... yes+ checking git version... 1.8.0+ checking cp -a... yes+ checking cp -p... yes+ checking cp --reflink=auto... yes+ checking uuid generator... uuidgen+ checking xargs -0... yes+ checking rsync... yes+ checking curl... yes+ checking wget... no+ checking bup... no+ checking gpg... no+ checking lsof... no+ checking ssh connection caching... yes+ checking sha1... sha1sum+ checking sha256... sha256sum+ checking sha512... sha512sum+ checking sha224... sha224sum+ checking sha384... sha384sum+ building Utility/Touch.hs+ hsc2hs Utility/Touch.hsc[b+ building Utility/Mounts.hs+ hsc2hs Utility/Mounts.hsc[b+ building Utility/libdiskfree.o+ cc -Wall -c -o Utility/libdiskfree.o Utility/libdiskfree.c[b+ building Utility/libmounts.o+ cc -Wall -c -o Utility/libmounts.o Utility/libmounts.c[b+ building git-annex+ install -d tmp[b+ ghc -O2 -Wall -outputdir tmp -IUtility -DWITH_ASSISTANT -DWITH_S3 -DWITH_WEBAPP -DWITH_PAIRING -DWITH_XMPP -DWITH_DNS -DWITH_INOTIFY -DWITH_DBUS -threaded --make git-annex -o tmp/git-annex Utility/libdiskfree.o Utility/libmounts.o[b+ [ 1 of 279] Compiling Utility.Dot ( Utility/Dot.hs, tmp/Utility/Dot.o )+ [ 2 of 279] Compiling Utility.ThreadLock ( Utility/ThreadLock.hs, tmp/Utility/ThreadLock.o )+ [ 3 of 279] Compiling Utility.Mounts ( Utility/Mounts.hs, tmp/Utility/Mounts.o )+ [ 4 of 279] Compiling Utility.Yesod ( Utility/Yesod.hs, tmp/Utility/Yesod.o )+ [ 5 of 279] Compiling Utility.Tense ( Utility/Tense.hs, tmp/Utility/Tense.o )+ [ 6 of 279] Compiling Utility.Verifiable ( Utility/Verifiable.hs, tmp/Utility/Verifiable.o )+ [ 7 of 279] Compiling Assistant.Types.TransferSlots ( Assistant/Types/TransferSlots.hs, tmp/Assistant/Types/TransferSlots.o )+ [ 8 of 279] Compiling Types.StandardGroups ( Types/StandardGroups.hs, tmp/Types/StandardGroups.o )+ [ 9 of 279] Compiling Utility.Percentage ( Utility/Percentage.hs, tmp/Utility/Percentage.o )+ [ 10 of 279] Compiling Utility.Base64 ( Utility/Base64.hs, tmp/Utility/Base64.o )+ [ 11 of 279] Compiling Utility.DataUnits ( Utility/DataUnits.hs, tmp/Utility/DataUnits.o )+ [ 12 of 279] Compiling Utility.JSONStream ( Utility/JSONStream.hs, tmp/Utility/JSONStream.o )+ [ 13 of 279] Compiling Messages.JSON ( Messages/JSON.hs, tmp/Messages/JSON.o )+ [ 14 of 279] Compiling Build.SysConfig ( Build/SysConfig.hs, tmp/Build/SysConfig.o )+ [ 15 of 279] Compiling Types.KeySource ( Types/KeySource.hs, tmp/Types/KeySource.o )+ [ 16 of 279] Compiling Utility.State ( Utility/State.hs, tmp/Utility/State.o )+ [ 17 of 279] Compiling Types.UUID ( Types/UUID.hs, tmp/Types/UUID.o )+ [ 18 of 279] Compiling Types.Messages ( Types/Messages.hs, tmp/Types/Messages.o )+ [ 19 of 279] Compiling Types.Group ( Types/Group.hs, tmp/Types/Group.o )+ [ 20 of 279] Compiling Types.TrustLevel ( Types/TrustLevel.hs, tmp/Types/TrustLevel.o )+ [ 21 of 279] Compiling Types.BranchState ( Types/BranchState.hs, tmp/Types/BranchState.o )+ [ 22 of 279] Compiling Utility.PartialPrelude ( Utility/PartialPrelude.hs, tmp/Utility/PartialPrelude.o )+ [ 23 of 279] Compiling Utility.HumanTime ( Utility/HumanTime.hs, tmp/Utility/HumanTime.o )+ [ 24 of 279] Compiling Utility.Format ( Utility/Format.hs, tmp/Utility/Format.o )+ [ 25 of 279] Compiling Utility.FileSystemEncoding ( Utility/FileSystemEncoding.hs, tmp/Utility/FileSystemEncoding.o )+ [ 26 of 279] Compiling Utility.Touch ( Utility/Touch.hs, tmp/Utility/Touch.o )+ [ 27 of 279] Compiling Utility.Applicative ( Utility/Applicative.hs, tmp/Utility/Applicative.o )+ [ 28 of 279] Compiling Utility.Monad ( Utility/Monad.hs, tmp/Utility/Monad.o )+ [ 29 of 279] Compiling Utility.Exception ( Utility/Exception.hs, tmp/Utility/Exception.o )+ [ 30 of 279] Compiling Utility.DBus ( Utility/DBus.hs, tmp/Utility/DBus.o )+ [ 31 of 279] Compiling Utility.Misc ( Utility/Misc.hs, tmp/Utility/Misc.o )+ [ 32 of 279] Compiling Utility.Process ( Utility/Process.hs, tmp/Utility/Process.o )+ [ 33 of 279] Compiling Utility.SafeCommand ( Utility/SafeCommand.hs, tmp/Utility/SafeCommand.o )+ [ 34 of 279] Compiling Utility.Network ( Utility/Network.hs, tmp/Utility/Network.o )+ [ 35 of 279] Compiling Utility.SRV ( Utility/SRV.hs, tmp/Utility/SRV.o )++ Utility/SRV.hs:88:1: Warning: Defined but not used: `lookupSRVHost'++ Utility/SRV.hs:94:1: Warning: Defined but not used: `parseSrvHost'+ [ 36 of 279] Compiling Git.Types ( Git/Types.hs, tmp/Git/Types.o )+ [ 37 of 279] Compiling Utility.UserInfo ( Utility/UserInfo.hs, tmp/Utility/UserInfo.o )+ [ 38 of 279] Compiling Utility.Path ( Utility/Path.hs, tmp/Utility/Path.o )+ [ 39 of 279] Compiling Utility.TempFile ( Utility/TempFile.hs, tmp/Utility/TempFile.o )+ [ 40 of 279] Compiling Utility.Directory ( Utility/Directory.hs, tmp/Utility/Directory.o )+ [ 41 of 279] Compiling Utility.FreeDesktop ( Utility/FreeDesktop.hs, tmp/Utility/FreeDesktop.o )+ [ 42 of 279] Compiling Assistant.Install.AutoStart ( Assistant/Install/AutoStart.hs, tmp/Assistant/Install/AutoStart.o )+ [ 43 of 279] Compiling Common ( Common.hs, tmp/Common.o )+ [ 44 of 279] Compiling Utility.FileMode ( Utility/FileMode.hs, tmp/Utility/FileMode.o )+ [ 45 of 279] Compiling Git ( Git.hs, tmp/Git.o )+ [ 46 of 279] Compiling Git.FilePath ( Git/FilePath.hs, tmp/Git/FilePath.o )+ [ 47 of 279] Compiling Utility.Matcher ( Utility/Matcher.hs, tmp/Utility/Matcher.o )+ [ 48 of 279] Compiling Utility.Gpg ( Utility/Gpg.hs, tmp/Utility/Gpg.o )+ [ 49 of 279] Compiling Types.Crypto ( Types/Crypto.hs, tmp/Types/Crypto.o )+ [ 50 of 279] Compiling Types.Key ( Types/Key.hs, tmp/Types/Key.o )+ [ 51 of 279] Compiling Types.Backend ( Types/Backend.hs, tmp/Types/Backend.o )+ [ 52 of 279] Compiling Types.Remote ( Types/Remote.hs, tmp/Types/Remote.o )+ [ 53 of 279] Compiling Git.Sha ( Git/Sha.hs, tmp/Git/Sha.o )+ [ 54 of 279] Compiling Utility.CoProcess ( Utility/CoProcess.hs, tmp/Utility/CoProcess.o )+ [ 55 of 279] Compiling Git.Command ( Git/Command.hs, tmp/Git/Command.o )+ [ 56 of 279] Compiling Git.Ref ( Git/Ref.hs, tmp/Git/Ref.o )+ [ 57 of 279] Compiling Git.Branch ( Git/Branch.hs, tmp/Git/Branch.o )+ [ 58 of 279] Compiling Git.UpdateIndex ( Git/UpdateIndex.hs, tmp/Git/UpdateIndex.o )+ [ 59 of 279] Compiling Git.Queue ( Git/Queue.hs, tmp/Git/Queue.o )+ [ 60 of 279] Compiling Git.HashObject ( Git/HashObject.hs, tmp/Git/HashObject.o )+ [ 61 of 279] Compiling Git.CatFile ( Git/CatFile.hs, tmp/Git/CatFile.o )+ [ 62 of 279] Compiling Git.UnionMerge ( Git/UnionMerge.hs, tmp/Git/UnionMerge.o )+ [ 63 of 279] Compiling Git.Url ( Git/Url.hs, tmp/Git/Url.o )+ [ 64 of 279] Compiling Git.Construct ( Git/Construct.hs, tmp/Git/Construct.o )+ [ 65 of 279] Compiling Git.Config ( Git/Config.hs, tmp/Git/Config.o )+ [ 66 of 279] Compiling Git.SharedRepository ( Git/SharedRepository.hs, tmp/Git/SharedRepository.o )+ [ 67 of 279] Compiling Git.Version ( Git/Version.hs, tmp/Git/Version.o )+ [ 68 of 279] Compiling Git.CheckAttr ( Git/CheckAttr.hs, tmp/Git/CheckAttr.o )+ [ 69 of 279] Compiling Annex ( Annex.hs, tmp/Annex.o )+ [ 70 of 279] Compiling Types.Option ( Types/Option.hs, tmp/Types/Option.o )+ [ 71 of 279] Compiling Types ( Types.hs, tmp/Types.o )+ [ 72 of 279] Compiling Messages ( Messages.hs, tmp/Messages.o )+ [ 73 of 279] Compiling Types.Command ( Types/Command.hs, tmp/Types/Command.o )+ [ 74 of 279] Compiling Locations ( Locations.hs, tmp/Locations.o )+ [ 75 of 279] Compiling Common.Annex ( Common/Annex.hs, tmp/Common/Annex.o )+ [ 76 of 279] Compiling Fields ( Fields.hs, tmp/Fields.o )+ [ 77 of 279] Compiling Annex.BranchState ( Annex/BranchState.hs, tmp/Annex/BranchState.o )+ [ 78 of 279] Compiling Annex.CatFile ( Annex/CatFile.hs, tmp/Annex/CatFile.o )+ [ 79 of 279] Compiling Annex.Perms ( Annex/Perms.hs, tmp/Annex/Perms.o )+ [ 80 of 279] Compiling Crypto ( Crypto.hs, tmp/Crypto.o )+ [ 81 of 279] Compiling Annex.Exception ( Annex/Exception.hs, tmp/Annex/Exception.o )+ [ 82 of 279] Compiling Annex.Journal ( Annex/Journal.hs, tmp/Annex/Journal.o )+ [ 83 of 279] Compiling Annex.Branch ( Annex/Branch.hs, tmp/Annex/Branch.o )+ [ 84 of 279] Compiling Usage ( Usage.hs, tmp/Usage.o )+ [ 85 of 279] Compiling Annex.CheckAttr ( Annex/CheckAttr.hs, tmp/Annex/CheckAttr.o )+ [ 86 of 279] Compiling Remote.Helper.Special ( Remote/Helper/Special.hs, tmp/Remote/Helper/Special.o )+ [ 87 of 279] Compiling Logs.Presence ( Logs/Presence.hs, tmp/Logs/Presence.o )+ [ 88 of 279] Compiling Logs.Location ( Logs/Location.hs, tmp/Logs/Location.o )+ [ 89 of 279] Compiling Logs.Web ( Logs/Web.hs, tmp/Logs/Web.o )+ [ 90 of 279] Compiling Annex.LockPool ( Annex/LockPool.hs, tmp/Annex/LockPool.o )+ [ 91 of 279] Compiling Logs.Transfer ( Logs/Transfer.hs, tmp/Logs/Transfer.o )+ [ 92 of 279] Compiling Backend.SHA ( Backend/SHA.hs, tmp/Backend/SHA.o )+ [ 93 of 279] Compiling Backend.WORM ( Backend/WORM.hs, tmp/Backend/WORM.o )+ [ 94 of 279] Compiling Backend.URL ( Backend/URL.hs, tmp/Backend/URL.o )+ [ 95 of 279] Compiling Assistant.Types.ScanRemotes ( Assistant/Types/ScanRemotes.hs, tmp/Assistant/Types/ScanRemotes.o )+ [ 96 of 279] Compiling Assistant.Types.ThreadedMonad ( Assistant/Types/ThreadedMonad.hs, tmp/Assistant/Types/ThreadedMonad.o )+ [ 97 of 279] Compiling Assistant.Types.TransferQueue ( Assistant/Types/TransferQueue.hs, tmp/Assistant/Types/TransferQueue.o )+ [ 98 of 279] Compiling Assistant.Types.Pushes ( Assistant/Types/Pushes.hs, tmp/Assistant/Types/Pushes.o )+ [ 99 of 279] Compiling Assistant.Types.BranchChange ( Assistant/Types/BranchChange.hs, tmp/Assistant/Types/BranchChange.o )+ [100 of 279] Compiling Logs.UUIDBased ( Logs/UUIDBased.hs, tmp/Logs/UUIDBased.o )+ [101 of 279] Compiling Logs.Remote ( Logs/Remote.hs, tmp/Logs/Remote.o )+ [102 of 279] Compiling Logs.Group ( Logs/Group.hs, tmp/Logs/Group.o )+ [103 of 279] Compiling Utility.DiskFree ( Utility/DiskFree.hs, tmp/Utility/DiskFree.o )+ [104 of 279] Compiling Utility.Url ( Utility/Url.hs, tmp/Utility/Url.o )+ [105 of 279] Compiling Utility.CopyFile ( Utility/CopyFile.hs, tmp/Utility/CopyFile.o )+ [106 of 279] Compiling Utility.Rsync ( Utility/Rsync.hs, tmp/Utility/Rsync.o )+ [107 of 279] Compiling Git.LsFiles ( Git/LsFiles.hs, tmp/Git/LsFiles.o )+ [108 of 279] Compiling Git.AutoCorrect ( Git/AutoCorrect.hs, tmp/Git/AutoCorrect.o )+ [109 of 279] Compiling Git.CurrentRepo ( Git/CurrentRepo.hs, tmp/Git/CurrentRepo.o )+ [110 of 279] Compiling Locations.UserConfig ( Locations/UserConfig.hs, tmp/Locations/UserConfig.o )+ [111 of 279] Compiling Utility.ThreadScheduler ( Utility/ThreadScheduler.hs, tmp/Utility/ThreadScheduler.o )+ [112 of 279] Compiling Git.Merge ( Git/Merge.hs, tmp/Git/Merge.o )+ [113 of 279] Compiling Utility.Parallel ( Utility/Parallel.hs, tmp/Utility/Parallel.o )+ [114 of 279] Compiling Git.Remote ( Git/Remote.hs, tmp/Git/Remote.o )+ [115 of 279] Compiling Assistant.Ssh ( Assistant/Ssh.hs, tmp/Assistant/Ssh.o )+ [116 of 279] Compiling Assistant.Pairing ( Assistant/Pairing.hs, tmp/Assistant/Pairing.o )+ [117 of 279] Compiling Assistant.Types.NetMessager ( Assistant/Types/NetMessager.hs, tmp/Assistant/Types/NetMessager.o )+ [118 of 279] Compiling Utility.NotificationBroadcaster ( Utility/NotificationBroadcaster.hs, tmp/Utility/NotificationBroadcaster.o )+ [119 of 279] Compiling Assistant.Types.Buddies ( Assistant/Types/Buddies.hs, tmp/Assistant/Types/Buddies.o )+ [120 of 279] Compiling Utility.TSet ( Utility/TSet.hs, tmp/Utility/TSet.o )+ [121 of 279] Compiling Assistant.Types.Commits ( Assistant/Types/Commits.hs, tmp/Assistant/Types/Commits.o )+ [122 of 279] Compiling Assistant.Types.Changes ( Assistant/Types/Changes.hs, tmp/Assistant/Types/Changes.o )+ [123 of 279] Compiling Utility.WebApp ( Utility/WebApp.hs, tmp/Utility/WebApp.o )+ [124 of 279] Compiling Utility.Daemon ( Utility/Daemon.hs, tmp/Utility/Daemon.o )+ [125 of 279] Compiling Utility.LogFile ( Utility/LogFile.hs, tmp/Utility/LogFile.o )+ [126 of 279] Compiling Git.Filename ( Git/Filename.hs, tmp/Git/Filename.o )+ [127 of 279] Compiling Git.LsTree ( Git/LsTree.hs, tmp/Git/LsTree.o )+ [128 of 279] Compiling Utility.Types.DirWatcher ( Utility/Types/DirWatcher.hs, tmp/Utility/Types/DirWatcher.o )+ [129 of 279] Compiling Utility.INotify ( Utility/INotify.hs, tmp/Utility/INotify.o )+ [130 of 279] Compiling Utility.DirWatcher ( Utility/DirWatcher.hs, tmp/Utility/DirWatcher.o )+ [131 of 279] Compiling Utility.Lsof ( Utility/Lsof.hs, tmp/Utility/Lsof.o )+ [132 of 279] Compiling Config ( Config.hs, tmp/Config.o )+ [133 of 279] Compiling Annex.UUID ( Annex/UUID.hs, tmp/Annex/UUID.o )+ [134 of 279] Compiling Logs.UUID ( Logs/UUID.hs, tmp/Logs/UUID.o )+ [135 of 279] Compiling Backend ( Backend.hs, tmp/Backend.o )+ [136 of 279] Compiling Remote.Helper.Hooks ( Remote/Helper/Hooks.hs, tmp/Remote/Helper/Hooks.o )+ [137 of 279] Compiling Remote.Helper.Encryptable ( Remote/Helper/Encryptable.hs, tmp/Remote/Helper/Encryptable.o )+ [138 of 279] Compiling Annex.Queue ( Annex/Queue.hs, tmp/Annex/Queue.o )+ [139 of 279] Compiling Annex.Content ( Annex/Content.hs, tmp/Annex/Content.o )+ [140 of 279] Compiling Remote.S3 ( Remote/S3.hs, tmp/Remote/S3.o )+ [141 of 279] Compiling Remote.Directory ( Remote/Directory.hs, tmp/Remote/Directory.o )+ [142 of 279] Compiling Remote.Rsync ( Remote/Rsync.hs, tmp/Remote/Rsync.o )+ [143 of 279] Compiling Remote.Web ( Remote/Web.hs, tmp/Remote/Web.o )+ [144 of 279] Compiling Remote.Hook ( Remote/Hook.hs, tmp/Remote/Hook.o )+ [145 of 279] Compiling Upgrade.V2 ( Upgrade/V2.hs, tmp/Upgrade/V2.o )+ [146 of 279] Compiling Annex.Ssh ( Annex/Ssh.hs, tmp/Annex/Ssh.o )+ [147 of 279] Compiling Remote.Helper.Ssh ( Remote/Helper/Ssh.hs, tmp/Remote/Helper/Ssh.o )+ [148 of 279] Compiling Remote.Bup ( Remote/Bup.hs, tmp/Remote/Bup.o )+ [149 of 279] Compiling Annex.Version ( Annex/Version.hs, tmp/Annex/Version.o )+ [150 of 279] Compiling Init ( Init.hs, tmp/Init.o )+ [151 of 279] Compiling Checks ( Checks.hs, tmp/Checks.o )+ [152 of 279] Compiling Remote.Git ( Remote/Git.hs, tmp/Remote/Git.o )+ [153 of 279] Compiling Remote.List ( Remote/List.hs, tmp/Remote/List.o )+ [154 of 279] Compiling Logs.Trust ( Logs/Trust.hs, tmp/Logs/Trust.o )+ [155 of 279] Compiling Remote ( Remote.hs, tmp/Remote.o )+ [156 of 279] Compiling Assistant.Alert ( Assistant/Alert.hs, tmp/Assistant/Alert.o )+ Loading package ghc-prim ... linking ... done.+ Loading package integer-gmp ... linking ... done.+ Loading package base ... linking ... done.+ Loading object (static) Utility/libdiskfree.o ... done+ Loading object (static) Utility/libmounts.o ... done+ final link ... done+ Loading package pretty-1.1.1.0 ... linking ... done.+ Loading package filepath-1.3.0.0 ... linking ... done.+ Loading package old-locale-1.0.0.4 ... linking ... done.+ Loading package old-time-1.1.0.0 ... linking ... done.+ Loading package bytestring-0.9.2.1 ... linking ... done.+ Loading package unix-2.5.1.0 ... linking ... done.+ Loading package directory-1.1.0.2 ... linking ... done.+ Loading package process-1.1.0.1 ... linking ... done.+ Loading package array-0.4.0.0 ... linking ... done.+ Loading package deepseq-1.3.0.0 ... linking ... done.+ Loading package time-1.4 ... linking ... done.+ Loading package containers-0.4.2.1 ... linking ... done.+ Loading package text-0.11.2.0 ... linking ... done.+ Loading package blaze-builder-0.3.1.0 ... linking ... done.+ Loading package blaze-markup-0.5.1.1 ... linking ... done.+ Loading package blaze-html-0.5.1.0 ... linking ... done.+ Loading package hashable-1.1.2.5 ... linking ... done.+ Loading package case-insensitive-0.4.0.3 ... linking ... done.+ Loading package primitive-0.5.0.1 ... linking ... done.+ Loading package vector-0.10.0.1 ... linking ... done.+ Loading package random-1.0.1.1 ... linking ... done.+ Loading package dlist-0.5 ... linking ... done.+ Loading package data-default-0.5.0 ... linking ... done.+ Loading package transformers-0.3.0.0 ... linking ... done.+ Loading package mtl-2.1.1 ... linking ... done.+ Loading package parsec-3.1.2 ... linking ... done.+ Loading package network-2.3.0.13 ... linking ... done.+ Loading package failure-0.2.0.1 ... linking ... done.+ Loading package template-haskell ... linking ... done.+ Loading package shakespeare-1.0.2 ... linking ... done.+ Loading package hamlet-1.1.1.1 ... linking ... done.+ Loading package http-types-0.7.3.0.1 ... linking ... done.+ Loading package base-unicode-symbols-0.2.2.4 ... linking ... done.+ Loading package transformers-base-0.4.1 ... linking ... done.+ Loading package monad-control-0.3.1.4 ... linking ... done.+ Loading package lifted-base-0.2 ... linking ... done.+ Loading package resourcet-0.4.3 ... linking ... done.+ Loading package semigroups-0.8.4.1 ... linking ... done.+ Loading package void-0.5.8 ... linking ... done.+ Loading package conduit-0.5.4.1 ... linking ... done.+ Loading package unordered-containers-0.2.2.1 ... linking ... done.+ Loading package vault-0.2.0.1 ... linking ... done.+ Loading package wai-1.3.0.1 ... linking ... done.+ Loading package date-cache-0.3.0 ... linking ... done.+ Loading package unix-time-0.1.2 ... linking ... done.+ Loading package fast-logger-0.3.1 ... linking ... done.+ Loading package attoparsec-0.10.2.0 ... linking ... done.+ Loading package cookie-0.4.0.1 ... linking ... done.+ Loading package shakespeare-css-1.0.2 ... linking ... done.+ Loading package syb-0.3.6.1 ... linking ... done.+ Loading package aeson-0.6.0.2 ... linking ... done.+ Loading package shakespeare-js-1.1.0 ... linking ... done.+ Loading package ansi-terminal-0.5.5 ... linking ... done.+ Loading package blaze-builder-conduit-0.5.0.2 ... linking ... done.+ Loading package stringsearch-0.3.6.4 ... linking ... done.+ Loading package byteorder-1.0.3 ... linking ... done.+ Loading package wai-logger-0.3.0 ... linking ... done.+ Loading package zlib-0.5.3.3 ... linking ... done.+ Loading package zlib-bindings-0.1.1.1 ... linking ... done.+ Loading package zlib-conduit-0.5.0.2 ... linking ... done.+ Loading package wai-extra-1.3.0.4 ... linking ... done.+ Loading package monad-logger-0.2.1 ... linking ... done.+ Loading package cereal-0.3.5.2 ... linking ... done.+ Loading package base64-bytestring-1.0.0.0 ... linking ... done.+ Loading package cipher-aes-0.1.2 ... linking ... done.+ Loading package entropy-0.2.1 ... linking ... done.+ Loading package largeword-1.0.3 ... linking ... done.+ Loading package tagged-0.4.4 ... linking ... done.+ Loading package crypto-api-0.10.2 ... linking ... done.+ Loading package cpu-0.1.1 ... linking ... done.+ Loading package crypto-pubkey-types-0.1.1 ... linking ... done.+ Loading package cryptocipher-0.3.5 ... linking ... done.+ Loading package cprng-aes-0.2.4 ... linking ... done.+ Loading package skein-0.1.0.9 ... linking ... done.+ Loading package clientsession-0.8.0.1 ... linking ... done.+ Loading package path-pieces-0.1.2 ... linking ... done.+ Loading package shakespeare-i18n-1.0.0.2 ... linking ... done.+ Loading package yesod-routes-1.1.1.1 ... linking ... done.+ Loading package yesod-core-1.1.5 ... linking ... done.+ [157 of 279] Compiling Assistant.Types.DaemonStatus ( Assistant/Types/DaemonStatus.hs, tmp/Assistant/Types/DaemonStatus.o )+ [158 of 279] Compiling Assistant.Monad ( Assistant/Monad.hs, tmp/Assistant/Monad.o )+ [159 of 279] Compiling Assistant.Types.NamedThread ( Assistant/Types/NamedThread.hs, tmp/Assistant/Types/NamedThread.o )+ [160 of 279] Compiling Assistant.Common ( Assistant/Common.hs, tmp/Assistant/Common.o )+ [161 of 279] Compiling Assistant.XMPP ( Assistant/XMPP.hs, tmp/Assistant/XMPP.o )+ [162 of 279] Compiling Assistant.XMPP.Buddies ( Assistant/XMPP/Buddies.hs, tmp/Assistant/XMPP/Buddies.o )+ [163 of 279] Compiling Assistant.NetMessager ( Assistant/NetMessager.hs, tmp/Assistant/NetMessager.o )++ Assistant/NetMessager.hs:12:1:+ Warning: The import of `Types.Remote' is redundant+ except perhaps to import instances from `Types.Remote'+ To import instances alone, use: import Types.Remote()++ Assistant/NetMessager.hs:13:1:+ Warning: The import of `Git' is redundant+ except perhaps to import instances from `Git'+ To import instances alone, use: import Git()++ Assistant/NetMessager.hs:20:1:+ Warning: The import of `Data.Text' is redundant+ except perhaps to import instances from `Data.Text'+ To import instances alone, use: import Data.Text()+ [164 of 279] Compiling Assistant.Pushes ( Assistant/Pushes.hs, tmp/Assistant/Pushes.o )+ [165 of 279] Compiling Assistant.ScanRemotes ( Assistant/ScanRemotes.hs, tmp/Assistant/ScanRemotes.o )+ [166 of 279] Compiling Assistant.Install ( Assistant/Install.hs, tmp/Assistant/Install.o )+ [167 of 279] Compiling Assistant.XMPP.Client ( Assistant/XMPP/Client.hs, tmp/Assistant/XMPP/Client.o )+ [168 of 279] Compiling Assistant.Commits ( Assistant/Commits.hs, tmp/Assistant/Commits.o )+ [169 of 279] Compiling Assistant.BranchChange ( Assistant/BranchChange.hs, tmp/Assistant/BranchChange.o )+ [170 of 279] Compiling Assistant.Changes ( Assistant/Changes.hs, tmp/Assistant/Changes.o )+ [171 of 279] Compiling Assistant.WebApp.Types ( Assistant/WebApp/Types.hs, tmp/Assistant/WebApp/Types.o )+ Loading package unix-compat-0.4.0.0 ... linking ... done.+ Loading package file-embed-0.0.4.6 ... linking ... done.+ Loading package system-filepath-0.4.7 ... linking ... done.+ Loading package system-fileio-0.3.10 ... linking ... done.+ Loading package cryptohash-0.7.8 ... linking ... done.+ Loading package crypto-conduit-0.4.0.1 ... linking ... done.+ Loading package http-date-0.0.2 ... linking ... done.+ Loading package mime-types-0.1.0.0 ... linking ... done.+ Loading package wai-app-static-1.3.0.4 ... linking ... done.+ Loading package yesod-static-1.1.1.1 ... linking ... done.+ [172 of 279] Compiling Assistant.WebApp ( Assistant/WebApp.hs, tmp/Assistant/WebApp.o )+ Loading package network-conduit-0.6.1.1 ... linking ... done.+ Loading package safe-0.3.3 ... linking ... done.+ Loading package simple-sendfile-0.2.8 ... linking ... done.+ Loading package warp-1.3.4.4 ... linking ... done.+ Loading package yaml-0.8.1 ... linking ... done.+ Loading package yesod-default-1.1.2 ... linking ... done.+ [173 of 279] Compiling Assistant.WebApp.OtherRepos ( Assistant/WebApp/OtherRepos.hs, tmp/Assistant/WebApp/OtherRepos.o )+ [174 of 279] Compiling Limit ( Limit.hs, tmp/Limit.o )+ [175 of 279] Compiling Option ( Option.hs, tmp/Option.o )+ [176 of 279] Compiling Seek ( Seek.hs, tmp/Seek.o )+ [177 of 279] Compiling Command ( Command.hs, tmp/Command.o )+ [178 of 279] Compiling CmdLine ( CmdLine.hs, tmp/CmdLine.o )+ [179 of 279] Compiling Command.ConfigList ( Command/ConfigList.hs, tmp/Command/ConfigList.o )+ [180 of 279] Compiling Command.InAnnex ( Command/InAnnex.hs, tmp/Command/InAnnex.o )+ [181 of 279] Compiling Command.DropKey ( Command/DropKey.hs, tmp/Command/DropKey.o )+ [182 of 279] Compiling Command.SendKey ( Command/SendKey.hs, tmp/Command/SendKey.o )+ [183 of 279] Compiling Command.RecvKey ( Command/RecvKey.hs, tmp/Command/RecvKey.o )+ [184 of 279] Compiling Command.TransferInfo ( Command/TransferInfo.hs, tmp/Command/TransferInfo.o )+ [185 of 279] Compiling Command.Commit ( Command/Commit.hs, tmp/Command/Commit.o )+ [186 of 279] Compiling Command.Add ( Command/Add.hs, tmp/Command/Add.o )+ [187 of 279] Compiling Command.Unannex ( Command/Unannex.hs, tmp/Command/Unannex.o )+ [188 of 279] Compiling Command.FromKey ( Command/FromKey.hs, tmp/Command/FromKey.o )+ [189 of 279] Compiling Command.ReKey ( Command/ReKey.hs, tmp/Command/ReKey.o )+ [190 of 279] Compiling Command.Fix ( Command/Fix.hs, tmp/Command/Fix.o )+ [191 of 279] Compiling Command.Describe ( Command/Describe.hs, tmp/Command/Describe.o )+ [192 of 279] Compiling Command.InitRemote ( Command/InitRemote.hs, tmp/Command/InitRemote.o )+ [193 of 279] Compiling Command.Unlock ( Command/Unlock.hs, tmp/Command/Unlock.o )+ [194 of 279] Compiling Command.Lock ( Command/Lock.hs, tmp/Command/Lock.o )+ [195 of 279] Compiling Command.PreCommit ( Command/PreCommit.hs, tmp/Command/PreCommit.o )+ [196 of 279] Compiling Command.Log ( Command/Log.hs, tmp/Command/Log.o )+ [197 of 279] Compiling Command.Merge ( Command/Merge.hs, tmp/Command/Merge.o )+ [198 of 279] Compiling Command.Group ( Command/Group.hs, tmp/Command/Group.o )+ [199 of 279] Compiling Command.Ungroup ( Command/Ungroup.hs, tmp/Command/Ungroup.o )+ [200 of 279] Compiling Command.Import ( Command/Import.hs, tmp/Command/Import.o )+ [201 of 279] Compiling Logs.Unused ( Logs/Unused.hs, tmp/Logs/Unused.o )+ [202 of 279] Compiling Command.AddUnused ( Command/AddUnused.hs, tmp/Command/AddUnused.o )+ [203 of 279] Compiling Command.Find ( Command/Find.hs, tmp/Command/Find.o )+ [204 of 279] Compiling Logs.PreferredContent ( Logs/PreferredContent.hs, tmp/Logs/PreferredContent.o )+ [205 of 279] Compiling Annex.Wanted ( Annex/Wanted.hs, tmp/Annex/Wanted.o )+ [206 of 279] Compiling Command.Whereis ( Command/Whereis.hs, tmp/Command/Whereis.o )+ [207 of 279] Compiling Command.Trust ( Command/Trust.hs, tmp/Command/Trust.o )+ [208 of 279] Compiling Command.Untrust ( Command/Untrust.hs, tmp/Command/Untrust.o )+ [209 of 279] Compiling Command.Semitrust ( Command/Semitrust.hs, tmp/Command/Semitrust.o )+ [210 of 279] Compiling Command.Dead ( Command/Dead.hs, tmp/Command/Dead.o )+ [211 of 279] Compiling Command.Vicfg ( Command/Vicfg.hs, tmp/Command/Vicfg.o )+ [212 of 279] Compiling Command.Map ( Command/Map.hs, tmp/Command/Map.o )+ [213 of 279] Compiling Command.Init ( Command/Init.hs, tmp/Command/Init.o )+ [214 of 279] Compiling Command.Uninit ( Command/Uninit.hs, tmp/Command/Uninit.o )+ [215 of 279] Compiling Command.Version ( Command/Version.hs, tmp/Command/Version.o )+ [216 of 279] Compiling Upgrade.V1 ( Upgrade/V1.hs, tmp/Upgrade/V1.o )+ [217 of 279] Compiling Upgrade.V0 ( Upgrade/V0.hs, tmp/Upgrade/V0.o )+ [218 of 279] Compiling Upgrade ( Upgrade.hs, tmp/Upgrade.o )+ [219 of 279] Compiling Command.Upgrade ( Command/Upgrade.hs, tmp/Command/Upgrade.o )+ [220 of 279] Compiling Command.Drop ( Command/Drop.hs, tmp/Command/Drop.o )+ [221 of 279] Compiling Command.Move ( Command/Move.hs, tmp/Command/Move.o )+ [222 of 279] Compiling Command.Copy ( Command/Copy.hs, tmp/Command/Copy.o )+ [223 of 279] Compiling Command.Get ( Command/Get.hs, tmp/Command/Get.o )+ [224 of 279] Compiling Command.TransferKey ( Command/TransferKey.hs, tmp/Command/TransferKey.o )+ [225 of 279] Compiling Command.DropUnused ( Command/DropUnused.hs, tmp/Command/DropUnused.o )+ [226 of 279] Compiling Command.Fsck ( Command/Fsck.hs, tmp/Command/Fsck.o )+ [227 of 279] Compiling Command.Reinject ( Command/Reinject.hs, tmp/Command/Reinject.o )+ [228 of 279] Compiling Command.Migrate ( Command/Migrate.hs, tmp/Command/Migrate.o )+ [229 of 279] Compiling Command.Unused ( Command/Unused.hs, tmp/Command/Unused.o )+ [230 of 279] Compiling Command.Status ( Command/Status.hs, tmp/Command/Status.o )+ [231 of 279] Compiling Command.Sync ( Command/Sync.hs, tmp/Command/Sync.o )+ [232 of 279] Compiling Command.Help ( Command/Help.hs, tmp/Command/Help.o )+ [233 of 279] Compiling Command.AddUrl ( Command/AddUrl.hs, tmp/Command/AddUrl.o )+ [234 of 279] Compiling Assistant.DaemonStatus ( Assistant/DaemonStatus.hs, tmp/Assistant/DaemonStatus.o )+ [235 of 279] Compiling Assistant.Sync ( Assistant/Sync.hs, tmp/Assistant/Sync.o )+ [236 of 279] Compiling Assistant.MakeRemote ( Assistant/MakeRemote.hs, tmp/Assistant/MakeRemote.o )+ [237 of 279] Compiling Assistant.XMPP.Git ( Assistant/XMPP/Git.hs, tmp/Assistant/XMPP/Git.o )+ [238 of 279] Compiling Command.XMPPGit ( Command/XMPPGit.hs, tmp/Command/XMPPGit.o )+ [239 of 279] Compiling Assistant.Threads.NetWatcher ( Assistant/Threads/NetWatcher.hs, tmp/Assistant/Threads/NetWatcher.o )+ [240 of 279] Compiling Assistant.NamedThread ( Assistant/NamedThread.hs, tmp/Assistant/NamedThread.o )+ [241 of 279] Compiling Assistant.WebApp.Notifications ( Assistant/WebApp/Notifications.hs, tmp/Assistant/WebApp/Notifications.o )++ Assistant/WebApp/Notifications.hs:39:11:+ No instances for (Text.Julius.ToJavascript String,+ Text.Julius.ToJavascript Text)+ arising from a use of `Text.Julius.toJavascript'+ Possible fix:+ add instance declarations for+ (Text.Julius.ToJavascript String, Text.Julius.ToJavascript Text)+ In the first argument of `Text.Julius.Javascript', namely+ `Text.Julius.toJavascript delay'+ In the expression:+ Text.Julius.Javascript (Text.Julius.toJavascript delay)+ In the first argument of `Data.Monoid.mconcat', namely+ `[Text.Julius.Javascript+ ((Data.Text.Lazy.Builder.fromText . Text.Shakespeare.pack')+ "function longpoll_"),+ Text.Julius.Javascript (Text.Julius.toJavascript ident),+ Text.Julius.Javascript+ ((Data.Text.Lazy.Builder.fromText . Text.Shakespeare.pack')+ "() {\+ \\tlongpoll(longpoll_"),+ Text.Julius.Javascript (Text.Julius.toJavascript ident), ....]'+ make: *** [git-annex] Error 1++> Reproduced this and confirmed it's fixed in git. --[[Joey]] [[done]]
+ doc/bugs/3.20121112_build_fails_on_Ubuntu_12.04.mdwn view
@@ -0,0 +1,97 @@+What steps will reproduce the problem?++* Start with Ubuntu 12.04+* sudo apt-get install haskell-platform libgsasl7-dev gsasl g2hs+* cabal install git-annex --bindir=$HOME/bin++What is the expected output? What do you see instead?++Expected omething like "installation successful"++Actual output, after build notices:+++Loading package IfElse-0.85 ... linking ... done.+Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libdiskfree.o ... done+Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libmounts.o ... done+final link ... done+[157 of 279] Compiling Assistant.Types.DaemonStatus ( Assistant/Types/DaemonStatus.hs, dist/build/git-annex/git-annex-tmp/Assistant/Types/DaemonStatus.o )+[158 of 279] Compiling Assistant.Monad ( Assistant/Monad.hs, dist/build/git-annex/git-annex-tmp/Assistant/Monad.o )++Assistant/Monad.hs:86:16:+ Couldn't match expected type `Assistant a'+ with actual type `Reader AssistantData a'+ Expected type: (AssistantData -> a) -> Assistant a+ Actual type: (AssistantData -> a) -> Reader AssistantData a+ In the expression: reader+ In an equation for `getAssistant': getAssistant = reader++Assistant/Monad.hs:93:15:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: st <- reader threadState+ In the expression:+ do { st <- reader threadState;+ liftIO $ runThreadState st a }++Assistant/Monad.hs:99:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ liftIO $ io $ runAssistant d a }++Assistant/Monad.hs:105:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ runAssistant d a }++Assistant/Monad.hs:110:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ \ v -> runAssistant d $ a v }++Assistant/Monad.hs:115:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ \ v1 v2 -> runAssistant d (a v1 v2) }++Assistant/Monad.hs:120:12:+ Couldn't match expected type `Assistant a0'+ with actual type `Reader r0 a1'+ In the return type of a call of `reader'+ In the first argument of `(>>=)', namely `reader v'+ In the expression: reader v >>= liftIO . io+cabal: Error: some packages failed to install:+git-annex-3.20121112 failed during the building phase. The exception was:+ExitFailure 1+++What version of git-annex are you using? On what operating system?++git annex 3.20121112+Ubuntu 12.04 (current "long term support", all packages up to date)++Please provide any additional information below.++No idea how important this is for git-annex in general but reporting in case it is. Thank you for working on git annex!++> I was able to reproduce this build error when I force installed+> an old version of the haskell mtl library. So git-annex needs version+> 2.1.1 to build, and I have adjusted the build dependencies appropriately.+> [[done]] --[[Joey]]
+ doc/bugs/3.20121113_build_error___39__not_in_scope_getAddBoxComR__39__.mdwn view
@@ -0,0 +1,33 @@+What steps will reproduce the problem?++Building from latest source, Cabal update, cabal install --only dependencies, cabal configure, Cabal build++What is the expected output? What do you see instead?++Error message from build++...++Loading package DAV-0.2 ... linking ... done.++Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libdiskfree.o ... done++Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libmounts.o ... done++final link ... done+++Assistant/Threads/WebApp.hs:47:1: Not in scope: `getAddBoxComR'++Assistant/Threads/WebApp.hs:47:1: Not in scope: `getEnableWebDAVR'+++What version of git-annex are you using? On what operating system?++Latest version via git from git-annex.branchable.com++Debian Squeeze (6.0.6)++Please provide any additional information below.++> I noticed this earlier and fixed it. [[done]] --[[Joey]]
doc/bugs/Detection_assumes_that_shell_is_bash.mdwn view
@@ -16,3 +16,5 @@ ###Please provide any additional information below.### Not everyone has bash as there login-shell.++[[!tag /design/assistant]]
+ doc/bugs/Install_of_git-annex-3.20121112_fails.mdwn view
@@ -0,0 +1,20 @@+What steps will reproduce the problem?++- rm -rf ~/.ghc/ && cabal update && cabal install git-annex --bindir=$HOME/bin++What is the expected output? What do you see instead?++- I would like to have the latest release installed++What version of git-annex are you using? On what operating system?++- git-annex-3.20121112+- Ubuntu 12.04 LTS+- The Glorious Glasgow Haskell Compilation System, version 7.4.1++Please provide any additional information below.++I use it heavily on 4 machines since a month and I really like it.++> closing since this is a cabal library problem, and not something that+> can be fixed by any change to git-annex. [[done]] --[[Joey]]
+ doc/bugs/Is_there_any_way_to_rate_limit_uploads_to_an_S3_backend__63__.mdwn view
@@ -0,0 +1,19 @@+What steps will reproduce the problem?++Adding files to a local annex set up to sync to a remote S3 one+++What is the expected output? What do you see instead?++It syncs, but maxes out the uplink+++What version of git-annex are you using? On what operating system?++3.20121112 on Debian testing+++Please provide any additional information below.++The man page lists how to configure rate limiting for rsync, not sure how to do it for this+
+ doc/bugs/another_build_error_in_assistant.mdwn view
@@ -0,0 +1,79 @@+What steps will reproduce the problem?+Just trying to install git-annex last release (20121120) from cabal or from bundled sources++What is the expected output? What do you see instead?+Build stop like this :+(doing cabal install or build from the bundle)+...+[161 of 284] Compiling Assistant.Alert ( Assistant/Alert.hs, dist/build/git-annex/git-annex-tmp/Assistant/Alert.o )+[162 of 284] Compiling Assistant.Types.DaemonStatus ( Assistant/Types/DaemonStatus.hs, dist/build/git-annex/git-annex-tmp/Assistant/Types/DaemonStatus.o )+[163 of 284] Compiling Assistant.Monad ( Assistant/Monad.hs, dist/build/git-annex/git-annex-tmp/Assistant/Monad.o )++Assistant/Monad.hs:86:16:+ Couldn't match expected type `Assistant a'+ with actual type `Reader AssistantData a'+ Expected type: (AssistantData -> a) -> Assistant a+ Actual type: (AssistantData -> a) -> Reader AssistantData a+ In the expression: reader+ In an equation for `getAssistant': getAssistant = reader++Assistant/Monad.hs:93:15:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: st <- reader threadState+ In the expression:+ do { st <- reader threadState;+ liftIO $ runThreadState st a }++Assistant/Monad.hs:99:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ liftIO $ io $ runAssistant d a }++Assistant/Monad.hs:105:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ runAssistant d a }++Assistant/Monad.hs:110:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ \ v -> runAssistant d $ a v }++Assistant/Monad.hs:115:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ \ v1 v2 -> runAssistant d (a v1 v2) }++Assistant/Monad.hs:120:12:+ Couldn't match expected type `Assistant a0'+ with actual type `Reader r0 a1'+ In the return type of a call of `reader'+ In the first argument of `(>>=)', namely `reader v'+ In the expression: reader v >>= liftIO . io+++++What version of git-annex are you using? On what operating system?+- version 3.20121112+- Ubuntu 12.04 LTS, 64 bits++> Dup of [[3.20121112_build_fails_on_Ubuntu_12.04]]. --[[Joey]] [[done]]
+ doc/bugs/com.branchable.git-annex.assistant.plist_is_invalid.mdwn view
@@ -0,0 +1,15 @@+What steps will reproduce the problem?+`cat com.branchable.git-annex.assistant`++What version of git-annex are you using? On what operating system?+ git-annex version: 3.20121112 on OS X Mountain Lion++Please provide any additional information below.+The '`RunAtLoad`' key is missing a value.++It should say:++`<key>RunAtLoad</key>`<br>+`<true/>`++> Fixed in git. [[done]] --[[Joey]]
+ doc/bugs/dropping_and_re-adding_from_web_remotes_doesn__39__t_work.mdwn view
@@ -0,0 +1,131 @@+In experimenting with the web remote, I found that dropping a URL gave an error, but still succeeded, while re-adding the same URL fails to work correctly.++#What steps will reproduce the problem?++<pre>+/tmp $ dd if=/dev/zero of=/tmp/file.bin count=1024+1024+0 records in+1024+0 records out+524288 bytes (524 kB) copied, 0.0135652 s, 38.6 MB/s+/tmp $ mkdir /tmp/repo+/tmp $ cd /tmp/repo+/tmp/repo $ git init+Initialized empty Git repository in /tmp/repo/.git/+/tmp/repo $ git annex init "test"+init test ok+(Recording state in git...)+/tmp/repo $ git annex addurl file:///tmp/file.bin --file annexed.bin+######################################################################## 100.0%+(checksum...) ok+(Recording state in git...)+/tmp/repo $ git annex drop annexed.bin +drop annexed.bin (checking file:///tmp/file.bin...) ok+(Recording state in git...)+/tmp/repo $ mv /tmp/file.bin /tmp/file2.bin +/tmp/repo $ git annex get annexed.bin +get annexed.bin (from web...) +curl: (37) Couldn't open file /tmp/file.bin++ Unable to access these remotes: web++ Try making some of these repositories available:+ 00000000-0000-0000-0000-000000000001 -- web+failed+git-annex: get: 1 failed+/tmp/repo $ git annex drop --from web annexed.bin --force+drop web annexed.bin+ removal from web not supported+failed+(Recording state in git...)+git-annex: drop: 1 failed+/tmp/repo $ git annex get annexed.bin +get annexed.bin (not available) + No other repository is known to contain the file.+failed+git-annex: get: 1 failed+/tmp/repo $ mv /tmp/file2.bin /tmp/file.bin +/tmp/repo $ git annex addurl file:///tmp/file.bin --file annexed.bin+addurl annexed.bin ok+/tmp/repo $ git annex whereis annexed.bin+whereis annexed.bin (0 copies) failed+git-annex: whereis: 1 failed+/tmp/repo $ git annex addurl file:///tmp/file.bin --file annexed2.bin+######################################################################## 100.0%+(checksum...) ok+(Recording state in git...)+/tmp/repo $ git annex whereis annexed.bin+whereis annexed.bin (1 copy)+ e2418e81-ec04-4091-aabe-ed75d65f93fa -- here (test)+ok+/tmp/repo $ git annex whereis annexed2.bin+whereis annexed2.bin (1 copy)+ e2418e81-ec04-4091-aabe-ed75d65f93fa -- here (test)+ok+/tmp/repo $ git annex drop annexed.bin+drop annexed.bin (unsafe)+ Could only verify the existence of 0 out of 1 necessary copies++ No other repository is known to contain the file.++ (Use --force to override this check, or adjust annex.numcopies.)+failed+git-annex: drop: 1 failed+/tmp/repo $ git annex drop annexed2.bin+drop annexed2.bin (unsafe)+ Could only verify the existence of 0 out of 1 necessary copies++ No other repository is known to contain the file.++ (Use --force to override this check, or adjust annex.numcopies.)+failed+git-annex: drop: 1 failed+/tmp/repo $ mv /tmp/file.bin /tmp/file2.bin +/tmp/repo $ git annex addurl file:///tmp/file2.bin --file annexed.bin+addurl annexed.bin ok+(Recording state in git...)+/tmp/repo $ git annex whereis annexed2.bin +whereis annexed2.bin (2 copies) + 00000000-0000-0000-0000-000000000001 -- web+ e2418e81-ec04-4091-aabe-ed75d65f93fa -- here (test)++ web: file:///tmp/file.bin+ web: file:///tmp/file2.bin+ok+/tmp/repo $ mv /tmp/file2.bin /tmp/file.bin +/tmp/repo $ git annex drop annexed.bin +drop annexed.bin (checking file:///tmp/file.bin...) ok+(Recording state in git...)+/tmp/repo $ git annex get annexed.bin +get annexed.bin (from web...) +######################################################################## 100.0%+ok+(Recording state in git...)+/tmp/repo $ git annex drop annexed.bin +drop annexed.bin (checking file:///tmp/file.bin...) ok+(Recording state in git...)+/tmp/repo $ mv /tmp/file.bin /tmp/file2.bin +/tmp/repo $ git annex get annexed.bin +get annexed.bin (from web...) +curl: (37) Couldn't open file /tmp/file.bin+######################################################################## 100.0%+ok+(Recording state in git...)+</pre>++#What is the expected output? What do you see inst+++When dropping one file and I see "git-annex: drop: 1 failed" I would expect the file to still be in the remote as far as git-annex is concerned.++When re-adding the URL, I expect the file to be re-added to the web remote. (note that it re-appears after adding the same file via a different URL)+++#What version of git-annex are you using? On what operating system?+++3.20121112 on Gentoo Linux+++#Please provide any additional information below.++This seems to be a corner case, and would probably have minimal impact on most people.
+ doc/bugs/fat_support/comment_8_acc947643a635eb10a1bff92083a3506._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawmcYryijvlF8bJvM_eZNSrUPEkMlxMDGTQ"+ nickname="Thiago"+ subject="POSIX layer on top of VFAT using FUSE"+ date="2012-11-24T00:21:23Z"+ content="""+I just found out about this project and didn't try it, but it looks like it would allow using git-annex on an usb stick with a normal repository:++<http://sourceforge.net/projects/posixovl/>+"""]]
+ doc/bugs/glacier_with_assistant_bugs.mdwn view
@@ -0,0 +1,13 @@+* Need to handle retrying downloads of files from glacier after 4 hours.++* When a file is moved into archive/, the assistant that sends it to+ glacier is able to trust that it's in glacier and remove the local copy.+ But other assistants that also have a copy cannot trust that, and so+ don't remove their copies.++* For that matter, glacier-cli currently relies on a local cache of+ inventory information, and so other git-annexes using the same glacier+ repository are not able to access stuff in it, unless and until+ `glacier vault sync` is run.++[[!tag /design/assistant]]
+ doc/bugs/map_not_respecting_annex_ssh_options__63__.mdwn view
@@ -0,0 +1,37 @@+### What steps will reproduce the problem?++1. Have a remote that uses annex-ssh-options to specify an sshkey which is needed to invoke git-annex-shell on that remote.+2. Run git-annex map.++### What is the expected output? What do you see instead?++I expect to see a map without any errors complaining of commands not recognized.++Instead I see:++ greg@x200s:~/Pictures/Photos$ git-annex map+ map /home/greg/Pictures/Photos ok+ map 60justin (sshing...) + ok+ map rose (sshing...) + fatal: unrecognized command 'cd '/home/greg/Media/Pictures/Photos/' && git config --null --list'+ git-annex-shell: git-shell failed++relevant part of .git/config:++ [remote "rose"]+ url = greg@rose.makesad.us:/home/greg/Media/Pictures/Photos/+ fetch = +refs/heads/*:refs/remotes/rose/*+ annex-ssh-options = "-i /home/greg/.ssh/annex.x200s_rsa"+ annex-trustlevel = trusted+ annex-uuid = c0e4106e-2631-11e2-9749-1bfa37a61069+++### What version of git-annex are you using? On what operating system?++ git-annex version: 3.20121017+ local repository version: 3+ default repository version: 3+ supported repository versions: 3+ upgrade supported from repository versions: 0 1 2+
doc/bugs/migrated_files_not_showing_up_in_unused_list.mdwn view
@@ -57,3 +57,6 @@ 100 /var/lib/dpkg/status 3.20120629 0 650 http://ftp.us.debian.org/debian/ wheezy/main i386 Packages+++As Joey predicted, this took care of itself over time. Marking as [[bugs/done]].
+ doc/bugs/unlock_fails_silently_with_directory_symlinks.mdwn view
@@ -0,0 +1,53 @@+What steps will reproduce the problem?+++ ```~/``` is tracked by git and git annex++ ```~/text/books/foo``` is annexed++ ```~/books``` is a symlink to ```text/books```++ from ```~/``` execute: ```git annex unlock books/foo```++ which returns immediately with zero exit code and does not unlock foo.++What is the expected output? What do you see instead?+++ I expect ```~/text/books/foo`` to be unlocked+++ I think ```git annex unlock``` should resolve the symlinks and realize that this is a tracked file.++What version of git-annex are you using? On what operating system?+++ 3.20121112 in debian unstable++Please provide any additional information below.+++ I can unlock foo if I provide the full path, eg:+from ```~/``` execute: ```git annex unlock text/books/foo```+++ Interestingly, the following _does_ successfully unlock the file: ```cd ~/books && git annex unlock foo```++ So it seems that symlinks in $PWD are being resolved, but not those in file paths passed as arguments.++Thank you, thank you!++ - Jason++jason@jasonwoof.com++> I'm afraid this is not a bug. Here's why: If you run "git mv books/foo+> books/bar", git will complain:+>+>> fatal: not under version control, source=books/foo, destination=books/bar+>+> So git-annex is just following git's lead (indeed, it's just running+> `git ls-files` to find files to act on), and git doesn't+> recognise this path as a file that's in git. --[[Joey]] +++ Also, I think ```git annex unlock``` should emit an error message if a file explicitly addressed on the commandline can not be acted upon.++> I'm beginning to think perhaps it should. Users seem to find the current+> behavior to be sometimes confusing.+> +> However, it's actually a very difficult change to make. Several commands+> have multiple seek stages that act on different types of files, so+> any warning printed by an earlier stage may be premature if a later+> stage comes along and deals with a file. --[[Joey]]++>> Figured out a non-invasive way to add that warning. [[done]] --[[Joey]]
doc/design/assistant.mdwn view
@@ -9,10 +9,10 @@ * Month 2 "shiny webapp": [[!traillink webapp]] [[!traillink progressbars]] * Month 3 "easy setup": [[!traillink configurators]] [[!traillink pairing]] * Month 4 "cloud": [[!traillink cloud]] [[!traillink transfer_control]]+* Month 5 "cloud continued": [[!traillink xmpp]] [[!traillink more_cloud_providers]] We are, approximately, here: -* Month 5 "cloud continued": [[!traillink xmpp]] [[polls]] * Months 6-7 "9k bonus round": [[!traillink Android]] [[!traillink partial_content]] [[!traillink leftovers]] * Months 8-11: more user-driven features and polishing (see remaining TODO items in all pages above) * Month 12: "Windows purgatory" [[Windows]]
doc/design/assistant/OSX.mdwn view
@@ -4,9 +4,7 @@ * icon to start webapp **done** * Use OSX's "network reachability functionality" to detect when on a network <http://developer.apple.com/library/mac/#documentation/Networking/Conceptual/SystemConfigFrameworks/SC_Intro/SC_Intro.html#//apple_ref/doc/uid/TP40001065>-* daily build seems to have a bad yesod-static, resulting in the url- for `/static/jquery-full.js` redirecting to `/jquery-full.js?etag=foo',- which is a 404. Works ok when I build it on oberon.+* Fix app build to work on more OSX versions. Gripes:
+ doc/design/assistant/blog/day_129__release.mdwn view
@@ -0,0 +1,4 @@+Cut a new release today. It's been nearly a month since the last one, and a+large number of improvements.. Be sure to read the+[[/assistant/release_notes]] if upgrading. All the standalone builds are+updated already.
+ doc/design/assistant/blog/day_130__what_now.mdwn view
@@ -0,0 +1,36 @@+Dealt with post-release feedback deluge. There are a couple weird bugs that+I don't understand yet. OSX app is still not working everywhere.++----++Got the list of repositories in the webapp to update automatically when+repositories are added, including when syncing with a remote causes+repositories to be discovered.++----++I need a plan for the rest of the month. It feels right to focus on more+cloud storage support. Particularly because all the cloud providers+supported so far are ones that, while often being best of breed, also cost+money. To finish up the cloud story, need support for some free ones.++Looking at the results of the [[polls/prioritizing_special_remotes]]+poll, I suspect that free storage is a large part of why Google Drive got+so many votes. Soo, since there is not yet a Haskell library for Google+Drive, rather than spending a large chunk of time writing one, I hope to+use a [Haskell WebDAV library](http://hackage.haskell.org/package/DAV)+that my friend Clint recently wrote. A generic+WebDAV special remote in git-annex will provide much better support for+box.com (which has 5 to 50 gb free storage), as well as all the +[OwnCloud providers](http://owncloud.org/providers/), at least one of which+provides 5 gb free storage.++If I have time left this month after doing that, I'd be inclined to do+Amazon Glacier. People have already gotten that working with git-annex, but+a proper special remote should be better/easier, and will allow integrating+support for it into the assistant, which should help deal with its long+retrieval delays. And since, if you have a lot of data+archived in Glacier, you will only want to pull out a few files at a time,+this is another place besides mobile phones where a [[partial_content]]+retrieval UI is needed. Which is on the roadmap to be worked on next+month-ish. Synergy, here I come. I hope.
+ doc/design/assistant/blog/day_131__webdav_groundwork.mdwn view
@@ -0,0 +1,28 @@+Read up on WebDAV, and got the haskell library working. Several hours+were wasted by stumbling over a bug in the library, that requires a+carefully crafted XML document to prevent. Such a pity about things+like DAV (and XMPP) being designed back when people were gung-ho about+XML.. but we're stuck with them now.++Now I'm able to send and receive files to box.com using the library. Trying to+use an OwnCloud server, though, I get a most strange error message, which+looks to be coming from deep in the HTTPS library stack: "invalid IV length"++The haskell DAV library didn't have a way to delete files. I've added one+and sent off a patch.++Roughed in a skeleton of a webdav special remote. Doesn't do anything yet.+Will soon.++Factored out a Creds module from parts of the S3 special remote and XMPP+support, that all has to do with credentials storage. Using this for webdav+creds storage too.++Will also need to factor out the code that's currently in the directory+special remote, for chunking of files.++----++PS: WebDAV, for all its monstrously complicated feature set, lacks one obvious+feature: The ability to check how much free space is available to store+files. Eyeroll.
+ doc/design/assistant/blog/day_132__webdav_continued.mdwn view
@@ -0,0 +1,22 @@+Two releases of the Haskell DAV library today. First release had my changes+from yesterday. Then I realized I'd also need support for making WebDAV+"collections" (subdirectories), and sent Clint a patch for that too, as+well as a patch for querying DAV properties, and that was 0.2.+Got it into Debian unstable as well. Should have everything I'll need now.++The webdav special remote is now working! Still todo:+Encryption support, progress bars, large file chunking, and webapp+configurators. But already, it's a lot nicer than the old approach of using+davfs2, which was really flakey and slow with large data volumes.++I did notice, though, that uploading a 100 mb file made the process use 100+mb of memory. This is a problem I've struggled with earlier with S3, the+Haskell http libraries are prevented from streaming data by several parts+of the protocol that cause the data to be accessed more than once. I guess+this won't be a problem for DAV though, since it'll probably be chunking+files anyway.++---++Mailed out all my Kickstarter USB key rewards, and ordered+the T-shirts too.
+ doc/design/assistant/blog/day_133__webdav_working.mdwn view
@@ -0,0 +1,31 @@+Worked on webdav special remotes all day. ++* Got encryption working,+ after fixing an amusing typo that made `initremote` for webdav throw away the+ encryption configuration and store files unencrypted.+* Factored out parts of the directory special remote that had to do with file+ chunking, and am using that for webdav. This refactoring was painful.++At this point, I feel the webdav special remote works better than the old+davfs2 + directory special remote hack. While webdav doesn't yet have+progress info for uploads, that info was pretty busted anyway with+davfs2 due to how it buffers files. So ... I've merged webdav into master!++-----++Tomorrow, webapp configurators for Box.com and any other webdav supporting+sites I can turn up and get to work..++-----++A while ago I made git-annex not store login credentials in git for special+remotes, when it's only encrypting them with a shared cipher. The+rationalle was that you don't want to give everyone who gets ahold of your+git repo (which includes the encryption key) access to your passwords,+Amazon S3 account, or whatever. I'm now considering adding a checkbox (or+command-line flag) that allows storing the login credentials in git,+if the user wants to. While using public key crypto is the real solution+(and is fully supported by git-annex (but not yet configurable in the+webapp)), this seems like a reasonable thing to do in some circumstances,+like when you have a Box.com account you really do want to share with+the people who use the git repo.
+ doc/design/assistant/blog/day_134__box.com_configurator.mdwn view
@@ -0,0 +1,8 @@+I needed an easy day, and I got one. Configurator in the webapp for Box.com+came together really quickly and easily, and worked on the first try.++Also filed a [bug](https://github.com/vincenthz/hs-cryptocipher/issues/21)+on the Haskell library that is failing on portknox.com's SSL certificate.+That site is the only OwnCloud provider currently offering free WebDAV+storage. Will hold off on adding OwnCloud to the webapp's cloud provider lists+until that's fixed.
+ doc/design/assistant/blog/day_135__progress_revisited.mdwn view
@@ -0,0 +1,37 @@+Unexpectedly today, I got progress displays working for uploads via WebDAV.++The roadblock had been that the interface of for uploading to S3 and WebDAV+is something like `ByteString -> IO a`. Which doesn't provide any hook to+update a progress display as the ByteString is consumed.++My solution to this was to create a `hGetContentsObserved`, that's similar+to `hGetContents`, but after reading each 64kb chunk of data from the+Handle to populate the ByteString, it runs some observing action. So when+the ByteString is streamed, as each chunk is consumed, the observer+runs. I was able to get this to run in constant space, despite not having+access to some of the ByteString internals that `hGetContents` is built+with.++So, a little scary, but nice. I am curious if there's not a better way+to solve this problem hidden in a library somewhere. Perhaps it's another+thing that conduit solves somehow? Because if there's not, my solution+probably deserves to be put into a library. Any Haskell folk know?++----++Used above to do progress displays for uploads to S3. Also did progress+display to console on download from S3. Now very close to being done+with [[progressbars]]. Finally. Only bup and hook remotes need progress+work.++----++Reworked the core crypto interface, to better support streaming data through+gpg. This allowed fixing both the directory and webdav special remotes to+not buffer whole files in memory when retrieving them as chunks from the+remote.++-----++Spent some time dealing with API changes in Yesod and Conduit. Some of them+annoyingly gratuitous.
+ doc/design/assistant/blog/day_136__misc.mdwn view
@@ -0,0 +1,14 @@+Changed how the directory and webdav special remotes store content.+The new method uses less system calls, is more robust, and leaves any+partially transferred key content in a single tmp directory, which+will make it easier to clean that out later.++Also found & fixed a cute bug in the directory special remote when the+chunksize is set to a smaller value than the ByteString chunk size, that+caused it to loop forever creating empty files.++----++Added an embedcreds=yes option when setting up special remotes. +Will put UI for it into the webapp later, but rather than work on that+tomorrow, I plan to work on glacier.
+ doc/design/assistant/blog/day_137__Glacier.mdwn view
@@ -0,0 +1,30 @@+Got Amazon Glacier working as a full-fledged special remote.++(Well, I think it works... Since it takes 4 hours to get data out,+which is longer than the time it took me to sign up for Glacier and+write the special remote ... I've yet to fully test it!)++Thanks to Robie Basak for writing glacier-cli, and developing the intial+hook remote support. Also thanks to Peter Todd for pointing out that+Glacier cannot store empty files, which had to be worked around in the+special remote.++Of course the 4 hour delay on retreval makes Glacier interesting. For now,+you have to run "git annex get" twice, once to queue the retrieval, and a+second time in 4 hours to get the file(s). There is a helpful example in+[[tips/using_Amazon_Glacier]].++The real complication though, is that Glacier's inventories take a long+time to get, and can be out of date. So glacier-cli caches inventory info.+I didn't feel comfortable making git-annex trust that information,+so it'll refuse to trust that Glacier has a copy of a file when dropping+it. There's a `--trust-glacier` switch to override this default paranoid+behavior when dropping files.++----++Tomorrow ... er, tomorrow is Thanksgiving trip start.++Next weekend: Webapp configurator for glacier, and maybe something+to get the assistant to detect when jobs are complete and finish+retrievals from Glacier, automatically.
+ doc/design/assistant/blog/day_138__back.mdwn view
@@ -0,0 +1,25 @@+Added a configurator for Glacier repositories to the webapp. That was the last+cloud repository configurator that was listed in the webapp and wasn't+done. Indeed, just two more repository configurators remain to be filled in:+phone and NAS.++By default, Glacier repositories are put in a new "small archive" group.+This makes only files placed in "archive" directories be sent to Glacier+(as well as being dropped from clients), unlike the full archive group+which archives all files. Of course you can change this setting, but+avoiding syncing all data to Glacier seemed like a good default, especially+since some are still worried about Glacier's pricing model.++Fixed several bugs in the handling of archive directories, and +the webapp makes a toplevel archive directory when an archive remote is+created, so the user can get on with using it.++Made the assistant able to drop local files immediately after transferring+them to glacier, despite not being able to trust glacier's inventory.+This was accomplished by making the transferrer, after a successful upload,+indicate that it trusts the remote it just uploaded to has the file,+when it checks if the file should be dropped.++Only thing left to do for glacier is to make the assistant retry failed+downloads from it after 4 hours, or better, as soon as they become+available.
+ doc/design/assistant/blog/day_139__catch_up.mdwn view
@@ -0,0 +1,11 @@+Got progress bars working for glacier. This needed some glacier-cli+changes, which Robie helpfully made earlier.++Spent some hours getting caught up and responding to bug reports, etc.++Spent a while trying to make git-annex commands that fail to find+any matching files to act on print a useful warning message,+rather than the current nothing. Concluded this will be surprisingly+hard to do, due to the multiple seek passes some commands perform. Update:+Thought of a non-invasive and not too ugly way to do this while on my+evening walk, and this wart is gone.
doc/design/assistant/blog/day_36__minimal_test_case.mdwn view
@@ -1,7 +1,7 @@ Managed to find a minimal, 20 line test case for at least one of the ways git-annex was hanging with GHC's threaded runtime. Sent it off to haskell-cafe for analysis.-[thread](http://news.gmane.org/gmane.comp.lang.haskell.cafe)+[thread](http://thread.gmane.org/gmane.comp.lang.haskell.cafe/99334) Further managed to narrow the bug down to MissingH's use of logging code, that git-annex doesn't use. [bug report](http://bugs.debian.org/681621).
doc/design/assistant/cloud.mdwn view
@@ -2,23 +2,6 @@ not in these pre-IPV6 days, so the cloud needs to be used to bridge between LANS. -## more cloud providers--Git-annex already supports storing large files in -several cloud providers via [[special_remotes]].-More should be added, such as:--* Google drive (attractive because it's free, only 5 gb tho)-* OpenStack Swift (teh future)-* Box.com (it's free, and current method is hard to set up and a sorta- shakey; a better method would be to use its API)-* Dropbox? That would be ironic.. Via its API, presumably.-* [[Amazon Glacier|todo/special_remote_for_amazon_glacier]]-* [nimbus.io](https://nimbus.io/) Fairly low prices ($0.06/GB);- REST API; free software--See poll at [[polls/prioritizing_special_remotes]].- ## The cloud notification problem **done** Alice and Bob have repos, and there is a cloud remote they both share.
+ doc/design/assistant/more_cloud_providers.mdwn view
@@ -0,0 +1,16 @@+Git-annex already supports storing large files in +several cloud providers via [[special_remotes]].+More should be added, such as:++* Google drive (attractive because it's free, only 5 gb tho)+* Owncloud (has several [providers](http://owncloud.org/providers/);+ at least one provides 5 gb free; open DAV based API)+* OpenStack Swift (teh future)+* Box.com (it's free, and current method is hard to set up and a sorta+ shakey; a better method would be to use its API) **done**+* Dropbox? That would be ironic.. Via its API, presumably.+* [[Amazon Glacier|todo/special_remote_for_amazon_glacier]] **done**+* [nimbus.io](https://nimbus.io/) Fairly low prices ($0.06/GB);+ REST API; free software++See poll at [[polls/prioritizing_special_remotes]].
doc/design/assistant/polls/prioritizing_special_remotes.mdwn view
@@ -6,7 +6,7 @@ Help me prioritize my work: What special remote would you most like to use with the git-annex assistant? -[[!poll open=yes 15 "Amazon S3 (done)" 11 "Amazon Glacier" 8 "Box.com" 61 "My phone (or MP3 player)" 15 "Tahoe-LAFS" 5 "OpenStack SWIFT" 23 "Google Drive"]]+[[!poll open=yes 15 "Amazon S3 (done)" 12 "Amazon Glacier (done)" 9 "Box.com (done)" 63 "My phone (or MP3 player)" 16 "Tahoe-LAFS" 6 "OpenStack SWIFT" 23 "Google Drive"]] This poll is ordered with the options I consider easiest to build listed first. Mostly because git-annex already supports them and they
doc/design/assistant/progressbars.mdwn view
@@ -10,7 +10,6 @@ ## downloads * Watch temp file as it's coming in and use its size.- This is the only option for some special remotes (ie, non-rsync). Can either poll every .5 seconds or so to check file size, or could use inotify. **done** @@ -23,7 +22,9 @@ * rsync: **done** * directory: **done** * web: Not applicable; does not upload -* S3: TODO+* webdav: **done**+* S3: **done**+* glacier: **done** * bup: TODO * hook: Would require the hook interface to somehow do this, which seems too complicated. So skipping.
doc/design/assistant/transfer_control.mdwn view
@@ -19,6 +19,9 @@ log is not updated in time, it will fail to drop unwanted content. (There's a 10 second sleep there now to avoid the race, but that's hardly a fix.)+* When a file is renamed into an archive directory, it's not immediately+ transferred to archive remotes. (Next expensive scan does successfully+ cause the transfer to happen). ### dropping no longer preferred content
doc/design/assistant/xmpp.mdwn view
@@ -11,10 +11,6 @@ See <http://git-annex.branchable.com/design/assistant/blog/day_114__xmpp/#comment-aaba579f92cb452caf26ac53071a6788> * Assistant.Sync.manualPull doesn't handle XMPP remotes yet. This is needed to handle getting back in sync after reconnection.-* When pairing, sometimes both sides start to push, and the other side- sends a PushRequest, and the two deadlock, neither doing anything.- (Timeout eventually breaks this.)- Maybe should allow one push and one receive-pack at a time? ## design goals
+ doc/forum/Building_git-annex-3.20121112-19309.mdwn view
@@ -0,0 +1,78 @@+Hi,++I have Problems building git-annex-3.20121112-19309, I rceive the following error:++...+ Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libmounts.o ... done+ final link ... done++ Assistant/Alert.hs:66:30:+ Warning: default newline style has changed, using an explicit $newline is recommended++ Assistant/Alert.hs:69:31:+ Warning: default newline style has changed, using an explicit $newline is recommended+ [157 of 279] Compiling Assistant.Types.DaemonStatus ( Assistant/Types/DaemonStatus.hs, dist/build/git-annex/git-annex-tmp/Assistant/Types/DaemonStatus.o )+ [158 of 279] Compiling Assistant.Monad ( Assistant/Monad.hs, dist/build/git-annex/git-annex-tmp/Assistant/Monad.o )++ Assistant/Monad.hs:86:16:+ Couldn't match expected type `Assistant a'+ with actual type `Reader AssistantData a'+ Expected type: (AssistantData -> a) -> Assistant a+ Actual type: (AssistantData -> a) -> Reader AssistantData a+ In the expression: reader+ In an equation for `getAssistant': getAssistant = reader++ Assistant/Monad.hs:93:15:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: st <- reader threadState+ In the expression:+ do { st <- reader threadState;+ liftIO $ runThreadState st a }++ Assistant/Monad.hs:99:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ liftIO $ io $ runAssistant d a }++ Assistant/Monad.hs:105:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ runAssistant d a }++ Assistant/Monad.hs:110:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ \ v -> runAssistant d $ a v }++ Assistant/Monad.hs:115:14:+ Couldn't match expected type `Assistant t0'+ with actual type `Reader r0 a0'+ In the return type of a call of `reader'+ In a stmt of a 'do' block: d <- reader id+ In the expression:+ do { d <- reader id;+ return $ \ v1 v2 -> runAssistant d (a v1 v2) }++ Assistant/Monad.hs:120:12:+ Couldn't match expected type `Assistant a0'+ with actual type `Reader r0 a1'+ In the return type of a call of `reader'+ In the first argument of `(>>=)', namely `reader v'+ In the expression: reader v >>= liftIO . io+ cabal: Error: some packages failed to install:+ git-annex-3.20121112 failed during the building phase. The exception was:+ ExitFailure 1
+ doc/forum/Default_text__47__html_handler.mdwn view
@@ -0,0 +1,2 @@+I've had to change my default `text/html .html` handler from a text editor to a browser to support the opening sequence of `git annex webapp`. any other way around this? perhaps to have it open the page in the default browser rather than the default text/html handler?+
+ doc/forum/How_to_restore_symlinks.mdwn view
@@ -0,0 +1,1 @@+Somehow the symlinks have vanished from one directory of my repository. How can I restore them?
+ doc/forum/Push__47__Pull_with_the_Assistant.mdwn view
@@ -0,0 +1,1 @@+If I use git-annex with a centralized bare git repository as [described here](http://git-annex.branchable.com/tips/centralized_git_repository_tutorial/), will the Assistant automatically `git push` and `git pull` the master and git-annex branches? Or does it basically just do a `git annex sync`?
+ doc/forum/Setup_of_rsync_special_remote_with_non-standard_ssh_port.mdwn view
@@ -0,0 +1,13 @@+I want to setup a rsync special remote on my server with a non-standard ssh port.++I tried the following steps:++ git annex initremote rsync-encrypted type=rsync rsyncurl=1.2.3.4:/encrypted-annex encryption=AAAAAAAAA+ git config remote.rsync-encrypted.annex-rsync-options "-e \'ssh -p 443\'"++But I just get this error:++ [2012-11-22 21:04:30 CET] read: rsync ["-e","'ssh","-p","443'","--progress","--recursive","--partial","-- partial-dir=.rsync-partial","/home/marco/annex/.git/annex/tmp/rsynctmp/15309/","1.2.3.4:/encrypted-annex"]+ Missing trailing-' in remote-shell command.++I tried some ways to escape the config but I don't have a clue. Anybody?
+ doc/forum/Special_remote_without_chmod.mdwn view
@@ -0,0 +1,12 @@+Apparently, the tablet computer I'm using (Galaxy Tab 2, non-rooted) does not export the sd cards via mass storage protocol (UMS) any more. In order to be able to use a special remote for my mp3-files, I installed an ftp/ssh server on the tablet device and used curlftpfs/sshfs (fuse file systems) to mount the sd cards on my local Linux machine. In this way, it is is quite easy to setup the special remote (e.g. "git-annex initremote galaxy-tab type=directory directory=$HOME/mnt/galaxy-tab encryption=none"). ++Problems arise, when files are transferred to the special remote. From the logs of the ftp server, I can see that the actual copy operation is successful (the data is written to the file system), but the subsequent "chmod" that changes the permissions to read-only fails. The output on the console of a "git-annex copy -t galaxy-tab" is ++ copy 01 some.mp3 (to galaxy-tab...) + failed + git-annex: copy: 1 failed+++Therefore my question: is it possible to perform a file transfer to a special remote (type=directory) without the final "chmod"-operation?++Thank you.
+ doc/forum/get_and_copy_with_bare_repositories.mdwn view
@@ -0,0 +1,7 @@+is `git annex get` and `git annex copy --to somewhere` expected to work with bare repos? the [[bare repositories]] page doesn't indicate otherwise, but a `git annex get` does plain nothing in my setup.++if it's supposed not to work, there should be an error message saying that and an indication on the [[bare repositories]], otherwise, how can i trace it down?++in case it is just unimplemented for lack of use cases: my setup consists of several laptops using parts of a 200gb+ photo collection, a central trusted server that should host everything, and an external encrypted remote backup. clients *should* copy everything they add to both central locations, but i'd prefer the trusted server to sync the two of them too.++`get` and `copy` usually operate on the current directory, which in case of a bare repo does not contain any relevant files, but i tried explicitly specifying files too. `git annex` should either look them up in master, or always operate on all files (as indexed in the `git-annex` branch) unconditionally.
doc/git-annex.mdwn view
@@ -568,6 +568,17 @@ The repository should be specified using the name of a configured remote, or the UUID or description of a repository. +* --trust-glacier-inventory++ Amazon Glacier inventories take hours to retrieve, and may not represent+ the current state of a repository. So git-annex does not trust that+ files that the inventory claims are in Glacier are really there.+ This switch can be used to allow it to trust the inventory.++ Be careful using this, especially if you or someone else might have recently+ removed a file from Glacier. If you try to drop the only other copy of the+ file, and this switch is enabled, you could lose data! + * --backend=name Specifies which key-value backend to use. This can be used when@@ -864,26 +875,36 @@ * `remote.<name>.rsyncurl` Used by rsync special remotes, this configures- the location of the rsync repository to use. Normally this is automaticaly+ the location of the rsync repository to use. Normally this is automatically set up by `git annex initremote`, but you can change it if needed. * `remote.<name>.buprepo` Used by bup special remotes, this configures- the location of the bup repository to use. Normally this is automaticaly+ the location of the bup repository to use. Normally this is automatically set up by `git annex initremote`, but you can change it if needed. * `remote.<name>.directory` Used by directory special remotes, this configures the location of the directory where annexed files are stored for this- remote. Normally this is automaticaly set up by `git annex initremote`,+ remote. Normally this is automatically set up by `git annex initremote`, but you can change it if needed. * `remote.<name>.s3` Used to identify Amazon S3 special remotes.- Normally this is automaticaly set up by `git annex initremote`.+ Normally this is automatically set up by `git annex initremote`.++* `remote.<name>.glacier`++ Used to identify Amazon Glacier special remotes.+ Normally this is automatically set up by `git annex initremote`.++* `remote.<name>.webdav`++ Used to identify webdav special remotes.+ Normally this is automatically set up by `git annex initremote`. * `remote.<name>.annex-xmppaddress`
+ doc/install/Debian/comment_5_38e6399083e10a6a274f35bddc15d4ac._comment view
@@ -0,0 +1,18 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawk3eiQwrpDGJ3MJb9NWB84m4tzQ6XjVZnY"+ nickname="Allard"+ subject="wheezy support"+ date="2012-11-23T20:47:58Z"+ content="""+Hey Joey,++As a backer, I'd like to see a backport of git annex assistant to wheezy.++It is currently impossible to get this assistant in wheezy without compiling it with cabal.++It would be nice to see it in backports or something :)++Best,++Allard+"""]]
+ doc/install/Debian/comment_6_2e7bbdbaabbfb9d89de22e913066e822._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://svend.ciffer.net/"+ ip="2001:4978:f:52e::2"+ subject="comment 6"+ date="2012-11-23T21:38:29Z"+ content="""+The git-annex packages in unstable install on testing (wheezy).+"""]]
+ doc/install/OSX/comment_10_4d15bfc4fc26e7249953bebfbb09e0aa._comment view
@@ -0,0 +1,11 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawkp-1EQboBDqZ05MxOHNkwNQDM4luWYioA"+ nickname="Charles"+ subject="comment 10"+ date="2012-11-15T13:26:57Z"+ content="""+Installing it with brew, I had to do the following steps before the final `cabal` command:++* `cabal install c2hs`+* add `$HOME/.cabal/bin` to my `$PATH` (so that c2hs program can be found)+"""]]
+ doc/install/OSX/comment_9_c6b1b31d16f2144ad08abd8c767b6ab9._comment view
@@ -0,0 +1,23 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawnBEsNDl_6O4rHb2en3I0-fg-6fUxglaRQ"+ nickname="chee"+ subject="Recent install for OS X"+ date="2012-11-13T04:40:05Z"+ content="""+if you are having trouble installing with `cabal install git-annex` at the moment, trouble of the XML kind, you'll need to do a couple things:++`brew update`+`brew install libxml2`+`cabal update`+`cabal install libxml --extra-include-dirs=/usr/local/Cellar/libxml2/2.8.0/include/libxml2 --extra-lib-dirs=/usr/local/Cellar/libxml2/2.8.0/lib`++well, then i hit a brick wall.++well.++I got it to work by manually symlinking from `../Cellar/libxml2/2.8.0/lib/`* into `/usr/local` and from `../../Cellar/libxml2/2.8.0/lib/` to `/usr/local/pkgconfig`, but i can't recommend it or claim to be too proud about it all.++OS X already has an old libxml knocking around so this might ruin everything for me.++let's find out !+"""]]
+ doc/install/comment_2_fd560811c57df5cbc3976639642b8b19._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawkN91jAhoesnVI9TtWANaBPaYjd1V9Pag8"+ nickname="Benjamin"+ subject="Package for older OS X"+ date="2012-11-17T12:36:45Z"+ content="""+Is there an option to provide application bundle for older versions of OS X? The last time I tried the bundle wouldn't work under 10.5. If no specific features from newer OS X versions are required, it could be enough to add a simple switch when building.+"""]]
+ doc/install/comment_3_08613b2e2318680508483d204a43da76._comment view
@@ -0,0 +1,75 @@+[[!comment format=mdwn+ username="http://edheil.wordpress.com/"+ nickname="edheil"+ subject="No luck running it on OS X Lion."+ date="2012-11-21T06:07:55Z"+ content="""+here's the crash info:++Process: git-annex [84369]+Path: /Applications/git-annex.app/Contents/MacOS/bin/git-annex+Identifier: git-annex+Version: ??? (???)+Code Type: X86-64 (Native)+Parent Process: sh [84364]++Date/Time: 2012-11-21 00:27:03.068 -0500+OS Version: Mac OS X 10.7.5 (11G63)+Report Version: 9++Crashed Thread: 0++Exception Type: EXC_BREAKPOINT (SIGTRAP)+Exception Codes: 0x0000000000000002, 0x0000000000000000++Application Specific Information:+dyld: launch, loading dependent libraries++Dyld Error Message:+ Library not loaded: /opt/local/lib/libgss.3.dylib+ Referenced from: /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libgsasl.7.dylib+ Reason: image not found++Binary Images:+ 0x105baa000 - 0x107b89fe7 +git-annex (??? - ???) <45311C82-015C-3F87-9F9B-01325EFBD0D9> /Applications/git-annex.app/Contents/MacOS/bin/git-annex+ 0x10822d000 - 0x10823eff7 +libz.1.dylib (1.2.7 - compatibility 1.0.0) <57016CC1-AD54-337E-A983-457933B24D35> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libz.1.dylib+ 0x108245000 - 0x10827dff7 +libpcre.1.dylib (2.1.0 - compatibility 2.0.0) <431BD758-FA7B-38B3-AB7E-6511EC06152E> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libpcre.1.dylib+ 0x108283000 - 0x1083b3ff7 +libxml2.2.dylib (11.0.0 - compatibility 11.0.0) <0663F820-D436-3304-B12F-9158901087EB> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libxml2.2.dylib+ 0x1083e9000 - 0x108400fef +libgsasl.7.dylib (16.6.0 - compatibility 16.0.0) <41503EE1-D58B-385C-AC2E-BEAA7D0D4E38> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libgsasl.7.dylib+ 0x10840a000 - 0x1084a1fff +libgnutls.26.dylib (49.3.0 - compatibility 49.0.0) <0320352A-3336-3B6B-A7DE-F3069669AD27> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libgnutls.26.dylib+ 0x1084c3000 - 0x1084f1ff7 +libidn.11.dylib (18.8.0 - compatibility 18.0.0) <97073970-9370-3F85-B943-1B989EA41148> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libidn.11.dylib+ 0x1084fc000 - 0x1085f5ff7 +libiconv.2.dylib (8.1.0 - compatibility 8.0.0) <1B8D243B-F617-301E-97B1-EE78A72617AB> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libiconv.2.dylib+ 0x108606000 - 0x108606fff +libcharset.1.dylib (2.0.0 - compatibility 2.0.0) <E3797413-2AA3-3698-B393-E1203B4799A0> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libcharset.1.dylib+ 0x10860c000 - 0x108665fef +libgmp.10.dylib (11.5.0 - compatibility 11.0.0) <EE407B22-0F44-38B6-9937-10CA6A529F37> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/libgmp.10.dylib+ 0x108675000 - 0x1086a2fe7 +libSystem.B.dylib (159.1.0 - compatibility 1.0.0) <7BEBB139-50BB-3112-947A-F4AA168F991C> /Applications/git-annex.app/Contents/MacOS/usr/lib/libSystem.B.dylib+ 0x1086b4000 - 0x1086c8fef +libgcc_s.1.dylib (??? - ???) <3C5BF0B8-B1E9-3B41-B52F-F7499687217C> /Applications/git-annex.app/Contents/MacOS/opt/local/lib/gcc47/libgcc_s.1.dylib+ 0x1086d8000 - 0x1086f5ff7 +liblzma.5.dylib (6.4.0 - compatibility 6.0.0) <1D682E06-EB89-34CA-855A-AEF611C4DF86> /usr/local/lib/liblzma.5.dylib+ 0x7fff657aa000 - 0x7fff657debaf dyld (195.6 - ???) <0CD1B35B-A28F-32DA-B72E-452EAD609613> /usr/lib/dyld+ 0x7fff8b669000 - 0x7fff8b672ff7 libsystem_notify.dylib (80.1.0 - compatibility 1.0.0) <A4D651E3-D1C6-3934-AD49-7A104FD14596> /usr/lib/system/libsystem_notify.dylib+ 0x7fff8b6e4000 - 0x7fff8b6e5ff7 libsystem_sandbox.dylib (??? - ???) <2A09E4DA-F47C-35CB-B70C-E0492BA9F20E> /usr/lib/system/libsystem_sandbox.dylib+ 0x7fff8c000000 - 0x7fff8c006ff7 libunwind.dylib (30.0.0 - compatibility 1.0.0) <1E9C6C8C-CBE8-3F4B-A5B5-E03E3AB53231> /usr/lib/system/libunwind.dylib+ 0x7fff8c1c4000 - 0x7fff8c1c5ff7 libremovefile.dylib (21.1.0 - compatibility 1.0.0) <739E6C83-AA52-3C6C-A680-B37FE2888A04> /usr/lib/system/libremovefile.dylib+ 0x7fff8cf13000 - 0x7fff8cf4efff libsystem_info.dylib (??? - ???) <35F90252-2AE1-32C5-8D34-782C614D9639> /usr/lib/system/libsystem_info.dylib+ 0x7fff8dbc3000 - 0x7fff8dbc8fff libcache.dylib (47.0.0 - compatibility 1.0.0) <1571C3AB-BCB2-38CD-B3B2-C5FC3F927C6A> /usr/lib/system/libcache.dylib+ 0x7fff8dbc9000 - 0x7fff8dbd0fff libcopyfile.dylib (85.1.0 - compatibility 1.0.0) <0AB51EE2-E914-358C-AC19-47BC024BDAE7> /usr/lib/system/libcopyfile.dylib+ 0x7fff8dbdf000 - 0x7fff8dbedfff libdispatch.dylib (187.10.0 - compatibility 1.0.0) <8E03C652-922A-3399-93DE-9EA0CBFA0039> /usr/lib/system/libdispatch.dylib+ 0x7fff8dcf2000 - 0x7fff8dcf7ff7 libsystem_network.dylib (??? - ???) <5DE7024E-1D2D-34A2-80F4-08326331A75B> /usr/lib/system/libsystem_network.dylib+ 0x7fff8e1bb000 - 0x7fff8e298fef libsystem_c.dylib (763.13.0 - compatibility 1.0.0) <41B43515-2806-3FBC-ACF1-A16F35B7E290> /usr/lib/system/libsystem_c.dylib+ 0x7fff8e6e2000 - 0x7fff8e6eafff libsystem_dnssd.dylib (??? - ???) <584B321E-5159-37CD-B2E7-82E069C70AFB> /usr/lib/system/libsystem_dnssd.dylib+ 0x7fff8fab6000 - 0x7fff8fab8fff libquarantine.dylib (36.7.0 - compatibility 1.0.0) <8D9832F9-E4A9-38C3-B880-E5210B2353C7> /usr/lib/system/libquarantine.dylib+ 0x7fff8fc3e000 - 0x7fff8fc80ff7 libcommonCrypto.dylib (55010.0.0 - compatibility 1.0.0) <BB770C22-8C57-365A-8716-4A3C36AE7BFB> /usr/lib/system/libcommonCrypto.dylib+ 0x7fff90fa3000 - 0x7fff90fa9fff libmacho.dylib (800.0.0 - compatibility 1.0.0) <165514D7-1BFA-38EF-A151-676DCD21FB64> /usr/lib/system/libmacho.dylib+ 0x7fff90faa000 - 0x7fff90fabfff libunc.dylib (24.0.0 - compatibility 1.0.0) <337960EE-0A85-3DD0-A760-7134CF4C0AFF> /usr/lib/system/libunc.dylib+ 0x7fff910b4000 - 0x7fff910b8fff libmathCommon.A.dylib (2026.0.0 - compatibility 1.0.0) <FF83AFF7-42B2-306E-90AF-D539C51A4542> /usr/lib/system/libmathCommon.A.dylib+ 0x7fff916b9000 - 0x7fff916bdfff libdyld.dylib (195.6.0 - compatibility 1.0.0) <FFC59565-64BD-3B37-90A4-E2C3A422CFC1> /usr/lib/system/libdyld.dylib+ 0x7fff916be000 - 0x7fff916defff libsystem_kernel.dylib (1699.32.7 - compatibility 1.0.0) <66C9F9BD-C7B3-30D4-B1A0-03C8A6392351> /usr/lib/system/libsystem_kernel.dylib+ 0x7fff916df000 - 0x7fff916e0fff libdnsinfo.dylib (395.11.0 - compatibility 1.0.0) <853BAAA5-270F-3FDC-B025-D448DB72E1C3> /usr/lib/system/libdnsinfo.dylib+ 0x7fff929f8000 - 0x7fff929fdfff libcompiler_rt.dylib (6.0.0 - compatibility 1.0.0) <98ECD5F6-E85C-32A5-98CD-8911230CB66A> /usr/lib/system/libcompiler_rt.dylib+ 0x7fff93a3c000 - 0x7fff93a3cfff libkeymgr.dylib (23.0.0 - compatibility 1.0.0) <61EFED6A-A407-301E-B454-CD18314F0075> /usr/lib/system/libkeymgr.dylib+ 0x7fff97139000 - 0x7fff9713aff7 libsystem_blocks.dylib (53.0.0 - compatibility 1.0.0) <8BCA214A-8992-34B2-A8B9-B74DEACA1869> /usr/lib/system/libsystem_blocks.dylib+ 0x7fff9724f000 - 0x7fff9726cfff libxpc.dylib (77.19.0 - compatibility 1.0.0) <9F57891B-D7EF-3050-BEDD-21E7C6668248> /usr/lib/system/libxpc.dylib+ 0x7fff97cfe000 - 0x7fff97d08ff7 liblaunch.dylib (392.39.0 - compatibility 1.0.0) <8C235D13-2928-30E5-9E12-2CC3D6324AE2> /usr/lib/system/liblaunch.dylib++ ++"""]]
doc/install/fromscratch.mdwn view
@@ -3,6 +3,7 @@ * Haskell stuff * [The Haskell Platform](http://haskell.org/platform/) (GHC 7.4 or newer)+ * [mtl](http://hackage.haskell.org.package/mtl) (2.1.1 or newer) * [MissingH](http://github.com/jgoerzen/missingh/wiki) * [pcre-light](http://hackage.haskell.org/package/pcre-light) * [utf8-string](http://hackage.haskell.org/package/utf8-string)@@ -18,8 +19,8 @@ * [bloomfilter](http://hackage.haskell.org/package/bloomfilter) * [edit-distance](http://hackage.haskell.org/package/edit-distance) * [hS3](http://hackage.haskell.org/package/hS3) (optional)+ * [DAV](http://hackage.haskell.org/package/DAV) (optional) * [SafeSemaphore](http://hackage.haskell.org/package/SafeSemaphore)- * [async](http://hackage.haskell.org/package/async) * Optional haskell stuff, used by the [[assistant]] and its webapp (edit Makefile to disable) * [stm](http://hackage.haskell.org/package/stm) (version 2.3 or newer)
− doc/news/version_3.20121009.mdwn
@@ -1,25 +0,0 @@-git-annex 3.20121009 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * watch, assistant: It's now safe to git annex unlock files while- the watcher is running, as well as modify files checked into git- as normal files. Additionally, .gitignore settings are now honored.- Closes: #[689979](http://bugs.debian.org/689979)- * group, ungroup: New commands to indicate groups of repositories.- * webapp: Adds newly created repositories to one of these groups:- clients, drives, servers- * vicfg: New command, allows editing (or simply viewing) most- of the repository configuration settings stored in the git-annex branch.- * Added preferred content expressions, configurable using vicfg.- * get --auto: If the local repository has preferred content- configured, only get that content.- * drop --auto: If the repository the content is dropped from has- preferred content configured, drop only content that is not preferred.- * copy --auto: Only transfer content that the destination repository prefers.- * assistant: Now honors preferred content settings when deciding what to- transfer.- * --copies=group:number can now be used to match files that are present- in a specified number of repositories in a group.- * Added --smallerthan, --largerthan, and --inall limits.- * Only build-depend on libghc-clientsession-dev on arches that will have- the webapp.- * uninit: Unset annex.version. Closes: #[689852](http://bugs.debian.org/689852)"""]]
+ doc/news/version_3.20121126.mdwn view
@@ -0,0 +1,27 @@+git-annex 3.20121126 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * New webdav and Amazon glacier special remotes.+ * Display a warning when a non-existing file or directory is specified.+ * webapp: Added configurator for Box.com.+ * webapp: Show error messages to user when testing XMPP creds.+ * Fix build of assistant without yesod.+ * webapp: The list of repositiories refreshes when new repositories are+ added, including when new repository configurations are pushed in from+ remotes.+ * OSX: Fix RunAtLoad value in plist file.+ * Getting a file from chunked directory special remotes no longer buffers+ it all in memory.+ * S3: Added progress display for uploading and downloading.+ * directory special remote: Made more efficient and robust.+ * Bugfix: directory special remote could loop forever storing a key+ when a too small chunksize was configured.+ * Allow controlling whether login credentials for S3 and webdav are+ committed to the repository, by setting embedcreds=yes|no when running+ initremote.+ * Added smallarchive repository group, that only archives files that are+ in archive directories. Used by default for glacier when set up in the+ webapp.+ * assistant: Fixed handling of toplevel archive directory and+ client repository group.+ * assistant: Apply preferred content settings when a new symlink+ is created, or a symlink gets renamed. Made archive directories work."""]]
doc/preferred_content.mdwn view
@@ -92,7 +92,7 @@ All content is preferred, unless it's in a "archive" directory. -`exclude=*/archive/*`+`exclude=*/archive/* and exclude=archive/*` ### transfer @@ -104,7 +104,7 @@ The preferred content expression for these causes them to get and retain data until all clients have a copy. -`not (inallgroup=client and copies=client:2) and exclude=*/archive/*`+`not (inallgroup=client and copies=client:2) and exclude=*/archive/* and exclude=archive/*` The "copies=client:2" part of the above handles the case where there is only one client repository. It makes a transfer repository@@ -112,17 +112,24 @@ has nowhere to transfer it to. Presumably, another client repository will be added later. -### archive+### backup +All content is preferred.++### small archive++Only prefers content that's located in an "archive" directory, and+only if it's not already been archived somewhere else.++`(include=*/archive/* or include=archive/*) and not (copies=archive:1 or copies=smallarchive:1)`++### full archive+ All content is preferred, unless it's already been archived somewhere else. -`not copies=archive:1`+`not (copies=archive:1 or copies=smallarchive:1)` Note that if you want to archive multiple copies (not a bad idea!), you should instead configure all your archive repositories with a version of the above preferred content expression with a larger number of copies.--### backup--All content is preferred.
doc/special_remotes.mdwn view
@@ -8,9 +8,11 @@ They cannot be used by other git commands though. * [[S3]] (Amazon S3, and other compatible services)+* [[Amazon_Glacier|glacier]] * [[bup]] * [[directory]] * [[rsync]]+* [[webdav]] * [[web]] * [[hook]] @@ -19,6 +21,7 @@ for various cloud things: * [[tips/using_Amazon_S3]]+* [[tips/using_Amazon_Glacier]] * [[tips/Internet_Archive_via_S3]] * [[tahoe-lafs|forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs]] * [[tips/using_box.com_as_a_special_remote]]
doc/special_remotes/S3.mdwn view
@@ -8,21 +8,29 @@ The standard environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are used to supply login credentials-for Amazon. When encryption is enabled, they are stored in encrypted form-by `git annex initremote`. Without encryption, they are stored in a-file only you can read inside the local git repository. So you do not-need to keep the environment variables set after the initial-initalization of the remote.+for Amazon. You need to set these only when running+`git annex initremote`, as they will be cached in a file only you+can read inside the local git repository. A number of parameters can be passed to `git annex initremote` to configure the S3 remote. -* `encryption` - Required. Either "none" to disable encryption- (not recommended),+* `encryption` - Required. Either "none" to disable encryption (not recommended), or a value that can be looked up (using gpg -k) to find a gpg encryption- key that will be given access to the remote. Note that additional gpg- keys can be given access to a remote by rerunning initremote with- the new key id. See [[encryption]].+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to access the encrypted data (use with caution).++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]].++* `embedcreds` - Optional. Set to "yes" embed the login credentials inside+ the git repository, which allows other clones to also access them. This is+ the default when gpg encryption is enabled; the credentials are stored+ encrypted and only those with the repository's keys can access them.++ It is not the default when using shared encryption, or no encryption.+ Think carefully about who can access your repository before using+ embedcreds without gpg encryption. * `datacenter` - Defaults to "US". Other values include "EU", "us-west-1", and "ap-southeast-1".
doc/special_remotes/bup.mdwn view
@@ -22,9 +22,11 @@ * `encryption` - Required. Either "none" to disable encryption of content stored in bup (ssh will still be used to transport it securely), or a value that can be looked up (using gpg -k) to find a gpg encryption- key that will be given access to the remote. Note that additional gpg- keys can be given access to a remote by rerunning initremote with- the new key id. See [[encryption]].+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to access the encrypted data (use with caution).++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]]. * `buprepo` - Required. This is passed to `bup` as the `--remote` to use to store data. To create the repository,`bup init` will be run.
doc/special_remotes/directory.mdwn view
@@ -10,12 +10,14 @@ These parameters can be passed to `git annex initremote` to configure the remote: -* `encryption` - Required. Either "none" to disable encryption of content- stored in the directory,+* `encryption` - Required. Either "none" to disable encryption, or a value that can be looked up (using gpg -k) to find a gpg encryption- key that will be given access to the remote. Note that additional gpg- keys can be given access to a remote by rerunning initremote with- the new key id. See [[encryption]].+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to decrypt the encrypted data.++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]].+ * `chunksize` - Avoid storing files larger than the specified size in the directory. For use on directories on mount points that have file size limitations. The default is to never chunk files.
+ doc/special_remotes/glacier.mdwn view
@@ -0,0 +1,50 @@+This special remote type stores file contents in Amazon Glacier.++To use it, you need to have [glacier-cli](http://github.com/basak/glacier-cli)+installed.++The unusual thing about Amazon Glacier is the multiple-hour delay it takes+to retrieve information out of Glacier. To deal with this, commands like+"git-annex get" request Glacier start the retrieval process, and will fail+due to the data not yet being available. You can then wait appriximately +four hours, re-run the same command, and this time, it will actually+download the data.++## configuration++The standard environment variables `AWS_ACCESS_KEY_ID` and +`AWS_SECRET_ACCESS_KEY` are used to supply login credentials+for Amazon. You need to set these only when running+`git annex initremote`, as they will be cached in a file only you+can read inside the local git repository.++A number of parameters can be passed to `git annex initremote` to configure+the Glacier remote.++* `encryption` - Required. Either "none" to disable encryption (not recommended),+ or a value that can be looked up (using gpg -k) to find a gpg encryption+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to access the encrypted data (use with caution).++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]].++* `embedcreds` - Optional. Set to "yes" embed the login credentials inside+ the git repository, which allows other clones to also access them. This is+ the default when gpg encryption is enabled; the credentials are stored+ encrypted and only those with the repository's keys can access them.++ It is not the default when using shared encryption, or no encryption.+ Think carefully about who can access your repository before using+ embedcreds without gpg encryption.++* `datacenter` - Defaults to "us-east-1".++* `vault` - By default, a vault name is chosen based on the remote name+ and UUID. This can be specified to pick a vault name.++* `fileprefix` - By default, git-annex places files in a tree rooted at the+ top of the Glacier vault. When this is set, it's prefixed to the filenames+ used. For example, you could set it to "foo/" in one special remote,+ and to "bar/" in another special remote, and both special remotes could+ then use the same vault.
doc/special_remotes/hook.mdwn view
@@ -25,11 +25,13 @@ These parameters can be passed to `git annex initremote`: -* `encryption` - Required. Either "none" to disable encryption of content,+* `encryption` - Required. Either "none" to disable encryption, or a value that can be looked up (using gpg -k) to find a gpg encryption- key that will be given access to the remote. Note that additional gpg- keys can be given access to a remote by rerunning initremote with- the new key id. See [[encryption]].+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to access the encrypted data.++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]]. * `hooktype` - Required. This specifies a collection of hooks to use for this remote.
doc/special_remotes/rsync.mdwn view
@@ -15,11 +15,13 @@ These parameters can be passed to `git annex initremote` to configure rsync: * `encryption` - Required. Either "none" to disable encryption of content- stored in rsync, - or a value that can be looked up (using `gpg -k`) to find a gpg encryption- key that will be given access to the remote. Note that additional gpg- keys can be given access to a remote by rerunning initremote with- the new key id. See [[encryption]].+ stored on the remote rsync server,+ or a value that can be looked up (using gpg -k) to find a gpg encryption+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to decrypt the encrypted data.++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]]. * `rsyncurl` - Required. This is the url or `hostname:/directory` to pass to rsync to tell it where to store content.
+ doc/special_remotes/webdav.mdwn view
@@ -0,0 +1,45 @@+This special remote type stores file contents in a WebDAV server.++## configuration++The environment variables `WEBDAV_USERNAME` and `WEBDAV_PASSWORD` are used+to supply login credentials. You need to set these only when running+`git annex initremote`, as they will be cached in a file only you +can read inside the local git repository.++A number of parameters can be passed to `git annex initremote` to configure+the webdav remote.++* `encryption` - Required. Either "none" to disable encryption (not recommended),+ or a value that can be looked up (using gpg -k) to find a gpg encryption+ key that will be given access to the remote, or "shared" which allows+ every clone of the repository to access the encrypted data (use with caution).++ Note that additional gpg keys can be given access to a remote by+ rerunning initremote with the new key id. See [[encryption]].++* `embedcreds` - Optional. Set to "yes" embed the login credentials inside+ the git repository, which allows other clones to also access them. This is+ the default when gpg encryption is enabled; the credentials are stored+ encrypted and only those with the repository's keys can access them.++ It is not the default when using shared encryption, or no encryption.+ Think carefully about who can access your repository before using+ embedcreds without gpg encryption.++* `url` - Required. The URL to the WebDAV directory where files will be+ stored. This can be a subdirectory of a larger WebDAV repository, and will+ be created as needed. Use of a https URL is strongly+ encouraged, since HTTP basic authentication is used.++* `chunksize` - Avoid storing files larger than the specified size in+ WebDAV. For use when the WebDAV server has file size+ limitations. The default is to never chunk files. + The value can use specified using any commonly used units.+ Example: `chunksize=75 megabytes` + Note that enabling chunking on an existing remote with non-chunked+ files is not recommended.++Setup example:++ # WEBDAV_USERNAME=joey@kitenet.net WEBDAV_PASSWORD=xxxxxxx git annex initremote box.com type=webdav url=https://www.box.com/dav/git-annex chunksize=75mb encryption=joey@kitenet.net
+ doc/tips/using_Amazon_Glacier.mdwn view
@@ -0,0 +1,75 @@+Amazon Glacier provides low-cost storage, well suited for archiving and+backup. But it takes around 4 hours to get content out of Glacier.++Recent versions of git-annex support Glacier. To use it, you need to have+[glacier-cli](http://github.com/basak/glacier-cli) installed.++First, export your Amazon AWS credentials:++ # export AWS_ACCESS_KEY_ID="08TJMT99S3511WOZEP91"+ # export AWS_SECRET_ACCESS_KEY="s3kr1t"++Now, create a gpg key, if you don't already have one. This will be used+to encrypt everything stored in Glacier, for your privacy. Once you have+a gpg key, run `gpg --list-secret-keys` to look up its key id, something+like "2512E3C7"++Next, create the Glacier remote.++ # git annex initremote glacier type=glacier encryption=2512E3C7+ initremote glacier (encryption setup with gpg key C910D9222512E3C7) (gpg) ok++The configuration for the Glacier remote is stored in git. So to make another+repository use the same Glacier remote is easy:++ # cd /media/usb/annex+ # git pull laptop+ # git annex initremote glacier+ initremote glacier (gpg) ok++Now the remote can be used like any other remote.++ # git annex move my_cool_big_file --to glacier+ copy my_cool_big_file (gpg) (checking glacier...) (to glacier...) ok++But, when you try to get a file out of Glacier, it'll queue a retrieval+job:++ # git annex get my_cool_big_file+ get my_cool_big_file (from glacier...) (gpg)+ glacier: queued retrieval job for archive 'GPGHMACSHA1--862afd4e67e3946587a9ef7fa5beb4e8f1aeb6b8'+ Recommend you wait up to 4 hours, and then run this command again.+ failed++Like it says, you'll need to run the command again later. Let's remember to+do that:++ # at now + 4 hours+ at> git annex get my_cool_big_file++Another oddity of Glacier is that git-annex is never entirely sure+if a file is still in Glacier. Glacier inventories take hours to retrieve,+and even when retrieved do not necessarily represent the current state.++So, git-annex plays it safe, and avoids trusting the inventory:++ # git annex copy important_file --to glacier+ copy important_file (gpg) (checking glacier...) (to glacier...) ok+ # git annex drop important_file+ drop important_file (gpg) (checking glacier...)+ Glacier's inventory says it has a copy.+ However, the inventory could be out of date, if it was recently removed.+ (Use --trust-glacier if you're sure it's still in Glacier.)+ + (unsafe) + Could only verify the existence of 0 out of 1 necessary copies++Like it says, you can use `--trust-glacier` if you're sure+Glacier's inventory is correct and up-to-date.++A final potential gotcha with Glacier is that glacier-cli keeps a local+mapping of file names to Glacier archives. If this cache is lost, or+you want to retrieve files on a different box than the one that put them in+glacier, you'll need to use `glacier vault sync` to rebuild this cache.++See [[special_remotes/Glacier]] for details.
doc/tips/using_Amazon_S3.mdwn view
@@ -2,7 +2,7 @@ are not git repositories. This way you can set up a remote using say, Amazon S3, and use git-annex to transfer files into the cloud. -First, export your S3 credentials:+First, export your Amazon AWS credentials: # export AWS_ACCESS_KEY_ID="08TJMT99S3511WOZEP91" # export AWS_SECRET_ACCESS_KEY="s3kr1t"
doc/tips/using_box.com_as_a_special_remote.mdwn view
@@ -2,8 +2,19 @@ for providing 50 gb of free storage if you sign up with its Android client. (Or a few gb free otherwise.) -With a little setup, git-annex can use Box as a-[[special remote|special_remotes]]. +git-annex can use Box as a [[special remote|special_remotes]].+Recent versions of git-annex make this very easy to set up:++ WEBDAV_USERNAME=you@example.com WEBDAV_PASSWORD=xxxxxxx git annex initremote box.com type=webdav url=https://www.box.com/dav/git-annex chunksize=75mb encryption=you@example.com++Note the use of chunksize; Box has a 100 mb maximum file size, and this+breaks up large files into chunks before that limit is reached.++# old davfs2 method++This method is deprecated, but still documented here just in case.+Note that the files stored using this method cannot reliably be retreived+using the webdav special remote. ## davfs2 setup
+ doc/todo/resuming_encrypted_uploads.mdwn view
@@ -0,0 +1,22 @@+Resuming interrupted uploads to encrypted special remotes is not currently+possible, because gpg does not produce consistent output. Special remotes+that could support resuming include rsync and glacier.++Without consistent output, git-annex would need to locally cache the encrypted+file, and reuse that cache when resuming an upload. This would make+encrypted uploads more expensive in terms of both file IO and disk space+used. ++[It would be possible to write to the cache at the same time the special+remote is being fed data, and if the special remote upload fails, continue+writing the rest of the file. That would avoid half the overhead, since+the file would not need to be read from, just written to. (Although OS+caching may accomplish the same thing.)]++Also, `git annex unused` would need to show temp files for uploads,+the same as it currently shows temp files for downloads, and users would+sometimes need to manually dropunused old uploads, that never completed.++The question, then, is whether resuming uploads is useful enough to add+this overhead and user-visible complexity.+--[[Joey]]
doc/todo/special_remote_for_amazon_glacier.mdwn view
@@ -18,8 +18,13 @@ --[[Joey]] +> [[done]]! --[[Joey]]+ ----- > In the coming months, Amazon S3 will introduce an option that will allow customers to seamlessly move data between Amazon S3 and Amazon Glacier based on data lifecycle policies. -- <http://aws.amazon.com/glacier/faqs/#How_should_I_choose_between_Amazon_Glacier_and_Amazon_S3>++>> They did, but it's IMHO not very useful for git-annex. It's rather+>> intended to allow aging S3 storage out to Glacier. --[[Joey]]
git-annex.1 view
@@ -505,6 +505,16 @@ The repository should be specified using the name of a configured remote, or the UUID or description of a repository. .IP+.IP "\-\-trust\-glacier\-inventory"+Amazon Glacier inventories take hours to retrieve, and may not represent+the current state of a repository. So git\-annex does not trust that+files that the inventory claims are in Glacier are really there.+This switch can be used to allow it to trust the inventory.+.IP+Be careful using this, especially if you or someone else might have recently+removed a file from Glacier. If you try to drop the only other copy of the+file, and this switch is enabled, you could lose data! +.IP .IP "\-\-backend=name" Specifies which key\-value backend to use. This can be used when adding a file to the annex, or migrating a file. Once files@@ -754,23 +764,31 @@ .IP .IP "remote.<name>.rsyncurl" Used by rsync special remotes, this configures-the location of the rsync repository to use. Normally this is automaticaly+the location of the rsync repository to use. Normally this is automatically set up by git annex initremote, but you can change it if needed. .IP .IP "remote.<name>.buprepo" Used by bup special remotes, this configures-the location of the bup repository to use. Normally this is automaticaly+the location of the bup repository to use. Normally this is automatically set up by git annex initremote, but you can change it if needed. .IP .IP "remote.<name>.directory" Used by directory special remotes, this configures the location of the directory where annexed files are stored for this-remote. Normally this is automaticaly set up by git annex initremote,+remote. Normally this is automatically set up by git annex initremote, but you can change it if needed. .IP .IP "remote.<name>.s3" Used to identify Amazon S3 special remotes.-Normally this is automaticaly set up by git annex initremote.+Normally this is automatically set up by git annex initremote.+.IP+.IP "remote.<name>.glacier"+Used to identify Amazon Glacier special remotes.+Normally this is automatically set up by git annex initremote.+.IP+.IP "remote.<name>.webdav"+Used to identify webdav special remotes.+Normally this is automatically set up by git annex initremote. .IP .IP "remote.<name>.annex\-xmppaddress" Used to identify the XMPP address of a Jabber buddy.
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 3.20121112+Version: 3.20121126 Cabal-Version: >= 1.8 License: GPL Maintainer: Joey Hess <joey@kitenet.net>@@ -28,6 +28,9 @@ Flag S3 Description: Enable S3 support +Flag WebDAV+ Description: Enable WebDAV support+ Flag Inotify Description: Enable inotify support @@ -52,12 +55,12 @@ Executable git-annex Main-Is: git-annex.hs Build-Depends: MissingH, hslogger, directory, filepath,- unix, containers, utf8-string, network (>= 2.0), mtl,+ unix, containers, utf8-string, network (>= 2.0), mtl (>= 2.1.1), bytestring, old-locale, time, pcre-light, extensible-exceptions, dataenc, SHA, process, json, HTTP, base (>= 4.5 && < 4.7), monad-control, transformers-base, lifted-base, IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance, process,- SafeSemaphore, async+ SafeSemaphore -- Need to list these because they're generated from .hsc files. Other-Modules: Utility.Touch Utility.Mounts Include-Dirs: Utility@@ -69,6 +72,10 @@ Build-Depends: hS3 CPP-Options: -DWITH_S3 + if flag(WebDAV)+ Build-Depends: DAV (>= 0.2), http-conduit, xml-conduit+ CPP-Options: -DWITH_WEBDAV+ if flag(Assistant) && ! os(windows) && ! os(solaris) Build-Depends: stm >= 2.3 CPP-Options: -DWITH_ASSISTANT@@ -88,7 +95,7 @@ if flag(Webapp) && flag(Assistant) Build-Depends: yesod, yesod-static, case-insensitive, http-types, transformers, wai, wai-logger, warp, blaze-builder,- crypto-api, hamlet, clientsession,+ crypto-api, hamlet, clientsession, aeson, template-haskell, yesod-default (>= 1.1.0), data-default CPP-Options: -DWITH_WEBAPP @@ -108,10 +115,11 @@ Type: exitcode-stdio-1.0 Main-Is: test.hs Build-Depends: testpack, HUnit, MissingH, hslogger, directory, filepath,- unix, containers, utf8-string, network, mtl, bytestring, old-locale, time,- pcre-light, extensible-exceptions, dataenc, SHA, process, json, HTTP,- base (>= 4.5 && < 4.7), monad-control, transformers-base, lifted-base,- IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance, process+ unix, containers, utf8-string, network, mtl (>= 2.1.1), bytestring,+ old-locale, time, pcre-light, extensible-exceptions, dataenc, SHA,+ process, json, HTTP, base (>= 4.5 && < 4.7), monad-control,+ transformers-base, lifted-base, IfElse, text, QuickCheck >= 2.1,+ bloomfilter, edit-distance, process Other-Modules: Utility.Touch Include-Dirs: Utility C-Sources: Utility/libdiskfree.c
templates/bootstrap.hamlet view
@@ -3,12 +3,12 @@ <head> <title> $maybe reldir <- relDir webapp- #{reldir} #{pageTitle page}+ #{reldir} #{pageTitle pageinfo} $nothing- #{pageTitle page}+ #{pageTitle pageinfo} <link rel="icon" href=@{StaticR favicon_ico} type="image/x-icon"> <meta name="viewport" content="width=device-width,initial-scale=1.0">- ^{pageHead page}+ ^{pageHead pageinfo} <body>- ^{pageBody page}+ ^{pageBody pageinfo} <div #modal></div>
+ templates/configurators/addbox.com.hamlet view
@@ -0,0 +1,26 @@+<div .span9 .hero-unit>+ <h2>+ Adding a Box.com repository+ <p>+ <a href="http://box.com">Box.com</a> offers a small quantity of storage #+ for free, and larger quantities for a fee.+ <p>+ Even a small amount of free storage is useful, as a transfer point #+ between your repositories.+ <p>+ All data will be encrypted before being sent to Box.com.+ <p>+ <form .form-horizontal enctype=#{enctype}>+ <fieldset>+ ^{form}+ ^{webAppFormAuthToken}+ <div .form-actions>+ <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">+ Add repository+<div .modal .fade #workingmodal>+ <div .modal-header>+ <h3>+ Making repository ...+ <div .modal-body>+ <p>+ Setting up your Box.com repository. This could take a minute.
templates/configurators/adddrive.hamlet view
@@ -25,7 +25,7 @@ <form enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <button .btn .btn-primary type=submit onclick="$('#clonemodal').modal('show');">Use this drive</button> # <a .btn href="@{AddDriveR}"> Rescan for removable drives
+ templates/configurators/addglacier.hamlet view
@@ -0,0 +1,40 @@+<div .span9 .hero-unit>+ <h2>+ Adding an Amazon Glacier repository+ <p>+ <a href="http://aws.amazon.com/glacier/">Amazon Glacier</a> is an #+ offline cloud storage provider. It takes several hours for requested #+ files to be retrieved from Glacier, making it mostly suitable for #+ backups and long-term data archival. #+ <a href="http://aws.amazon.com/glacier/pricing/">+ Pricing details+ <p>+ <i .icon-warning-sign></i> By default, only files you place in #+ "archive" directories will be archived in Amazon Glacier. #+ You will be charged by Amazon for data #+ uploaded to Glacier, as well as data downloaded from Glacier, and a #+ monthly fee for data storage.+ <p>+ All data will be encrypted before being sent to Amazon Glacier.+ <p>+ When you sign up to Amazon Glacier, they provide you with an Access #+ Key ID, and a Secret Access Key. You will need to enter both below. #+ These access keys will be stored in a file that only you can #+ access. #+ <a href="https://portal.aws.amazon.com/gp/aws/securityCredentials#id_block">+ Look up your access keys+ <p>+ <form .form-horizontal enctype=#{enctype}>+ <fieldset>+ ^{form}+ ^{webAppFormAuthToken}+ <div .form-actions>+ <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">+ Add Glacier repository+<div .modal .fade #workingmodal>+ <div .modal-header>+ <h3>+ Making repository ...+ <div .modal-body>+ <p>+ Setting up your Amazon Glacier repository. This could take a minute.
templates/configurators/addrsync.net.hamlet view
@@ -28,7 +28,7 @@ <form .form-horizontal enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .form-actions> <button .btn .btn-primary type=submit onclick="$('#testmodal').modal('show');"> Use this rsync.net repository
templates/configurators/adds3.hamlet view
@@ -25,7 +25,7 @@ <form .form-horizontal enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .form-actions> <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');"> Add S3 repository
templates/configurators/editrepository.hamlet view
@@ -16,7 +16,7 @@ <form .form-horizontal enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .form-actions> <button .btn .btn-primary type=submit> Save Changes
+ templates/configurators/enableaws.hamlet view
@@ -0,0 +1,30 @@+<div .span9 .hero-unit>+ <h2>+ Enabling #{description}+ <p>+ To use this Amazon repository, you need an Access Key ID, and a #+ Secret Access Key. These access keys will be stored in a file that #+ only you can access.+ <p>+ If this repository uses your Amazon account, you can #+ <a href="https://portal.aws.amazon.com/gp/aws/securityCredentials#id_block">+ look up your access keys. #+ If this repository uses someone else's Amazon account, they #+ can generate access keys for you, using their #+ <a href="https://console.aws.amazon.com/iam/home">+ IAM Management Console.+ <p>+ <form .form-horizontal enctype=#{enctype}>+ <fieldset>+ ^{form}+ ^{webAppFormAuthToken}+ <div .form-actions>+ <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">+ Enable Amazon repository+<div .modal .fade #workingmodal>+ <div .modal-header>+ <h3>+ Enabling repository ...+ <div .modal-body>+ <p>+ Enabling this Amazon repository. This could take a minute.
− templates/configurators/enables3.hamlet
@@ -1,30 +0,0 @@-<div .span9 .hero-unit>- <h2>- Enabling #{description}- <p>- To use this Amazon S3 repository, you need an Access Key ID, and a #- Secret Access Key. These access keys will be stored in a file that #- only you can access.- <p>- If this repository uses your Amazon S3 account, you can #- <a href="https://portal.aws.amazon.com/gp/aws/securityCredentials#id_block">- look up your access keys. #- If this repository uses someone else's Amazon S3 account, they #- can generate access keys for you, using their #- <a href="https://console.aws.amazon.com/iam/home">- IAM Management Console.- <p>- <form .form-horizontal enctype=#{enctype}>- <fieldset>- ^{form}- ^{authtoken}- <div .form-actions>- <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">- Enable S3 repository-<div .modal .fade #workingmodal>- <div .modal-header>- <h3>- Enabling repository ...- <div .modal-body>- <p>- Enabling this Amazon S3 repository. This could take a minute.
+ templates/configurators/enablewebdav.hamlet view
@@ -0,0 +1,22 @@+<div .span9 .hero-unit>+ <h2>+ Enabling #{description}+ <p>+ This is a WebDAV repository located at #+ <a href="#{url}">+ #{url}+ <p>+ <form .form-horizontal enctype=#{enctype}>+ <fieldset>+ ^{form}+ ^{webAppFormAuthToken}+ <div .form-actions>+ <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">+ Enable repository+<div .modal .fade #workingmodal>+ <div .modal-header>+ <h3>+ Enabling repository ...+ <div .modal-body>+ <p>+ Enabling this WebDAV repository. This could take a minute.
+ templates/configurators/needglaciercli.hamlet view
@@ -0,0 +1,10 @@+<div .span9 .hero-unit>+ <h2>+ Need glacier-cli+ <p>+ To use Amazon Glacier, you need to install #+ <a href="https://github.com/basak/glacier-cli">+ glacier-cli+ <p>+ <a .btn .btn-primary .btn-large href="">+ Retry
templates/configurators/pairing/local/prompt.hamlet view
@@ -29,7 +29,7 @@ <form .form-horizontal enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .form-actions> <button .btn .btn-primary type=submit> $if start
templates/configurators/pairing/xmpp/end.hamlet view
@@ -9,38 +9,23 @@ $nothing A pair request has been sent to all other devices using your jabber # account.- $else- Pair request accepted.- <h2>- Configure a shared cloud repository+ $else+ Pair request accepted.+ <h2>+ Configure a shared cloud repository $maybe name <- friend <p> ☂ To share files with #{name}, you'll need a repository in # the cloud, that you both can access.- $if null cloudrepolist- <hr>- ^{makeCloudRepositories}- $else- <p>- Make sure that #{name} has access to one of these cloud repositories, #- and that the repository is enabled.- ^{repoTable cloudrepolist}- <hr>- Or, add a new cloud repository:- ^{makeCloudRepositories} $nothing <p> ☂ To share files with your other devices, when they're not # nearby, you'll need a repository in the cloud.- $if null cloudrepolist- <hr>- ^{makeCloudRepositories}- $else- <p>- Make sure that your other devices are configured to access one of #- these cloud repositories, and that the repository is enabled here #- too.- ^{repoTable cloudrepolist}- <hr>- Or, add a new cloud repository:- ^{makeCloudRepositories}+ <p>+ Make sure that your other devices are configured to access a #+ cloud repository, and that the same repository is enabled here #+ too.+ ^{cloudrepolist}+ <h2>+ Add a cloud repository+ ^{makeCloudRepositories}
templates/configurators/repositories.hamlet view
@@ -1,7 +1,5 @@ <div .span9>- <h2>- Your repositories- ^{repoTable repolist}+ ^{repolist} <div .row-fluid> <div .span6> <h2>
templates/configurators/repositories/cloud.hamlet view
@@ -1,4 +1,10 @@ <h3>+ <a href="@{AddBoxComR}">+ <i .icon-plus-sign></i> Box.com+<p>+ Provides <b>free</b> cloud storage for small amounts of data.++<h3> <a href="@{AddRsyncNetR}"> <i .icon-plus-sign></i> Rsync.net <p>@@ -8,17 +14,13 @@ <a href="@{AddS3R}"> <i .icon-plus-sign></i> Amazon S3 <p>- Good choice for professional storage quality and low prices.+ Good choice for professional quality storage. <h3>- <i .icon-plus-sign></i> Amazon Glacier+ <a href="@{AddGlacierR}">+ <i .icon-plus-sign></i> Amazon Glacier <p> Low cost offline data archival.--<h3>- <i .icon-plus-sign></i> Box.com-<p>- Provides free cloud storage for small amounts of data. <h3> <a href="@{AddSshR}">
+ templates/configurators/repositories/list.hamlet view
@@ -0,0 +1,36 @@+<div ##{ident}>+ $if onlyCloud reposelector+ $if not (null repolist)+ <h2>+ Cloud repositories+ $else+ No cloud repositories are configured yet.+ $else+ $if not (null repolist)+ <h2>+ Repositories+ <table .table .table-condensed>+ <tbody>+ $forall (num, name, actions) <- repolist+ <tr>+ <td>+ #{num}+ <td>+ #{name}+ <td>+ $if needsEnabled actions+ <a href="@{setupRepoLink actions}">+ <i .icon-warning-sign></i> not enabled+ $else+ <a href="@{syncToggleLink actions}">+ $if notSyncing actions+ <i .icon-pause></i> syncing paused+ $else+ <i .icon-refresh></i> syncing enabled+ <td>+ $if needsEnabled actions+ <a href="@{setupRepoLink actions}">+ enable+ $else+ <a href="@{setupRepoLink actions}">+ configure
− templates/configurators/repositories/table.hamlet
@@ -1,25 +0,0 @@-<table .table .table-condensed>- <tbody>- $forall (num, name, actions) <- repolist- <tr>- <td>- #{num}- <td>- #{name}- <td>- $if needsEnabled actions- <a href="@{setupRepoLink actions}">- <i .icon-warning-sign></i> not enabled- $else- <a href="@{syncToggleLink actions}">- $if notSyncing actions- <i .icon-pause></i> syncing paused- $else- <i .icon-refresh></i> syncing enabled- <td>- $if needsEnabled actions- <a href="@{setupRepoLink actions}">- enable- $else- <a href="@{setupRepoLink actions}">- configure
templates/configurators/ssh/add.hamlet view
@@ -19,7 +19,7 @@ <form .form-horizontal enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .form-actions> <button .btn .btn-primary type=submit onclick="$('#testmodal').modal('show');"> Check this server
templates/configurators/ssh/enable.hamlet view
@@ -18,7 +18,7 @@ <i .icon-warning-sign></i> #{msg} $of _ ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .modal .fade #testmodal> <div .modal-header> <h3>
templates/configurators/xmpp.hamlet view
@@ -9,10 +9,10 @@ It's fine to reuse an existing jabber account; git-annex won't # post any messages to it. <p>- $if problem+ $maybe msg <- problem <i .icon-warning-sign></i> Unable to connect to the Jabber server. #- Maybe you entered the wrong password?- $else+ Maybe you entered the wrong password? (Error message: #{msg})+ $nothing <i .icon-user></I> If you have a Gmail account, you can use # Google Talk. Just enter your full Gmail address # <small>(<tt>you@gmail.com</tt>)</small> #@@ -21,7 +21,7 @@ <form .form-horizontal enctype=#{enctype}> <fieldset> ^{form}- ^{authtoken}+ ^{webAppFormAuthToken} <div .form-actions> <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');"> Use this account