+{-
+data InfoPieceMap = { infoLength :: Integer
+ , infoMap :: Map Integer (Maybe ByteString)
+ }
+
+newtype InfoState = InfoState (MVar InfoPieceMap)
+
+-}
+
+
+metadataMsgLoop :: Handle -> InfoState -> IO ()
+metadataMsgLoop h (InfoState st) = do
+ infoState <- readMVar st
+ let metadataLen = infoLength infoState
+ -- send the handshake msg
+ metadata = encode (metadataMsg metadataLen)
+ sendMsg h (ExtendedMsg 0 metadata)
+ -- recv return msg from the peer. Will have 'metadata_size'
+ msg <- getMsg h
+ case msg of
+ ExtendedMsg 0 rBs -> do
+ -- decode rBs
+ let (Right (Bdict msgMap)) = decode rBs
+ (Bdict mVal) = msgMap ! "m" -- which is another dict
+ (Bint metadata_msgID) = mVal ! "ut_metadata"
+ (Bint metadata_size) = msgMap ! "metadata_size"
+ -- divide metadata_size into 16384 sized pieces, find number of pieces
+ (q, r) = metadata_size `divMod` 16384
+ -- pNumLengthPairs = zip [0..q-1] (take q (repeat 16384)) ++ (q, r)
+ -- TODO: corner case where infodict size is a multiple of 16384
+ -- and start sending request msg for each.
+ if metadataLen == 0
+ then -- We don't have any piece. Send request msg for all pieces.
+ mapM_ (\n -> do
+ sendMsg h (ExtendedMsg metadata_msgID (encode (requestMsg n)))
+ dataOrRejectMsg <- getMsg h
+ case dataOrRejectMsg of
+ ExtendedMsg 3 payload -> do
+ -- bencoded dict followed by XXXXXX
+ infoState <- takeMVar st
+ let (Right (Bdict bval, pieceData)) = decodeWithLeftOvers payload
+ (Bint pieceIndex) = bval ! "piece"
+ payloadLen = length (unpack pieceData)
+ infoMapVal = infoMap infoState
+ putMVar st infoState {
+ infoMap = insert pieceIndex (Just payload) infoMapVal }
+ )
+ [0..q]
+ else
+ return () -- TODO: reject for now
+ where
+ metadataMsg 0 = Bdict (fromList [("m", Bdict (fromList [("ut_metadata", (Bint 3))]))])
+ metadataMsg l = Bdict (fromList [("m", Bdict (fromList [("ut_metadata", (Bint 3))])),
+ ("metadata_size", (Bint l))])
+ requestMsg i = Bdict (fromList [("msg_type", (Bint 0)), ("piece", (Bint i))])
+ rejectmsg i = Bdict (fromList [("msg_type", (Bint 2)), ("piece", (Bint i))])
+
+doesPeerSupportExtendedMsg :: ByteString -> Bool
+doesPeerSupportExtendedMsg bs = take 1 (drop 5 bs) == singleton 0x10