2011-07-01 17:15:46 -04:00
|
|
|
{- git-annex command
|
|
|
|
-
|
|
|
|
- Copyright 2011 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Command.AddUrl where
|
|
|
|
|
|
|
|
import Network.URI
|
|
|
|
|
2011-10-05 16:02:51 -04:00
|
|
|
import Common.Annex
|
2011-07-01 17:15:46 -04:00
|
|
|
import Command
|
2012-06-05 19:51:03 -04:00
|
|
|
import Backend
|
2011-07-01 17:15:46 -04:00
|
|
|
import qualified Command.Add
|
2011-07-01 18:46:07 -04:00
|
|
|
import qualified Annex
|
2013-04-11 13:35:52 -04:00
|
|
|
import qualified Annex.Queue
|
2011-08-06 14:57:22 -04:00
|
|
|
import qualified Backend.URL
|
2012-02-10 19:23:23 -04:00
|
|
|
import qualified Utility.Url as Url
|
2011-10-04 00:40:47 -04:00
|
|
|
import Annex.Content
|
2011-10-15 16:36:56 -04:00
|
|
|
import Logs.Web
|
2012-02-08 15:35:18 -04:00
|
|
|
import qualified Option
|
2012-02-10 19:40:36 -04:00
|
|
|
import Types.Key
|
2012-06-20 16:07:14 -04:00
|
|
|
import Types.KeySource
|
2012-04-22 01:13:09 -04:00
|
|
|
import Config
|
2013-01-06 17:34:44 -04:00
|
|
|
import Annex.Content.Direct
|
2013-04-11 13:35:52 -04:00
|
|
|
import Logs.Location
|
2013-04-11 16:14:17 -04:00
|
|
|
import qualified Logs.Transfer as Transfer
|
|
|
|
import Utility.Daemon (checkDaemon)
|
2011-07-01 17:15:46 -04:00
|
|
|
|
2011-10-29 15:19:05 -04:00
|
|
|
def :: [Command]
|
2013-03-11 19:55:01 -04:00
|
|
|
def = [notBareRepo $ withOptions [fileOption, pathdepthOption, relaxedOption] $
|
2013-03-24 18:28:21 -04:00
|
|
|
command "addurl" (paramRepeating paramUrl) seek
|
|
|
|
SectionCommon "add urls to annex"]
|
2012-02-08 15:35:18 -04:00
|
|
|
|
|
|
|
fileOption :: Option
|
|
|
|
fileOption = Option.field [] "file" paramFile "specify what file the url is added to"
|
2011-07-01 17:15:46 -04:00
|
|
|
|
2012-02-16 12:25:19 -04:00
|
|
|
pathdepthOption :: Option
|
2012-02-16 12:37:30 -04:00
|
|
|
pathdepthOption = Option.field [] "pathdepth" paramNumber "path components to use in filename"
|
2012-02-16 12:25:19 -04:00
|
|
|
|
2013-03-11 19:55:01 -04:00
|
|
|
relaxedOption :: Option
|
|
|
|
relaxedOption = Option.flag [] "relaxed" "skip size check"
|
|
|
|
|
2011-07-01 17:15:46 -04:00
|
|
|
seek :: [CommandSeek]
|
2012-02-08 15:35:18 -04:00
|
|
|
seek = [withField fileOption return $ \f ->
|
2013-03-11 19:55:01 -04:00
|
|
|
withFlag relaxedOption $ \relaxed ->
|
2012-02-16 12:25:19 -04:00
|
|
|
withField pathdepthOption (return . maybe Nothing readish) $ \d ->
|
2013-03-11 19:55:01 -04:00
|
|
|
withStrings $ start relaxed f d]
|
2011-07-01 17:15:46 -04:00
|
|
|
|
2013-03-11 19:55:01 -04:00
|
|
|
start :: Bool -> Maybe FilePath -> Maybe Int -> String -> CommandStart
|
|
|
|
start relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI s
|
2012-11-12 01:05:04 -04:00
|
|
|
where
|
|
|
|
bad = fromMaybe (error $ "bad url " ++ s) $
|
|
|
|
parseURI $ escapeURIString isUnescapedInURI s
|
|
|
|
go url = do
|
|
|
|
let file = fromMaybe (url2file url pathdepth) optfile
|
|
|
|
showStart "addurl" file
|
2013-03-11 19:55:01 -04:00
|
|
|
next $ perform relaxed s file
|
2011-10-31 16:46:51 -04:00
|
|
|
|
2013-03-11 19:55:01 -04:00
|
|
|
perform :: Bool -> String -> FilePath -> CommandPerform
|
|
|
|
perform relaxed url file = ifAnnexed file addurl geturl
|
2012-11-12 01:05:04 -04:00
|
|
|
where
|
|
|
|
geturl = do
|
|
|
|
liftIO $ createDirectoryIfMissing True (parentDir file)
|
2013-03-11 19:55:01 -04:00
|
|
|
ifM (Annex.getState Annex.fast <||> pure relaxed)
|
|
|
|
( nodownload relaxed url file , download url file )
|
2013-03-12 15:58:36 -04:00
|
|
|
addurl (key, _backend)
|
|
|
|
| relaxed = do
|
|
|
|
setUrlPresent key url
|
|
|
|
next $ return True
|
|
|
|
| otherwise = do
|
|
|
|
headers <- getHttpHeaders
|
|
|
|
ifM (liftIO $ Url.check url headers $ keySize key)
|
|
|
|
( do
|
|
|
|
setUrlPresent key url
|
|
|
|
next $ return True
|
|
|
|
, do
|
|
|
|
warning $ "failed to verify url: " ++ url
|
|
|
|
stop
|
|
|
|
)
|
2011-08-06 14:57:22 -04:00
|
|
|
|
|
|
|
download :: String -> FilePath -> CommandPerform
|
|
|
|
download url file = do
|
2011-07-19 14:07:23 -04:00
|
|
|
showAction $ "downloading " ++ url ++ " "
|
2013-04-11 16:14:17 -04:00
|
|
|
dummykey <- genkey
|
2011-11-08 15:34:10 -04:00
|
|
|
tmp <- fromRepo $ gitAnnexTmpLocation dummykey
|
2013-04-11 16:14:17 -04:00
|
|
|
stopUnless (runtransfer dummykey tmp) $ do
|
2012-06-05 19:51:03 -04:00
|
|
|
backend <- chooseBackend file
|
2013-02-14 16:54:36 -04:00
|
|
|
let source = KeySource
|
|
|
|
{ keyFilename = file
|
|
|
|
, contentLocation = tmp
|
|
|
|
, inodeCache = Nothing
|
|
|
|
}
|
2012-06-05 19:51:03 -04:00
|
|
|
k <- genKey source backend
|
2011-12-09 12:23:45 -04:00
|
|
|
case k of
|
|
|
|
Nothing -> stop
|
2013-04-11 13:35:52 -04:00
|
|
|
Just (key, _) -> next $ cleanup url file key (Just tmp)
|
2013-04-11 16:14:17 -04:00
|
|
|
where
|
|
|
|
{- Generate a dummy key to use for this download, before we can
|
|
|
|
- examine the file and find its real key. This allows resuming
|
|
|
|
- downloads, as the dummy key for a given url is stable.
|
|
|
|
-
|
|
|
|
- If the assistant is running, actually hits the url here,
|
|
|
|
- to get the size, so it can display a pretty progress bar.
|
|
|
|
-}
|
|
|
|
genkey = do
|
|
|
|
pidfile <- fromRepo gitAnnexPidFile
|
|
|
|
size <- ifM (liftIO $ isJust <$> checkDaemon pidfile)
|
|
|
|
( do
|
|
|
|
headers <- getHttpHeaders
|
|
|
|
liftIO $ snd <$> Url.exists url headers
|
|
|
|
, return Nothing
|
|
|
|
)
|
|
|
|
return $ Backend.URL.fromUrl url size
|
|
|
|
runtransfer dummykey tmp =
|
2013-04-11 17:15:45 -04:00
|
|
|
Transfer.download webUUID dummykey (Just file) Transfer.forwardRetry $ const $ do
|
2013-04-11 16:14:17 -04:00
|
|
|
liftIO $ createDirectoryIfMissing True (parentDir tmp)
|
|
|
|
downloadUrl [url] tmp
|
|
|
|
|
2013-04-11 13:35:52 -04:00
|
|
|
|
|
|
|
cleanup :: String -> FilePath -> Key -> Maybe FilePath -> CommandCleanup
|
|
|
|
cleanup url file key mtmp = do
|
|
|
|
when (isJust mtmp) $
|
|
|
|
logStatus key InfoPresent
|
|
|
|
setUrlPresent key url
|
|
|
|
Command.Add.addLink file key False
|
|
|
|
whenM isDirect $ do
|
|
|
|
void $ addAssociatedFile key file
|
|
|
|
{- For moveAnnex to work in direct mode, the symlink
|
|
|
|
- must already exist, so flush the queue. -}
|
|
|
|
Annex.Queue.flush
|
|
|
|
maybe noop (moveAnnex key) mtmp
|
|
|
|
return True
|
2011-07-01 17:15:46 -04:00
|
|
|
|
2013-03-11 19:55:01 -04:00
|
|
|
nodownload :: Bool -> String -> FilePath -> CommandPerform
|
|
|
|
nodownload relaxed url file = do
|
2012-04-22 01:13:09 -04:00
|
|
|
headers <- getHttpHeaders
|
2013-03-11 19:55:01 -04:00
|
|
|
(exists, size) <- if relaxed
|
|
|
|
then pure (True, Nothing)
|
|
|
|
else liftIO $ Url.exists url headers
|
2012-02-18 11:44:21 -04:00
|
|
|
if exists
|
|
|
|
then do
|
|
|
|
let key = Backend.URL.fromUrl url size
|
2013-04-11 13:35:52 -04:00
|
|
|
next $ cleanup url file key Nothing
|
2012-02-18 11:44:21 -04:00
|
|
|
else do
|
|
|
|
warning $ "unable to access url: " ++ url
|
|
|
|
stop
|
2011-08-06 14:57:22 -04:00
|
|
|
|
2012-02-16 12:25:19 -04:00
|
|
|
url2file :: URI -> Maybe Int -> FilePath
|
|
|
|
url2file url pathdepth = case pathdepth of
|
|
|
|
Nothing -> filesize $ escape fullurl
|
|
|
|
Just depth
|
2012-02-16 14:26:53 -04:00
|
|
|
| depth > 0 -> frombits $ drop depth
|
2012-02-16 14:28:17 -04:00
|
|
|
| depth < 0 -> frombits $ reverse . take (negate depth) . reverse
|
|
|
|
| otherwise -> error "bad --pathdepth"
|
2012-11-12 01:05:04 -04:00
|
|
|
where
|
|
|
|
fullurl = uriRegName auth ++ uriPath url ++ uriQuery url
|
2013-04-22 20:24:53 -04:00
|
|
|
frombits a = intercalate "/" $ a urlbits
|
2012-11-12 01:05:04 -04:00
|
|
|
urlbits = map (filesize . escape) $ filter (not . null) $ split "/" fullurl
|
|
|
|
auth = fromMaybe (error $ "bad url " ++ show url) $ uriAuthority url
|
|
|
|
filesize = take 255
|
|
|
|
escape = replace "/" "_" . replace "?" "_"
|