This commit looks at the downloaded file, verifies the hashes of
the downloaded pieces, marks them as "have" pieces and proceeds
to download those that are not in its posession.
module FuncTorrent.Fileops
(createDummyFile,
module FuncTorrent.Fileops
(createDummyFile,
+ writeFileAtOffset,
+ readFileAtOffset
) where
import Prelude hiding (writeFile)
import System.IO (withFile, hSeek, IOMode(..), SeekMode(..))
) where
import Prelude hiding (writeFile)
import System.IO (withFile, hSeek, IOMode(..), SeekMode(..))
-import Data.ByteString (ByteString, writeFile, hPut)
+import System.Directory (doesFileExist)
+import Data.ByteString (ByteString, writeFile, hPut, hGet)
import qualified Data.ByteString.Char8 as BC (replicate)
createDummyFile :: FilePath -> Int -> IO ()
import qualified Data.ByteString.Char8 as BC (replicate)
createDummyFile :: FilePath -> Int -> IO ()
-createDummyFile path size =
- writeFile path (BC.replicate size '\0')
+createDummyFile path size = do
+ dfe <- doesFileExist path
+ if dfe
+ then return ()
+ else
+ writeFile path (BC.replicate size '\0')
-- write into a file at a specific offet
writeFileAtOffset :: FilePath -> Integer -> ByteString -> IO ()
-- write into a file at a specific offet
writeFileAtOffset :: FilePath -> Integer -> ByteString -> IO ()
withFile path ReadWriteMode (\h -> do
hSeek h AbsoluteSeek offset
hPut h block)
withFile path ReadWriteMode (\h -> do
hSeek h AbsoluteSeek offset
hPut h block)
+readFileAtOffset :: FilePath -> Integer -> Integer -> IO ByteString
+readFileAtOffset path offset len =
+ withFile path ReadWriteMode (\h -> do
+ hSeek h AbsoluteSeek offset
+ hGet h (fromInteger len))
import Prelude hiding (lookup, concat, replicate, splitAt, take, filter)
import System.IO (Handle, BufferMode(..), hSetBuffering)
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 Control.Monad.State
import Data.Bits
import Data.Word (Word8)
import Data.ByteString (ByteString, unpack, concat, hGet, hPut, take, empty)
import qualified Data.ByteString.Char8 as BC (length)
import Network (connectTo, PortID(..))
import Control.Monad.State
import Data.Bits
import Data.Word (Word8)
-import Data.Map (Map, fromList, toList, (!), mapWithKey, adjust, filter)
+import Data.Map (Map, fromList, toList, (!), mapWithKey, traverseWithKey, adjust, filter)
import qualified Crypto.Hash.SHA1 as SHA1 (hash)
import Safe (headMay)
import FuncTorrent.Metainfo (Info(..), Metainfo(..))
import FuncTorrent.Utils (splitN, splitNum)
import qualified Crypto.Hash.SHA1 as SHA1 (hash)
import Safe (headMay)
import FuncTorrent.Metainfo (Info(..), Metainfo(..))
import FuncTorrent.Utils (splitN, splitNum)
-import FuncTorrent.Fileops (createDummyFile, writeFileAtOffset)
+import FuncTorrent.Fileops (createDummyFile, 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
+ where
+ f k v = do
+ let offset = if k == 0 then 0 else k * len (pieceMap ! (k - 1))
+ isHashValid <- (flip verifyHash) (hash v) <$> (readFileAtOffset filePath offset (len v))
+ if isHashValid
+ then return $ v { dlstate = Have }
+ else return $ v
+
havePiece :: PieceMap -> Integer -> Bool
havePiece pm index =
dlstate (pm ! index) == Have
havePiece :: PieceMap -> Integer -> Bool
havePiece pm index =
dlstate (pm ! index) == Have
fileLen = lengthInBytes (info m)
fileName = name (info m)
pieceStatus = initPieceMap pieceHash fileLen pLen
fileLen = lengthInBytes (info m)
fileName = name (info m)
pieceStatus = initPieceMap pieceHash fileLen pLen
+ pieceStatus' <- updatePieceMap fileName pieceStatus
createDummyFile fileName (fromIntegral fileLen)
createDummyFile fileName (fromIntegral fileLen)
- _ <- runStateT (msgLoop pieceStatus fileName) pstate
+ _ <- runStateT (msgLoop pieceStatus' fileName) pstate
return ()
msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
return ()
msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
case torrentToMetainfo torrentStr of
Left e -> logError e log
Right m -> do
case torrentToMetainfo torrentStr of
Left e -> logError e log
Right m -> do
- log "Input File OK"
- log $ "Downloading file : " ++ name (info m)
-
+ 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.
log $ "starting server"
(serverSock, (PortNumber portnum)) <- Server.start
log $ "server started on " ++ show portnum
log $ "starting server"
(serverSock, (PortNumber portnum)) <- Server.start
log $ "server started on " ++ show portnum