handlePeerMsgs
) where
-import Prelude hiding (lookup, concat, replicate, splitAt, writeFile)
+import Prelude hiding (lookup, concat, replicate, splitAt, writeFile, take)
import System.IO (Handle, BufferMode(..), IOMode(..), SeekMode(..), withFile, hSeek, hSetBuffering)
-import Data.ByteString (ByteString, pack, unpack, concat, hGet, hPut, singleton, writeFile)
+import Data.ByteString (ByteString, pack, unpack, concat, hGet, hPut, singleton, writeFile, take, empty)
import Data.ByteString.Lazy (fromStrict, fromChunks, toStrict)
import qualified Data.ByteString.Char8 as BC (replicate, pack, length)
import Network (connectTo, PortID(..))
import Data.Bits
import Data.Word (Word8)
import Data.Map (Map, fromList, toList, (!), mapWithKey, adjust)
+import qualified Crypto.Hash.SHA1 as SHA1 (hash)
import FuncTorrent.Metainfo (Info(..), Metainfo(..))
import FuncTorrent.Utils (splitN, splitNum)
, hash = h
, len = pLen })
| (i, h, pLen) <- zip3 [0..numPieces] hashes pLengths]
- hashes = splitN (fromIntegral numPieces) pieceHash
+ hashes = splitN 20 pieceHash
havePiece :: PieceMap -> Integer -> Bool
havePiece pm index =
-- write into a file at a specific offet
writeFileAtOffset :: FilePath -> Integer -> ByteString -> IO ()
writeFileAtOffset path offset block =
- withFile path WriteMode $ (\h -> do
- _ <- hSeek h AbsoluteSeek offset
- hPut h block)
+ withFile path ReadWriteMode $ (\h -> do
+ _ <- hSeek h AbsoluteSeek offset
+ hPut h block)
-- recvMsg :: Peer -> Handle -> Msg
msgLoop :: PeerState -> PieceMap -> IO ()
Nothing -> putStrLn "Nothing to download"
Just workPiece -> do
let pLen = len (pieceStatus ! workPiece)
- _ <- downloadPiece (handle pState) workPiece pLen
- -- TODO: verify the hash
+ pBS <- downloadPiece (handle pState) workPiece pLen
+ -- if not $ verifyHash pBS (hash (pieceStatus ! workPiece))
+ -- then
+ -- putStrLn $ "Hash mismatch: " ++ show (hash (pieceStatus ! workPiece)) ++ " vs " ++ show (take 20 (SHA1.hash pBS))
+ -- else do
+ writeFileAtOffset "/tmp/download.file" (workPiece * pLen) pBS
msgLoop pState (adjust (\pieceData -> pieceData { state = Have }) workPiece pieceStatus)
| otherwise = do
msg <- getMsg (handle pState)
BitFieldMsg bss -> do
let pieceList = bitfieldToList (unpack bss)
pieceStatus' = updatePieceAvailability pieceStatus (peer pState) pieceList
- print pieceList
+ putStrLn $ show (length pieceList) ++ " Pieces"
-- for each pieceIndex in pieceList, make an entry in the pieceStatus
-- map with pieceIndex as the key and modify the value to add the peer.
-- download each of the piece in order
pLen = pieceLength (info m)
fileLen = lengthInBytes (info m)
pieceStatus = mkPieceMap numPieces pieceHash (splitNum fileLen pLen)
+ createDummyFile "/tmp/download.file" (fromIntegral fileLen)
msgLoop state pieceStatus
-downloadPiece :: Handle -> Integer -> Integer -> IO [ByteString]
+downloadPiece :: Handle -> Integer -> Integer -> IO ByteString
downloadPiece h index pieceLength = do
let chunks = splitNum pieceLength 16384
- forM (zip [0..] chunks) (\(i, pLen) -> do
- sendMsg h (RequestMsg index (i*pLen) pLen)
- putStrLn $ "--> " ++ "RequestMsg for Piece "
- ++ (show index) ++ ", part: " ++ show i ++ " of length: "
- ++ show pLen
- PieceMsg index begin block <- getMsg h
- putStrLn $ " <-- PieceMsg for Piece: "
- ++ show index
- ++ ", offset: "
- ++ show begin
- return block)
+ liftM concat $ forM (zip [0..] chunks) (\(i, pLen) -> do
+ sendMsg h (RequestMsg index (i*pLen) pLen)
+ putStrLn $ "--> " ++ "RequestMsg for Piece "
+ ++ (show index) ++ ", part: " ++ show i ++ " of length: "
+ ++ show pLen
+ msg <- getMsg h
+ case msg of
+ PieceMsg index begin block -> do
+ putStrLn $ " <-- PieceMsg for Piece: "
+ ++ show index
+ ++ ", offset: "
+ ++ show begin
+ return block
+ _ -> do
+ putStrLn "ignoring irrelevant msg"
+ return empty)
+
+verifyHash :: ByteString -> ByteString -> Bool
+verifyHash bs pieceHash =
+ take 20 (SHA1.hash bs) == pieceHash