From e82e69136d468dac0b133e4f8c99d5f91cbdc598 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Tue, 25 Jul 2017 19:34:13 +0530 Subject: [PATCH] metainfo: support multifile .torrent files --- src/FuncTorrent/Metainfo.hs | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/FuncTorrent/Metainfo.hs b/src/FuncTorrent/Metainfo.hs index 4cc6ed1..801b53d 100644 --- a/src/FuncTorrent/Metainfo.hs +++ b/src/FuncTorrent/Metainfo.hs @@ -27,18 +27,22 @@ module FuncTorrent.Metainfo import Prelude hiding (lookup) import Data.ByteString.Char8 (ByteString, unpack) import Data.Map as M ((!), lookup) +import Data.List (intersperse) import Crypto.Hash.SHA1 (hash) import Data.Maybe (maybeToList) import FuncTorrent.Bencode (BVal(..), encode, decode, bstrToString, bValToInteger) --- only single file mode supported for the time being. +data FileMeta = FileMeta { lengthInBytes :: !Integer + , md5sum :: !(Maybe String) + , path :: String + } deriving (Eq, Show) + data Info = Info { pieceLength :: !Integer , pieces :: !ByteString , private :: !(Maybe Integer) , name :: !String - , lengthInBytes :: !Integer - , md5sum :: !(Maybe String) + , filemeta :: [FileMeta] } deriving (Eq, Show) data Metainfo = Metainfo { info :: !(Maybe Info) @@ -55,16 +59,40 @@ bvalToInfo (Bdict m) = let (Bint pieceLength') = m ! "piece length" (Bstr pieces') = m ! "pieces" private' = Nothing (Bstr name') = m ! "name" + -- is the key "files" present? If so, it is a multi-file torrent + -- if not, it is a single file torrent. + filesIfMulti = lookup "files" m (Bint length') = m ! "length" md5sum' = Nothing - in Just Info { pieceLength = pieceLength' - , pieces = pieces' - , private = private' - , name = unpack name' - , lengthInBytes = length' - , md5sum = md5sum'} + partialInfo = Info { pieceLength = pieceLength' + , pieces = pieces' + , private = private' + , name = unpack name' + } + in + case filesIfMulti of + Nothing -> let (Bint length') = m ! "length" + filemeta' = FileMeta { lengthInBytes = length' + , md5sum = Nothing + , path = unpack name' } + in Just (partialInfo { filemeta = [filemeta'] }) + Just (Blist files) -> mapM toFileMeta files >>= + \filemeta' -> + Just partialInfo { filemeta = filemeta' } bvalToInfo _ = Nothing +toFileMeta :: BVal -> Maybe FileMeta +toFileMeta (Bdict fm) = let (Bint length) = fm ! "length" + (Blist pathElems) = fm ! "path" + pathStrings = fmap bstrToString pathElems + in + sequence pathStrings >>= + \pathList -> let path' = concat $ intersperse "/" pathList + in Just (FileMeta { lengthInBytes = length + , md5sum = Nothing + , path = path' }) +toFileMeta _ = Nothing + mkMetaInfo :: BVal -> Either String Metainfo mkMetaInfo (Bdict m) = let info' = bvalToInfo $ m ! "info" -- 2.37.2