haskell-ftp-0.1.0.0: Network/FTP/Socket.hs
module Network.FTP.Socket
( startPasvServer
, toPortString
, fromPortString
, module Network.Socket
, module Network.BSD
) where
{-|
- Network Utils.
-}
import Data.List (intercalate)
import Data.Word
import Data.Bits
import Control.Exception (bracketOnError)
import Network.Socket
import Network.BSD (getProtocolNumber)
startPasvServer :: SockAddr -> IO Socket
startPasvServer (SockAddrInet _ host) = do
proto <- getProtocolNumber "tcp"
bracketOnError
(socket AF_INET Stream proto)
sClose
(\sock -> do
setSocketOption sock ReuseAddr 0
bindSocket sock (SockAddrInet aNY_PORT host)
listen sock 1
return sock
)
startPasvServer _ = fail "Require IPv4 sockets"
{- | Converts a socket address to a string suitable for a PORT command.
Example:
> toPortString (SockAddrInet (PortNum 0x1234) (0xaabbccdd)) ->
> "170,187,204,221,18,52"
-}
toPortString :: SockAddr -> IO String
toPortString (SockAddrInet port hostaddr) =
let wport = fromIntegral port::Word16
in do
hn <- inet_ntoa hostaddr
return (replace '.' ',' hn ++ "," ++
(intercalate "," . map show . getBytes $ wport))
toPortString _ =
error "toPortString only works on AF_INET addresses"
-- | Converts a port string to a socket address. This is the inverse calculation of 'toPortString'.
fromPortString :: String -> IO SockAddr
fromPortString instr =
let inbytes = split ',' instr
(hostpart, portpart) = splitAt 4 inbytes
hostname = intercalate "." hostpart
portbytes = map read portpart
in
do
addr <- inet_addr hostname
return $ SockAddrInet (fromInteger $ fromBytes portbytes) addr
-- copied from cgi
--
-- * Utilities
--
-- | Replaces all instances of a value in a list by another value.
replace :: Eq a =>
a -- ^ Value to look for
-> a -- ^ Value to replace it with
-> [a] -- ^ Input list
-> [a] -- ^ Output list
replace x y = map (\z -> if z == x then y else z)
-- copied from MissingH
{- | Returns a list representing the bytes that comprise a data type.
Example:
> getBytes (0x12345678::Int) -> [0x12, 0x34, 0x56, 0x78]
-}
getBytes :: (Integral a, Bounded a, Bits a) => a -> [a]
getBytes input =
let getByte _ 0 = []
getByte x remaining = (x .&. 0xff) : getByte (shiftR x 8) (remaining - 1)
in
if (bitSize input `mod` 8) /= 0
then error "Input data bit size must be a multiple of 8"
else reverse $ getByte input (bitSize input `div` 8)
{- | The opposite of 'getBytes', this function builds a number based on
its component bytes.
Results are undefined if any components of the input list are > 0xff!
-}
fromBytes :: (Bits a) => [a] -> a
fromBytes =
foldl (\accum x -> shiftL accum 8 .|. x) 0
-- | split a list by sperator
split :: Eq a => a -> [a] -> [[a]]
split _ [] = []
split sep xs = s : split sep rest'
where (s, rest) = break (==sep) xs
rest' = case rest of
(_:r) -> r
_ -> []