summaryrefslogtreecommitdiff
path: root/Remote
diff options
context:
space:
mode:
Diffstat (limited to 'Remote')
-rw-r--r--Remote/S3real.hs90
1 files changed, 72 insertions, 18 deletions
diff --git a/Remote/S3real.hs b/Remote/S3real.hs
index e8c700e2c..7d6b5d5ba 100644
--- a/Remote/S3real.hs
+++ b/Remote/S3real.hs
@@ -15,6 +15,9 @@ import Network.AWS.AWSResult
import qualified Data.ByteString.Lazy.Char8 as L
import qualified Data.Map as M
import Data.Maybe
+import Data.List
+import Data.Char
+import Data.String.Utils
import Control.Monad (when)
import Control.Monad.State (liftIO)
import System.Environment
@@ -68,24 +71,53 @@ gen' r u c cst = do
}
s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig
-s3Setup u c = do
- -- verify configuration is sane
- c' <- encryptionSetup c
- let fullconfig = M.union c' defaults
-
- genBucket fullconfig
- gitConfigSpecialRemote u fullconfig "s3" "true"
- s3SetCreds fullconfig
+s3Setup u c = handlehost $ M.lookup "host" c
where
remotename = fromJust (M.lookup "name" c)
- bucket = remotename ++ "-" ++ u
+ defbucket = remotename ++ "-" ++ u
defaults = M.fromList
[ ("datacenter", "US")
, ("storageclass", "STANDARD")
, ("host", defaultAmazonS3Host)
, ("port", show defaultAmazonS3Port)
- , ("bucket", bucket)
+ , ("bucket", defbucket)
]
+
+ handlehost Nothing = defaulthost
+ handlehost (Just h)
+ | ".archive.org" `isSuffixOf` (map toLower h) = archiveorg
+ | otherwise = defaulthost
+
+ use fullconfig = do
+ genBucket fullconfig
+ gitConfigSpecialRemote u fullconfig "s3" "true"
+ s3SetCreds fullconfig
+
+ defaulthost = do
+ c' <- encryptionSetup c
+ use $ M.union c' defaults
+
+ archiveorg = do
+ showNote $ "Internet Archive mode"
+ maybe (error "specify bucket=") (const $ return ()) $
+ M.lookup "bucket" archiveconfig
+ use archiveconfig
+ where
+ archiveconfig =
+ -- hS3 does not pass through
+ -- x-archive-* headers
+ M.mapKeys (replace "x-archive-" "x-amz-") $
+ -- encryption does not make sense here
+ M.insert "encryption" "none" $
+ M.union c $
+ -- special constraints on key names
+ M.insert "mungekeys" "ia" $
+ -- buckets created only as files
+ -- are uploaded
+ M.insert "x-amz-auto-make-bucket" "1" $
+ -- no default bucket name; should
+ -- be human-readable
+ M.delete "bucket" defaults
store :: Remote Annex -> Key -> Annex Bool
store r k = s3Action r False $ \(conn, bucket) -> do
@@ -111,8 +143,8 @@ storeHelper (conn, bucket) r k file = do
-- buffered to calculate it
size <- maybe getsize (return . fromIntegral) $ keySize k
let object = setStorageClass storageclass $
- S3Object bucket (show k) ""
- [("Content-Length",(show size)), ("x-amz-auto-make-bucket","1")] content
+ S3Object bucket (bucketFile r k) ""
+ (("Content-Length", show size) : xheaders) content
sendObject conn object
where
storageclass =
@@ -122,10 +154,13 @@ storeHelper (conn, bucket) r k file = do
getsize = do
s <- liftIO $ getFileStatus file
return $ fileSize s
+
+ xheaders = filter isxheader $ M.assocs $ fromJust $ config r
+ isxheader (h, _) = "x-amz-" `isPrefixOf` h
retrieve :: Remote Annex -> Key -> FilePath -> Annex Bool
retrieve r k f = s3Action r False $ \(conn, bucket) -> do
- res <- liftIO $ getObject conn $ bucketKey bucket k
+ res <- liftIO $ getObject conn $ bucketKey r bucket k
case res of
Right o -> do
liftIO $ L.writeFile f $ obj_data o
@@ -134,7 +169,7 @@ retrieve r k f = s3Action r False $ \(conn, bucket) -> do
retrieveEncrypted :: Remote Annex -> (Cipher, Key) -> FilePath -> Annex Bool
retrieveEncrypted r (cipher, enck) f = s3Action r False $ \(conn, bucket) -> do
- res <- liftIO $ getObject conn $ bucketKey bucket enck
+ res <- liftIO $ getObject conn $ bucketKey r bucket enck
case res of
Right o -> liftIO $
withDecryptedContent cipher (return $ obj_data o) $ \content -> do
@@ -144,13 +179,13 @@ retrieveEncrypted r (cipher, enck) f = s3Action r False $ \(conn, bucket) -> do
remove :: Remote Annex -> Key -> Annex Bool
remove r k = s3Action r False $ \(conn, bucket) -> do
- res <- liftIO $ deleteObject conn $ bucketKey bucket k
+ res <- liftIO $ deleteObject conn $ bucketKey r bucket k
s3Bool res
checkPresent :: Remote Annex -> Key -> Annex (Either IOException Bool)
checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
showNote ("checking " ++ name r ++ "...")
- res <- liftIO $ getObjectInfo conn $ bucketKey bucket k
+ res <- liftIO $ getObjectInfo conn $ bucketKey r bucket k
case res of
Right _ -> return $ Right True
Left (AWSError _ _) -> return $ Right False
@@ -182,8 +217,27 @@ s3Action r noconn action = do
(Just b, Just c) -> action (c, b)
_ -> return noconn
-bucketKey :: String -> Key -> S3Object
-bucketKey bucket k = S3Object bucket (show k) "" [] L.empty
+bucketFile :: Remote Annex -> Key -> FilePath
+bucketFile r k = (munge $ show k)
+ where
+ munge s = case M.lookup "mungekeys" $ fromJust $ config r of
+ Just "ia" -> iaMunge s
+ _ -> s
+
+bucketKey :: Remote Annex -> String -> Key -> S3Object
+bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
+
+{- Internet Archive limits filenames to a subset of ascii,
+ - with no whitespace. Other characters are xml entity
+ - encoded. -}
+iaMunge :: String -> String
+iaMunge = concat . (map munge)
+ where
+ munge c
+ | isAsciiUpper c || isAsciiLower c || isNumber c = [c]
+ | c `elem` "_-.\"" = [c]
+ | isSpace c = []
+ | otherwise = "&" ++ show (ord c) ++ ";"
genBucket :: RemoteConfig -> Annex ()
genBucket c = do