ftp-conduit 0.0.3 → 0.0.4
raw patch · 2 files changed
+55/−40 lines, 2 filesdep ~conduitPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: conduit
API changes (from Hackage documentation)
- Network.FTP.Conduit: GeneralError :: String -> FTPError
- Network.FTP.Conduit: data FTPError
- Network.FTP.Conduit: instance Error FTPError
- Network.FTP.Conduit: instance Show FTPError
+ Network.FTP.Conduit: data FTPException
+ Network.FTP.Conduit: instance Exception FTPException
+ Network.FTP.Conduit: instance Show FTPException
+ Network.FTP.Conduit: instance Typeable FTPException
- Network.FTP.Conduit: IncorrectScheme :: String -> FTPError
+ Network.FTP.Conduit: IncorrectScheme :: String -> FTPException
- Network.FTP.Conduit: SocketClosed :: FTPError
+ Network.FTP.Conduit: SocketClosed :: FTPException
- Network.FTP.Conduit: UnexpectedCode :: Int -> ByteString -> FTPError
+ Network.FTP.Conduit: UnexpectedCode :: Int -> ByteString -> FTPException
- Network.FTP.Conduit: createSink :: URI -> Sink ByteString (ErrorT FTPError IO) ()
+ Network.FTP.Conduit: createSink :: URI -> Sink ByteString IO ()
- Network.FTP.Conduit: createSource :: URI -> Source (ErrorT FTPError IO) ByteString
+ Network.FTP.Conduit: createSource :: URI -> Source IO ByteString
Files
- Network/FTP/Conduit.hs +53/−38
- ftp-conduit.cabal +2/−2
Network/FTP/Conduit.hs view
@@ -1,16 +1,17 @@+{-# LANGUAGE DeriveDataTypeable #-} -- | This module contains code to use files on a remote FTP server as -- Sources and Sinks. -- -- Using these functions looks like this: -- > let uri = fromJust $ parseURI "ftp://ftp.kernel.org/pub/README_ABOUT_BZ2_FILES"--- > runErrorT $ runResourceT $ createSource uri $$ consume+-- > runResourceT $ createSource uri $$ consume -- -- The functions here operate on the ErrorT monad transformer, because -- the server can send unexpected replies, which are thrown as errors. module Network.FTP.Conduit ( createSink , createSource- , FTPError(..)+ , FTPException(..) ) where import Data.Conduit@@ -19,35 +20,36 @@ import Network.Socket hiding (send, sendTo, recv, recvFrom, Closed) import Network.Socket.ByteString import Network.URI-import Network.Utils-import Control.Monad.Error+import Network.Utils (connectTCP)+import Control.Monad.Trans (lift)+import Control.Monad (when) import Data.Word import System.ByteOrder import Data.Bits import Prelude hiding (getLine) import Control.Monad.Trans.Resource+import Data.Typeable (Typeable)+import Control.Exception (Exception, throw) -data FTPError = UnexpectedCode Int BS.ByteString- | GeneralError String- | IncorrectScheme String- | SocketClosed- deriving (Show)-instance Error FTPError where- noMsg = GeneralError ""- strMsg = GeneralError+data FTPException = UnexpectedCode Int BS.ByteString+ | IncorrectScheme String+ | SocketClosed+ deriving (Show, Typeable) +instance Exception FTPException+ hton_16 :: Word16 -> Word16 hton_16 x = case byteOrder of BigEndian -> x LittleEndian -> x `shiftL` 8 + x `shiftR` 8 _ -> undefined -getByte :: Socket -> ResourceT (ErrorT FTPError IO) Word8+getByte :: Socket -> ResourceT IO Word8 getByte s = do- b <- lift $ lift $ recv s 1- if BS.null b then lift (throwError SocketClosed) else return $ BS.head b+ b <- lift $ recv s 1+ if BS.null b then throw SocketClosed else return $ BS.head b -getLine :: Socket -> ResourceT (ErrorT FTPError IO) BS.ByteString+getLine :: Socket -> ResourceT IO BS.ByteString getLine s = do b <- getByte s helper b@@ -60,23 +62,23 @@ extractCode :: BS.ByteString -> Int extractCode = read . toString . (BS.takeWhile (/= 32)) -readExpected :: Socket -> Int -> ResourceT (ErrorT FTPError IO) BS.ByteString+readExpected :: Socket -> Int -> ResourceT IO BS.ByteString readExpected s i = do line <- getLine s --lift $ lift $ putStrLn $ "Read: " ++ (toString line) if extractCode line /= i- then lift $ throwError $ UnexpectedCode i line+ then throw $ UnexpectedCode i line else return line -writeLine :: Socket -> BS.ByteString -> ResourceT (ErrorT FTPError IO) ()-writeLine s bs = lift $ lift $ do+writeLine :: Socket -> BS.ByteString -> ResourceT IO ()+writeLine s bs = lift $ do --lift $ lift $ putStrLn $ "Writing: " ++ (toString bs)- sendAll s $ bs `BS.append` (fromString "\r\n") -- hardcode the newline for platform independence+ sendAll s $ bs `BS.append` (fromString "\r\n") -createSource :: URI -> Source (ErrorT FTPError IO) BS.ByteString+createSource :: URI -> Source IO BS.ByteString createSource uri = Source { sourcePull = pull- , sourceClose = close- }+ , sourceClose = close+ } where pull = do (c, rc, d, rd, path') <- common uri@@ -84,7 +86,7 @@ _ <- readExpected c 150 pull' c rc d rd pull' c rc d rd= do- bytes <- lift $ lift $ recv d 1024+ bytes <- lift $ recv d 1024 if BS.null bytes then do close' c rc d rd@@ -101,17 +103,17 @@ _ <- readExpected c 221 release rc -createSink :: URI -> Sink BS.ByteString (ErrorT FTPError IO) ()+createSink :: URI -> Sink BS.ByteString IO () createSink uri = SinkData { sinkPush = push- , sinkClose = close- }+ , sinkClose = close+ } where push input = do (c, rc, d, rd, path') <- common uri writeLine c $ fromString $ "STOR " ++ path' _ <- readExpected c 150 push' c rc d rd input push' c rc d rd input = do- lift $ lift $ sendAll d input+ lift $ sendAll d input return $ Processing (push' c rc d rd) (close' c rc d rd) close = return () close' c rc _ rd = do@@ -121,13 +123,13 @@ _ <- readExpected c 221 release rc -common :: URI -> ResourceT (ErrorT FTPError IO) (Socket, ReleaseKey, Socket, ReleaseKey, String)+common :: URI -> ResourceT IO (Socket, ReleaseKey, Socket, ReleaseKey, String) common (URI { uriScheme = scheme' , uriAuthority = authority' , uriPath = path' }) = do- if scheme' /= "ftp:" then lift (throwError (IncorrectScheme scheme')) else return ()- c <- lift $ lift $ connectTCP host (PortNum (hton_16 port))+ when (scheme' /= "ftp:") $ throw (IncorrectScheme scheme')+ c <- lift $ connectTCP host (PortNum (hton_16 port)) rc <- register $ sClose c _ <- readExpected c 220 writeLine c $ fromString $ "USER " ++ user@@ -139,18 +141,31 @@ writeLine c $ fromString "PASV" pasv_response <- readExpected c 227 let (pasvhost, pasvport) = parsePasvString pasv_response- d <- lift $ lift $ connectTCP (toString pasvhost) (PortNum (hton_16 pasvport))+ d <- lift $ connectTCP (toString pasvhost) (PortNum (hton_16 pasvport)) rd <- register $ sClose d return (c, rc, d, rd, path') where (host, port, user, pass) = case authority' of Nothing -> undefined Just (URIAuth userInfo regName port') -> ( regName- , if null port' then 21 else read (tail port')- , if null userInfo then "anonymous" else takeWhile (\ l -> l /= ':' && l /= '@') userInfo- , if null userInfo || not (':' `elem` userInfo) then "" else init $ tail $ (dropWhile (/= ':')) userInfo+ , if null port'+ then 21+ else read (tail port')+ , if null userInfo+ then "anonymous"+ else takeWhile (\ l -> l /= ':' && l /= '@') userInfo+ , if null userInfo || not (':' `elem` userInfo)+ then "" else+ init $ tail $ (dropWhile (/= ':')) userInfo ) parsePasvString ps = (pasvhost, pasvport)- where pasvhost = BS.init $ foldl (\ a ip -> a `BS.append` (fromString $ show ip) `BS.append` (fromString ".")) BS.empty [ip1, ip2, ip3, ip4]+ where pasvhost = BS.init $ foldl+ (\ a ip -> a+ `BS.append` (fromString $ show ip)+ `BS.append` (fromString "."))+ BS.empty [ip1, ip2, ip3, ip4] pasvport = (fromIntegral port1) `shiftL` 8 + (fromIntegral port2)- (ip1, ip2, ip3, ip4, port1, port2) = read $ toString $ (`BS.append` (fromString ")")) $ (BS.takeWhile (/= 41)) $ (BS.dropWhile (/= 40)) ps :: (Int, Int, Int, Int, Int, Int)+ (ip1, ip2, ip3, ip4, port1, port2) = read $+ toString $ (`BS.append` (fromString ")")) $+ (BS.takeWhile (/= 41)) $ (BS.dropWhile (/= 40))+ ps :: (Int, Int, Int, Int, Int, Int)
ftp-conduit.cabal view
@@ -1,5 +1,5 @@ name: ftp-conduit-version: 0.0.3+version: 0.0.4 license: BSD3 license-file: LICENSE author: Myles C. Maxfield <myles.maxfield@gmail.com>@@ -15,7 +15,7 @@ library build-depends: base >= 4 && < 5- , conduit >= 0.2+ , conduit >= 0.1.1 , network >= 2.0 , bytestring >= 0.9 , MissingH >= 0.18.6