diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2013-11-28 11:24:49 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2013-11-28 11:24:49 +0400 |
commit | 0278075496121fcfe9ff5fe5a70fb8ed17a45119 (patch) | |
tree | 8b4bde5d926b1b9e4d29b44e9c9707930bc723c3 /src/Data | |
parent | 53d47a403f8e940f6f55f292c3d5d4b2edc0b3cb (diff) |
Add Convertible, Query instances to infohash
Diffstat (limited to 'src/Data')
-rw-r--r-- | src/Data/Torrent/InfoHash.hs | 94 |
1 files changed, 63 insertions, 31 deletions
diff --git a/src/Data/Torrent/InfoHash.hs b/src/Data/Torrent/InfoHash.hs index e9e6cf39..25fd3e49 100644 --- a/src/Data/Torrent/InfoHash.hs +++ b/src/Data/Torrent/InfoHash.hs | |||
@@ -9,6 +9,8 @@ | |||
9 | -- | 9 | -- |
10 | {-# LANGUAGE FlexibleInstances #-} | 10 | {-# LANGUAGE FlexibleInstances #-} |
11 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | 11 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} |
12 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
13 | {-# LANGUAGE DeriveDataTypeable #-} | ||
12 | module Data.Torrent.InfoHash | 14 | module Data.Torrent.InfoHash |
13 | ( InfoHash | 15 | ( InfoHash |
14 | 16 | ||
@@ -29,20 +31,21 @@ import Data.Aeson | |||
29 | import Data.BEncode | 31 | import Data.BEncode |
30 | import Data.ByteString as BS | 32 | import Data.ByteString as BS |
31 | import Data.ByteString.Char8 as BC | 33 | import Data.ByteString.Char8 as BC |
32 | import Data.ByteString.Lazy as BL | ||
33 | import Data.ByteString.Base16 as Base16 | 34 | import Data.ByteString.Base16 as Base16 |
34 | import Data.ByteString.Base32 as Base32 | 35 | import Data.ByteString.Base32 as Base32 |
35 | import qualified Data.ByteString.Lazy.Builder as B | 36 | import Data.ByteString.Base64 as Base64 |
36 | import qualified Data.ByteString.Lazy.Builder.ASCII as B | ||
37 | import Data.Char | 37 | import Data.Char |
38 | import Data.Convertible.Base | ||
39 | import Data.Default | ||
38 | import Data.List as L | 40 | import Data.List as L |
39 | import Data.Maybe | 41 | import Data.Maybe |
40 | import Data.Hashable as Hashable | 42 | import Data.Hashable as Hashable |
41 | import Data.URLEncoded as URL | ||
42 | import Data.Serialize | 43 | import Data.Serialize |
43 | import Data.String | 44 | import Data.String |
44 | import Data.Text as T | 45 | import Data.Text as T |
45 | import Data.Text.Encoding as T | 46 | import Data.Text.Encoding as T |
47 | import Data.Typeable | ||
48 | import Network.HTTP.Types.QueryLike | ||
46 | import Network.URI | 49 | import Network.URI |
47 | import Numeric | 50 | import Numeric |
48 | import Text.ParserCombinators.ReadP as P | 51 | import Text.ParserCombinators.ReadP as P |
@@ -62,7 +65,13 @@ import Text.PrettyPrint.Class | |||
62 | 65 | ||
63 | -- | Exactly 20 bytes long SHA1 hash of the info part of torrent file. | 66 | -- | Exactly 20 bytes long SHA1 hash of the info part of torrent file. |
64 | newtype InfoHash = InfoHash { getInfoHash :: BS.ByteString } | 67 | newtype InfoHash = InfoHash { getInfoHash :: BS.ByteString } |
65 | deriving (Eq, Ord) | 68 | deriving (Eq, Ord, Typeable) |
69 | |||
70 | infoHashLen :: Int | ||
71 | infoHashLen = 20 | ||
72 | |||
73 | instance Default InfoHash where | ||
74 | def = "0123456789012345678901234567890123456789" | ||
66 | 75 | ||
67 | -- | for hex encoded strings | 76 | -- | for hex encoded strings |
68 | instance Show InfoHash where | 77 | instance Show InfoHash where |
@@ -88,52 +97,76 @@ instance IsString InfoHash where | |||
88 | 97 | ||
89 | instance Hashable InfoHash where | 98 | instance Hashable InfoHash where |
90 | hash = Hashable.hash . getInfoHash | 99 | hash = Hashable.hash . getInfoHash |
100 | {-# INLINE hash #-} | ||
91 | 101 | ||
102 | -- | Raw bytes. | ||
92 | instance BEncode InfoHash where | 103 | instance BEncode InfoHash where |
93 | toBEncode = toBEncode . getInfoHash | 104 | toBEncode = toBEncode . getInfoHash |
94 | fromBEncode be = InfoHash <$> fromBEncode be | 105 | fromBEncode be = InfoHash <$> fromBEncode be |
95 | 106 | ||
107 | -- | Raw bytes. | ||
96 | instance Serialize InfoHash where | 108 | instance Serialize InfoHash where |
97 | put = putByteString . getInfoHash | 109 | put = putByteString . getInfoHash |
98 | get = InfoHash <$> getBytes 20 | 110 | get = InfoHash <$> getBytes 20 |
99 | 111 | ||
100 | -- | Represented as base16 encoded string. | 112 | -- | base16 encoded string. |
101 | instance ToJSON InfoHash where | 113 | instance ToJSON InfoHash where |
102 | toJSON (InfoHash ih) = String $ T.decodeUtf8 $ Base16.encode ih | 114 | toJSON (InfoHash ih) = String $ T.decodeUtf8 $ Base16.encode ih |
103 | 115 | ||
104 | -- | Can be base16 or base32 encoded string. | 116 | -- | Can be base16 or base32 encoded string. |
105 | instance FromJSON InfoHash where | 117 | instance FromJSON InfoHash where |
106 | parseJSON = withText "JSON" $ | 118 | parseJSON = withText "JSON" $ -- TODO |
107 | maybe (fail "could not parse InfoHash") pure . textToInfoHash | 119 | maybe (fail "could not parse InfoHash") pure . textToInfoHash |
108 | 120 | ||
109 | instance URLShow InfoHash where | 121 | -- | Raw bytes. |
110 | urlShow = show | 122 | instance QueryValueLike InfoHash where |
123 | toQueryValue (InfoHash ih) = Just ih | ||
124 | {-# INLINE toQueryValue #-} | ||
111 | 125 | ||
112 | -- | base16 encoded. | 126 | -- | base16 encoded. |
113 | instance Pretty InfoHash where | 127 | instance Pretty InfoHash where |
114 | pretty = text . BC.unpack . ppHex . getInfoHash | 128 | pretty = text . T.unpack . longHex |
115 | |||
116 | infoHashLen :: Int | ||
117 | infoHashLen = 20 | ||
118 | 129 | ||
119 | -- | Convert raw bytes to info hash. | 130 | -- | Convert raw bytes to info hash. |
131 | instance Convertible BS.ByteString InfoHash where | ||
132 | safeConvert bs | ||
133 | | BS.length bs == infoHashLen = pure (InfoHash bs) | ||
134 | | otherwise = convError "invalid length" bs | ||
135 | |||
136 | -- | Parse infohash from base16\/base32\/base64 encoded string. | ||
137 | instance Convertible Text InfoHash where | ||
138 | safeConvert t | ||
139 | | hashLen <= 28 = | ||
140 | case Base64.decode hashStr of | ||
141 | Left msg -> convError ("invalid base64 encoding" ++ msg) t | ||
142 | Right ihStr -> pure $ InfoHash ihStr | ||
143 | |||
144 | | hashLen == 32 = pure $ InfoHash $ Base32.decode hashStr | ||
145 | -- TODO FIX Base32.decode can return 'undefined' | ||
146 | |||
147 | | hashLen == 40 = | ||
148 | let (ihStr, inv) = Base16.decode hashStr | ||
149 | in if BS.length inv == 0 | ||
150 | then pure $ InfoHash ihStr | ||
151 | else convError "invalid base16 encoding" t | ||
152 | |||
153 | | otherwise = convError "invalid length" t | ||
154 | where | ||
155 | hashLen = BS.length hashStr | ||
156 | hashStr = T.encodeUtf8 t | ||
157 | |||
158 | ignoreErrorMsg :: Either a b -> Maybe b | ||
159 | ignoreErrorMsg = either (const Nothing) Just | ||
160 | |||
161 | -- | TODO remove from API | ||
120 | byteStringToInfoHash :: BS.ByteString -> Maybe InfoHash | 162 | byteStringToInfoHash :: BS.ByteString -> Maybe InfoHash |
121 | byteStringToInfoHash bs | 163 | byteStringToInfoHash = ignoreErrorMsg . safeConvert |
122 | | BS.length bs == infoHashLen = Just (InfoHash bs) | ||
123 | | otherwise = Nothing | ||
124 | 164 | ||
125 | -- | Tries both base16 and base32 while decoding info hash. | 165 | -- | Tries both base16 and base32 while decoding info hash. To Use |
166 | -- 'safeConvert' to find out | ||
167 | -- | ||
126 | textToInfoHash :: Text -> Maybe InfoHash | 168 | textToInfoHash :: Text -> Maybe InfoHash |
127 | textToInfoHash t | 169 | textToInfoHash = ignoreErrorMsg . safeConvert |
128 | | hashLen == 32 = Just $ InfoHash $ Base32.decode hashStr | ||
129 | | hashLen == 40 = let (ihStr, inv) = Base16.decode hashStr | ||
130 | in if BS.length inv == 0 | ||
131 | then Just $ InfoHash ihStr | ||
132 | else Nothing | ||
133 | | otherwise = Nothing | ||
134 | where | ||
135 | hashLen = BS.length hashStr | ||
136 | hashStr = T.encodeUtf8 t | ||
137 | 170 | ||
138 | -- | Hex encode infohash to text, full length. | 171 | -- | Hex encode infohash to text, full length. |
139 | longHex :: InfoHash -> Text | 172 | longHex :: InfoHash -> Text |
@@ -143,10 +176,9 @@ longHex = T.decodeUtf8 . Base16.encode . getInfoHash | |||
143 | shortHex :: InfoHash -> Text | 176 | shortHex :: InfoHash -> Text |
144 | shortHex = T.take 7 . longHex | 177 | shortHex = T.take 7 . longHex |
145 | 178 | ||
146 | ppHex :: BS.ByteString -> BS.ByteString | 179 | -- | TODO remove from API |
147 | ppHex = BL.toStrict . B.toLazyByteString . B.byteStringHexFixed | 180 | -- |
148 | 181 | -- Add query info hash parameter to uri. | |
149 | -- | Add query info hash parameter to uri. | ||
150 | -- | 182 | -- |
151 | -- > info_hash=<url_encoded_info_hash> | 183 | -- > info_hash=<url_encoded_info_hash> |
152 | -- | 184 | -- |