diff options
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r-- | src/Network/BitTorrent/Core/PeerId.hs | 18 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/Message.hs | 108 |
2 files changed, 79 insertions, 47 deletions
diff --git a/src/Network/BitTorrent/Core/PeerId.hs b/src/Network/BitTorrent/Core/PeerId.hs index 2c79739a..a2b03e92 100644 --- a/src/Network/BitTorrent/Core/PeerId.hs +++ b/src/Network/BitTorrent/Core/PeerId.hs | |||
@@ -15,6 +15,7 @@ | |||
15 | module Network.BitTorrent.Core.PeerId | 15 | module Network.BitTorrent.Core.PeerId |
16 | ( -- * PeerId | 16 | ( -- * PeerId |
17 | PeerId (getPeerId) | 17 | PeerId (getPeerId) |
18 | , byteStringToPeerId | ||
18 | 19 | ||
19 | -- * Generation | 20 | -- * Generation |
20 | , genPeerId | 21 | , genPeerId |
@@ -60,30 +61,37 @@ import Paths_bittorrent (version) | |||
60 | 61 | ||
61 | import Data.Torrent.Client | 62 | import Data.Torrent.Client |
62 | 63 | ||
63 | -- TODO use unpacked form (length is known statically) | 64 | -- TODO use unpacked Word160 form (length is known statically) |
64 | 65 | ||
65 | -- | Peer identifier is exactly 20 bytes long bytestring. | 66 | -- | Peer identifier is exactly 20 bytes long bytestring. |
66 | newtype PeerId = PeerId { getPeerId :: ByteString } | 67 | newtype PeerId = PeerId { getPeerId :: ByteString } |
67 | deriving (Show, Eq, Ord, BEncode, ToJSON, FromJSON) | 68 | deriving (Show, Eq, Ord, BEncode, ToJSON, FromJSON) |
68 | 69 | ||
70 | peerIdLen :: Int | ||
71 | peerIdLen = 20 | ||
72 | |||
69 | instance Serialize PeerId where | 73 | instance Serialize PeerId where |
70 | put = putByteString . getPeerId | 74 | put = putByteString . getPeerId |
71 | get = PeerId <$> getBytes 20 | 75 | get = PeerId <$> getBytes peerIdLen |
72 | 76 | ||
73 | instance URLShow PeerId where | 77 | instance URLShow PeerId where |
74 | urlShow = BC.unpack . getPeerId | 78 | urlShow = BC.unpack . getPeerId |
75 | 79 | ||
76 | instance IsString PeerId where | 80 | instance IsString PeerId where |
77 | fromString str | 81 | fromString str |
78 | | BS.length bs == 20 = PeerId bs | 82 | | BS.length bs == peerIdLen = PeerId bs |
79 | | otherwise = error $ "Peer id should be 20 bytes long: " | 83 | | otherwise = error $ "Peer id should be 20 bytes long: " ++ show str |
80 | ++ show str | ||
81 | where | 84 | where |
82 | bs = fromString str | 85 | bs = fromString str |
83 | 86 | ||
84 | instance Pretty PeerId where | 87 | instance Pretty PeerId where |
85 | pretty = text . BC.unpack . getPeerId | 88 | pretty = text . BC.unpack . getPeerId |
86 | 89 | ||
90 | byteStringToPeerId :: BS.ByteString -> Maybe PeerId | ||
91 | byteStringToPeerId bs | ||
92 | | BS.length bs == peerIdLen = Just (PeerId bs) | ||
93 | | otherwise = Nothing | ||
94 | |||
87 | {----------------------------------------------------------------------- | 95 | {----------------------------------------------------------------------- |
88 | -- Encoding | 96 | -- Encoding |
89 | -----------------------------------------------------------------------} | 97 | -----------------------------------------------------------------------} |
diff --git a/src/Network/BitTorrent/Tracker/Message.hs b/src/Network/BitTorrent/Tracker/Message.hs index 3447a367..59ef2027 100644 --- a/src/Network/BitTorrent/Tracker/Message.hs +++ b/src/Network/BitTorrent/Tracker/Message.hs | |||
@@ -52,6 +52,8 @@ import Data.Aeson (ToJSON(..), FromJSON(..)) | |||
52 | import Data.Aeson.TH | 52 | import Data.Aeson.TH |
53 | import Data.BEncode as BE | 53 | import Data.BEncode as BE |
54 | import Data.BEncode.BDict as BE | 54 | import Data.BEncode.BDict as BE |
55 | import Data.ByteString as BS | ||
56 | import Data.ByteString.Char8 as BC | ||
55 | import Data.Char as Char | 57 | import Data.Char as Char |
56 | import Data.List as L | 58 | import Data.List as L |
57 | import Data.Map as M | 59 | import Data.Map as M |
@@ -64,8 +66,10 @@ import Data.Typeable | |||
64 | import Data.URLEncoded as URL | 66 | import Data.URLEncoded as URL |
65 | import Data.Word | 67 | import Data.Word |
66 | import Network | 68 | import Network |
67 | import Network.URI | 69 | import Network.HTTP.Types.URI hiding (urlEncode) |
68 | import Network.Socket | 70 | import Network.Socket |
71 | import Network.URI | ||
72 | import Text.Read (readMaybe) | ||
69 | 73 | ||
70 | import Data.Torrent.InfoHash | 74 | import Data.Torrent.InfoHash |
71 | import Data.Torrent.Progress | 75 | import Data.Torrent.Progress |
@@ -226,63 +230,83 @@ data QueryParam | |||
226 | = ParamInfoHash | 230 | = ParamInfoHash |
227 | | ParamPeerId | 231 | | ParamPeerId |
228 | | ParamPort | 232 | | ParamPort |
229 | | ParamProgress | 233 | | ParamUploaded |
234 | | ParamLeft | ||
235 | | ParamDownloaded | ||
230 | | ParamIP | 236 | | ParamIP |
231 | | ParamNumWant | 237 | | ParamNumWant |
232 | | ParamEvent | 238 | | ParamEvent |
233 | deriving (Show, Eq, Ord, Enum) | 239 | deriving (Show, Eq, Ord, Enum) |
234 | 240 | ||
235 | data ParamParseFailure | 241 | data ParamParseFailure |
236 | = Missing QueryParam -- ^ param not found in query string; | 242 | = Missing QueryParam -- ^ param not found in query string; |
237 | | Invalid QueryParam Text -- ^ param present but not valid. | 243 | | Invalid QueryParam ByteString -- ^ param present but not valid. |
238 | deriving (Show, Eq) | 244 | deriving (Show, Eq) |
239 | 245 | ||
240 | type ParamResult = Either ParamParseFailure | 246 | paramName :: QueryParam -> ByteString |
247 | paramName ParamInfoHash = "info_hash" | ||
248 | paramName ParamPeerId = "peer_id" | ||
249 | paramName ParamPort = "port" | ||
250 | paramName ParamUploaded = "uploaded" | ||
251 | paramName ParamLeft = "left" | ||
252 | paramName ParamDownloaded = "downloaded" | ||
253 | paramName ParamIP = "ip" | ||
254 | paramName ParamNumWant = "numwant" | ||
255 | paramName ParamEvent = "event" | ||
256 | |||
257 | class FromParam a where | ||
258 | fromParam :: BS.ByteString -> Maybe a | ||
259 | |||
260 | instance FromParam InfoHash where | ||
261 | fromParam = byteStringToInfoHash | ||
262 | |||
263 | instance FromParam PeerId where | ||
264 | fromParam = byteStringToPeerId | ||
265 | |||
266 | instance FromParam Word32 where | ||
267 | fromParam = readMaybe . BC.unpack | ||
268 | |||
269 | instance FromParam Word64 where | ||
270 | fromParam = readMaybe . BC.unpack | ||
271 | |||
272 | instance FromParam Int where | ||
273 | fromParam = readMaybe . BC.unpack | ||
241 | 274 | ||
242 | textToPeerId :: Text -> Maybe PeerId | 275 | instance FromParam PortNumber where |
243 | textToPeerId = undefined | 276 | fromParam bs = fromIntegral <$> (fromParam bs :: Maybe Word32) |
244 | 277 | ||
245 | textToPortNumber :: Text -> Maybe PortNumber | 278 | instance FromParam Event where |
246 | textToPortNumber = undefined | 279 | fromParam bs = case BC.uncons bs of |
280 | Nothing -> Nothing | ||
281 | Just (x, xs) -> readMaybe $ BC.unpack $ BC.cons (Char.toUpper x) xs | ||
247 | 282 | ||
248 | textToHostAddress :: Text -> Maybe HostAddress | 283 | withError e = maybe (Left e) Right |
249 | textToHostAddress = undefined | ||
250 | 284 | ||
251 | textToNumWant :: Text -> Maybe Int | 285 | reqParam param xs = do |
252 | textToNumWant = undefined | 286 | val <- withError (Missing param) $ L.lookup (paramName param) xs |
287 | withError (Invalid param val) (fromParam val) | ||
253 | 288 | ||
254 | textToEvent :: Text -> Maybe Event | 289 | optParam param ps |
255 | textToEvent = undefined | 290 | | Just x <- L.lookup (paramName param) ps |
291 | = pure <$> withError (Invalid param x) (fromParam x) | ||
292 | | otherwise = pure Nothing | ||
256 | 293 | ||
257 | paramName :: QueryParam -> Text | 294 | parseProgress :: SimpleQuery -> Either ParamParseFailure Progress |
258 | paramName ParamInfoHash = "info_hash" | 295 | parseProgress params = Progress |
259 | paramName ParamPeerId = "peer_id" | 296 | <$> reqParam ParamDownloaded params |
260 | paramName ParamPort = "port" | 297 | <*> reqParam ParamLeft params |
298 | <*> reqParam ParamUploaded params | ||
261 | 299 | ||
262 | -- | Parse announce request from a decoded query string. | 300 | -- | Parse announce request from a query string. |
263 | parseAnnounceQuery :: [(Text, Text)] -> Either ParamParseFailure AnnounceQuery | 301 | parseAnnounceQuery :: SimpleQuery -> Either ParamParseFailure AnnounceQuery |
264 | parseAnnounceQuery params = AnnounceQuery | 302 | parseAnnounceQuery params = AnnounceQuery |
265 | <$> reqParam ParamInfoHash textToInfoHash params | 303 | <$> reqParam ParamInfoHash params |
266 | <*> reqParam ParamPeerId textToPeerId params | 304 | <*> reqParam ParamPeerId params |
267 | <*> reqParam ParamPort textToPortNumber params | 305 | <*> reqParam ParamPort params |
268 | <*> progress params | 306 | <*> parseProgress params |
269 | <*> optParam ParamIP textToHostAddress params | 307 | <*> optParam ParamIP params |
270 | <*> optParam ParamNumWant textToNumWant params | 308 | <*> optParam ParamNumWant params |
271 | <*> optParam ParamEvent textToEvent params | 309 | <*> optParam ParamEvent params |
272 | where | ||
273 | withError e = maybe (Left e) Right | ||
274 | reqParam param p = withError (Missing param) . L.lookup (paramName param) | ||
275 | >=> \x -> withError (Invalid param x) (p x) | ||
276 | |||
277 | optParam param p ps | ||
278 | | Just x <- L.lookup (paramName param) ps | ||
279 | = pure <$> withError (Invalid param x) (p x) | ||
280 | | otherwise = pure Nothing | ||
281 | |||
282 | progress = undefined | ||
283 | ip = undefined | ||
284 | numwant = undefined | ||
285 | event = undefined | ||
286 | 310 | ||
287 | -- TODO add extension datatype | 311 | -- TODO add extension datatype |
288 | 312 | ||