{-# LANGUAGE OverloadedStrings #-}
module FuncTorrent.Peer
(Peer(..),
+ PieceMap,
handlePeerMsgs,
- bytesDownloaded
+ bytesDownloaded,
+ initPieceMap,
+ pieceMapFromFile
) where
import Prelude hiding (lookup, concat, replicate, splitAt, take, filter)
import System.IO (Handle, BufferMode(..), hSetBuffering)
-import System.Directory (doesFileExist)
import Data.ByteString (ByteString, unpack, concat, hGet, hPut, take, empty)
import qualified Data.ByteString.Char8 as BC (length)
import Network (connectTo, PortID(..))
import Safe (headMay)
import FuncTorrent.Metainfo (Info(..), Metainfo(..))
-import FuncTorrent.Utils (splitN, splitNum, createDummyFile, writeFileAtOffset, readFileAtOffset)
+import FuncTorrent.Utils (splitN, splitNum, writeFileAtOffset, readFileAtOffset)
import FuncTorrent.PeerMsgs (Peer(..), PeerMsg(..), sendMsg, getMsg, genHandshakeMsg)
data PState = PState { handle :: Handle
hashes = splitN 20 pieceHash
pLengths = (splitNum fileLen pieceLen)
-updatePieceMap :: FilePath -> PieceMap -> IO PieceMap
-updatePieceMap filePath pieceMap = do
- dfe <- doesFileExist filePath
- -- TODO: this is not enough, file should have the same size as well
- if dfe
- then pieceMapFromFile filePath pieceMap
- else return pieceMap
-
pieceMapFromFile :: FilePath -> PieceMap -> IO PieceMap
pieceMapFromFile filePath pieceMap = do
traverseWithKey f pieceMap
then (pd { peers = p : peers pd })
else pd) pieceStatus
-handlePeerMsgs :: Peer -> String -> Metainfo -> IO ()
-handlePeerMsgs p peerId m = do
+handlePeerMsgs :: Peer -> String -> Metainfo -> PieceMap -> IO ()
+handlePeerMsgs p peerId m pieceMap = do
h <- connectToPeer p
doHandshake h p (infoHash m) peerId
let pstate = toPeerState h p False False True True
- pieceHash = pieces (info m)
- pLen = pieceLength (info m)
- fileLen = lengthInBytes (info m)
- fileName = name (info m)
- pieceStatus = initPieceMap pieceHash fileLen pLen
- pieceStatus' <- updatePieceMap fileName pieceStatus
- createDummyFile fileName (fromIntegral fileLen)
- _ <- runStateT (msgLoop pieceStatus' fileName) pstate
+ filePath = name (info m)
+ _ <- runStateT (msgLoop pieceMap filePath) pstate
return ()
msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
import System.IO (hSetBuffering, BufferMode ( NoBuffering ))
import FuncTorrent.Metainfo (Metainfo)
-import FuncTorrent.Peer (handlePeerMsgs, Peer(..))
+import FuncTorrent.Peer (handlePeerMsgs, Peer(..), PieceMap)
-- server is listening on any port from 6881 - 6889
-- return the port number used
sock <- listenOn $ PortNumber $ fromIntegral (head portnums)
return (sock, PortNumber $ head portnums)
-run :: Socket -> String -> Metainfo -> IO ()
-run listenSock peerid m = forever $ do
+run :: Socket -> String -> Metainfo -> PieceMap -> IO ()
+run listenSock peerid m pieceMap = forever $ do
(handle, ip, port) <- accept listenSock
let peer = Peer "" ip (fromIntegral port)
hSetBuffering handle NoBuffering
- forkIO $ handlePeerMsgs peer peerid m
+ forkIO $ handlePeerMsgs peer peerid m pieceMap
import System.Directory (doesFileExist)
import Data.ByteString (ByteString, writeFile, hPut, hGet)
import qualified Data.ByteString.Char8 as BC
-import qualified Data.ByteString.Char8 as BC (replicate)
splitN :: Int -> BC.ByteString -> [BC.ByteString]
splitN n bs | BC.null bs = []
{-# LANGUAGE OverloadedStrings #-}
module Main where
-import Prelude hiding (log, length, readFile, getContents)
+import Prelude hiding (log, length, readFile, getContents, replicate, writeFile)
import Control.Concurrent (forkIO)
-import Data.ByteString.Char8 (ByteString, getContents, readFile, unpack)
+import Data.ByteString.Char8 (ByteString, getContents, readFile, writeFile, unpack, replicate)
import Network (PortID (PortNumber))
import System.Environment (getArgs)
import System.Exit (exitSuccess)
import FuncTorrent.Logger (initLogger, logMessage, logStop)
import FuncTorrent.Metainfo (Info(..), Metainfo(..), torrentToMetainfo)
-import FuncTorrent.Peer (handlePeerMsgs)
+import FuncTorrent.Peer (initPieceMap, handlePeerMsgs, pieceMapFromFile)
import qualified FuncTorrent.Server as Server
import FuncTorrent.Tracker (peers, getTrackerResponse)
case torrentToMetainfo torrentStr of
Left e -> logError e log
Right m -> do
- let p = name (info m)
- log $ "Downloading file : " ++ p
-- if we had downloaded the file before (partly or completely)
-- then we should check the current directory for the existence
-- of the file and then update the map of each piece' availability.
-- This can be donw by reading each piece and verifying the checksum.
-- If the checksum does not match, we don't have that piece.
+ let filePath = name (info m) -- really this is just the file name, not file path
+ fileLen = lengthInBytes (info m)
+ pieceHash = pieces (info m)
+ pLen = pieceLength (info m)
+ defaultPieceMap = initPieceMap pieceHash fileLen pLen
+ log $ "Downloading file : " ++ filePath
+ dfe <- doesFileExist filePath
+ pieceMap <- if dfe
+ then
+ pieceMapFromFile filePath defaultPieceMap
+ else do
+ -- create a dummy file
+ _ <- writeFile filePath (replicate (fromIntegral fileLen) '\0')
+ return defaultPieceMap
log $ "starting server"
(serverSock, (PortNumber portnum)) <- Server.start
log $ "server started on " ++ show portnum
log "Trying to fetch peers"
- forkIO $ Server.run serverSock peerId m
+ forkIO $ Server.run serverSock peerId m pieceMap
log $ "Trackers: " ++ head (announceList m)
trackerResp <- getTrackerResponse portnum peerId m
case trackerResp of
Right peerList -> do
log $ "Peers List : " ++ (show . peers $ peerList)
let p1 = head (peers peerList)
- handlePeerMsgs p1 peerId m
+ handlePeerMsgs p1 peerId m pieceMap
logStop logR