module FuncTorrent.Fileops
(createDummyFile,
- writeFileAtOffset
+ writeFileAtOffset,
+ readFileAtOffset
) 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 ()
-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 ()
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 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.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 FuncTorrent.Fileops (createDummyFile, writeFileAtOffset)
+import FuncTorrent.Fileops (createDummyFile, 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
+ 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
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
+ _ <- runStateT (msgLoop pieceStatus' fileName) pstate
return ()
msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
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