Move parts of the download resume code into Main, since it avoids
duplication of some code and makes the flow, a lot more easy to
understand.
{-# LANGUAGE OverloadedStrings #-}
module FuncTorrent.Peer
(Peer(..),
{-# LANGUAGE OverloadedStrings #-}
module FuncTorrent.Peer
(Peer(..),
+ bytesDownloaded,
+ initPieceMap,
+ pieceMapFromFile
) where
import Prelude hiding (lookup, concat, replicate, splitAt, take, filter)
import System.IO (Handle, BufferMode(..), hSetBuffering)
) 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 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 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
import FuncTorrent.PeerMsgs (Peer(..), PeerMsg(..), sendMsg, getMsg, genHandshakeMsg)
data PState = PState { handle :: Handle
hashes = splitN 20 pieceHash
pLengths = (splitNum fileLen pieceLen)
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
pieceMapFromFile :: FilePath -> PieceMap -> IO PieceMap
pieceMapFromFile filePath pieceMap = do
traverseWithKey f pieceMap
then (pd { peers = p : peers pd })
else pd) pieceStatus
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
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 ()
return ()
msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
import System.IO (hSetBuffering, BufferMode ( NoBuffering ))
import FuncTorrent.Metainfo (Metainfo)
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
-- 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)
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
(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 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 = []
splitN :: Int -> BC.ByteString -> [BC.ByteString]
splitN n bs | BC.null bs = []
{-# LANGUAGE OverloadedStrings #-}
module Main where
{-# 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 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 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.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)
import qualified FuncTorrent.Server as Server
import FuncTorrent.Tracker (peers, getTrackerResponse)
case torrentToMetainfo torrentStr of
Left e -> logError e log
Right m -> do
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.
-- 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"
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
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)
Right peerList -> do
log $ "Peers List : " ++ (show . peers $ peerList)
let p1 = head (peers peerList)
- handlePeerMsgs p1 peerId m
+ handlePeerMsgs p1 peerId m pieceMap