summaryrefslogtreecommitdiff
path: root/Remote/WebDAV.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Remote/WebDAV.hs')
-rw-r--r--Remote/WebDAV.hs113
1 files changed, 88 insertions, 25 deletions
diff --git a/Remote/WebDAV.hs b/Remote/WebDAV.hs
index c1e221295..b69d51f23 100644
--- a/Remote/WebDAV.hs
+++ b/Remote/WebDAV.hs
@@ -20,6 +20,7 @@ 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
@@ -108,25 +109,45 @@ storeHelper r urlbase user pass b = catchBoolIO $ do
storehttp url v = putContentAndProps url user pass
(noProps, (contentType, v))
-retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool
-retrieve r k _f d = retrieveHelper r k (L.writeFile d)
-
retrieveCheap :: Remote -> Key -> FilePath -> Annex Bool
retrieveCheap _ _ _ = return False
-retrieveEncrypted :: Remote -> (Cipher, Key) -> Key -> FilePath -> Annex Bool
-retrieveEncrypted r (cipher, enck) _ d = retrieveHelper r enck $ \b -> do
- withDecryptedContent cipher (return b) (L.writeFile d)
+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 $
+ feeder user pass
+ return True
+ where
+ onerr _ = return False
-retrieveHelper :: Remote -> Key -> (L.ByteString -> IO ()) -> Annex Bool
-retrieveHelper r k saver = davAction r False $ \(baseurl, user, pass) -> liftIO $ do
- let url = davLocation baseurl k
- maybe (return False) save
- =<< catchMaybeHttp (getPropsAndContent url user pass)
+ feeder _ _ [] = return ([], [])
+ feeder user pass (url:urls) = do
+ mb <- davGetUrlContent url user pass
+ case mb of
+ Nothing -> throwDownloadFailed
+ Just b -> return (urls, L.toChunks b)
+
+throwDownloadFailed :: IO a
+throwDownloadFailed = ioError $ mkIOError userErrorType "download failed" Nothing Nothing
+
+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
+ withDecryptedContent cipher (L.concat <$> feeder user pass urls []) $
+ meteredWriteFile meterupdate d
+ return True
where
- save (_, (_, b)) = do
- saver b
- return True
+ onerr _ = return False
+
+ feeder _ _ [] c = return $ reverse c
+ feeder user pass (url:urls) c = do
+ mb <- davGetUrlContent url user pass
+ case mb of
+ Nothing -> throwDownloadFailed
+ Just b -> feeder user pass urls (b:c)
remove :: Remote -> Key -> Annex Bool
remove r k = davAction r False $ \(baseurl, user, pass) -> liftIO $ do
@@ -136,20 +157,48 @@ remove r k = davAction r False $ \(baseurl, user, pass) -> liftIO $ do
isJust <$> catchMaybeHttp (deleteContent url user pass)
checkPresent :: Remote -> Key -> Annex (Either String Bool)
-checkPresent r k = davAction r noconn $ \(baseurl, user, pass) -> do
- showAction $ "checking " ++ name r
- let url = davLocation baseurl k
- v <- liftIO $ catchHttp $ getProps url user pass
- case v of
- Right _ -> return $ Right True
- Left (Left (StatusCodeException status _))
- | statusCode status == statusCode notFound404 -> return $ Right False
- | otherwise -> return $ Left $ show $ statusMessage status
- Left (Left httpexception) -> return $ Left $ show httpexception
- Left (Right ioexception) -> return $ Left $ show ioexception
+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 = url ++ chunkCount
+ maybe (onerr chunkcount) (a . listChunks url . L8.toString)
+ =<< davGetUrlContent chunkcount user pass
+ | otherwise = a [url]
+ where
+ url = davLocation baseurl k
+
davAction :: Remote -> a -> ((DavUrl, DavUser, DavPass) -> Annex a) -> Annex a
davAction r unconfigured action = case config r of
Nothing -> return unconfigured
@@ -173,6 +222,20 @@ davLocation baseurl k = davUrl baseurl $ annexLocation k hashDirLower
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
+ | otherwise = Left $ show $ statusMessage status
+ decode (Left (Left httpexception)) = Left $ show httpexception
+ decode (Left (Right ioexception)) = Left $ show ioexception
+
+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 ()