OTWO-1213 Works around lost encoding in Ruby/C binding layer
[ohcount] / test / src_dir / haskell1.hs
1 {-|
2   This module contains some functions that are useful in several places in the
3   program and don't belong to one specific other module.
4 -}
5 module Gnutella.Misc where
6
7 import Data.ByteString(ByteString)
8 import qualified Data.ByteString as BS
9 import Data.Bits
10 import Data.Word
11 import Text.Read
12 import Data.Char(isNumber)
13 import Data.List(intersperse)
14 import Network
15 import Network.BSD(getHostByName, HostEntry(..))
16 import Network.Socket(HostAddress(..))
17 import Debug.Trace
18
19 {-|
20   Maakt van vier bytes een Word32. Gaat ervan uit dat die vier bytes little-endian achter elkaar
21   staan. Als de gegeven string korter is dan 4 bytes, termineert de functie. Als de string langer
22   is, worden alle bytes voorbij de vierde genegeerd.
23 -}
24 composeWord32 :: ByteString -> Word32
25 composeWord32 s = shiftL byte4 24 + shiftL byte3 16 + shiftL byte2 8 + byte1
26   where byte1, byte2, byte3, byte4 :: Word32
27         [byte1, byte2, byte3, byte4] = map fromIntegral $ BS.unpack (BS.take 4 s)
28
29 {-| 
30   Turns a Word32 into a tuple of Word8s. The tuple is little-endian: the least
31   significant octet comes first.
32 -}
33 word32ToWord8s :: Word32 -> (Word8, Word8, Word8, Word8)
34 word32ToWord8s w = (fromIntegral (w .&. 0x000000ff)
35                    ,fromIntegral (shiftR w 8 .&. 0x000000ff)
36                    ,fromIntegral (shiftR w 16 .&. 0x000000ff)
37                    ,fromIntegral (shiftR w 24 .&. 0x000000ff)
38                    )
39
40 {-|
41   Parses a host specification in the "name:12345"-style notation into a hostname
42   and a port number.
43
44   As a rather special feature, it returns 6346 as the port number when there is
45   no port specified. When there is a port specified, but it is unparseable, it
46   returns Nothing.
47 -}
48 parseHostnameWithPort :: String -> IO (Maybe ((Word8, Word8, Word8, Word8)
49                                              ,PortNumber))
50 parseHostnameWithPort str = do maybeHostName <- stringToIP hostNameStr
51                                return $ (do portNum <- maybePortNum
52                                             hostName <- maybeHostName
53                                             return (hostName, portNum)
54                                         )
55   where hostNameStr = takeWhile (/=':') str
56         maybePortNum  = case tail (dropWhile (/=':') str) of
57                           [] -> Just $ 6346
58                           s  -> case reads s of
59                                   []     -> Nothing
60                                   (x:xs) -> Just $ fromIntegral $ fst x
61
62 {-|
63   Translates a string, representing an IP address, to a list of bytes.
64   Returns Nothing when the string does not represent an IP address in xxx.xxx.xxx.xxx format
65 -}
66 ipStringToBytes :: String -> Maybe (Word8, Word8, Word8, Word8)
67 -- Again, hugs won't let us use regexes where they would be damn convenient
68 ipStringToBytes s =
69     let ipBytesStrings = splitAtDots s
70     in if all (all isNumber) ipBytesStrings
71          then let bytesList = map (fst . head . reads) ipBytesStrings
72               in Just (bytesList!!0
73                       ,bytesList!!1
74                       ,bytesList!!2
75                       ,bytesList!!3
76                       )
77          else Nothing
78   where splitAtDots s = foldr (\c (n:nums) -> if c == '.'
79                                               then [] : n : nums
80                                               else (c:n) : nums
81                               ) [[]] s
82
83 {-|
84   Translates a list of bytes representing an IP address (big endian) to a string
85   in the xxx.xxx.xxx.xxx format.
86 -}
87 ipBytesToString :: (Word8, Word8, Word8, Word8) -> String
88 ipBytesToString (b1, b2, b3, b4) = 
89     concat $ intersperse "." $ map show [b1, b2, b3, b4]
90
91 {-| 
92   Takes a String that's either an IP address or a hostname, and returns you the
93   IP address as a list of 4 bytes (in big-endian byte order). It returns Nothing
94   if there is no parse for the string as IP address and the hostname can't be
95   found.
96 -}
97 stringToIP :: String -> IO (Maybe (Word8, Word8, Word8, Word8))
98 stringToIP hostName = case ipStringToBytes hostName of
99                         Just a  -> return (Just a)
100                         Nothing -> do hostent <- getHostByName hostName
101                                       let ipWord32 = head (hostAddresses hostent)
102                                           ipWord8s = word32ToWord8s ipWord32
103                                       return (Just ipWord8s)
104
105 -- used in reading the hostcache
106 instance Read PortNumber where
107     readsPrec i = map (\(a, b) -> (fromIntegral a, b)) . (readsPrec i :: ReadS Word16)
108