]> git.rkrishnan.org Git - functorrent.git/blobdiff - src/FuncTorrent/Metainfo.hs
Bencode, Metainfo: remove warnings and general cleanup
[functorrent.git] / src / FuncTorrent / Metainfo.hs
index 9eb9955bfff5e4463111e75b5d387599d3f09e73..3f860ec1d062d3fe46b24e4e112e8eea393db959 100644 (file)
@@ -1,28 +1,51 @@
+{-
+ - Copyright (C) 2015-2016 Ramakrishnan Muthukrishnan <ram@rkrishnan.org>
+ -
+ - This file is part of FuncTorrent.
+ -
+ - FuncTorrent is free software; you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License as published by
+ - the Free Software Foundation; either version 3 of the License, or
+ - (at your option) any later version.
+ -
+ - FuncTorrent is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with FuncTorrent; if not,  see <http://www.gnu.org/licenses/>
+ -}
+
+{-# LANGUAGE OverloadedStrings #-}
 module FuncTorrent.Metainfo
     (Info(..),
      Metainfo(..),
-     mkInfo,
      torrentToMetainfo
     ) where
 
 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 :: !Info
+data Metainfo = Metainfo { info :: !(Maybe Info)
                          , announceList :: ![String]
                          , creationDate :: !(Maybe Integer)
                          , comment :: !(Maybe String)
@@ -31,24 +54,47 @@ data Metainfo = Metainfo { info :: !Info
                          , infoHash :: !ByteString
                          } deriving (Eq, Show)
 
-mkInfo :: BVal -> Maybe Info
-mkInfo (Bdict m) = let (Bint pieceLength') = m ! "piece length"
-                       (Bstr pieces') = m ! "pieces"
-                       private' = Nothing
-                       (Bstr name') = m ! "name"
-                       (Bint length') = m ! "length"
-                       md5sum' = Nothing
-                   in Just Info { pieceLength = pieceLength'
-                                , pieces = pieces'
-                                , private = private'
-                                , name = unpack name'
-                                , lengthInBytes = length'
-                                , md5sum = md5sum'}
-mkInfo _ = Nothing
+bvalToInfo :: BVal -> Maybe Info
+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
+                           partialInfo = Info { pieceLength = pieceLength'
+                                               , pieces = pieces'
+                                               , private = private'
+                                               , name = unpack name'
+                                               , filemeta = []
+                                               }
+                       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 (Just info')  = mkInfo $ m ! "info"
+    let info'         = bvalToInfo $ m ! "info"
         announce'     = lookup "announce" m
         announceList' = lookup "announce-list" m
         creationDate' = lookup "creation date" m
@@ -65,7 +111,6 @@ mkMetaInfo (Bdict m)  =
              , encoding     = bstrToString  =<< encoding'
              , infoHash     = hash . encode $ (m ! "info")
              }
-
 mkMetaInfo _ = Left "mkMetaInfo: expect an input dict"
 
 getAnnounceList :: Maybe BVal -> [String]
@@ -78,13 +123,10 @@ getAnnounceList (Just (Blist l)) = map (\s -> case s of
                                                               [Bstr s''] -> unpack s''
                                                               _ -> ""
                                                _ -> "") l
-
 getAnnounceList (Just (Bdict _)) = []
 
 torrentToMetainfo :: ByteString -> Either String Metainfo
 torrentToMetainfo s =
   case decode s of
-   Right d ->
-     mkMetaInfo d
-   Left e ->
-     Left $ show e
+   Right d -> mkMetaInfo d
+   Left e -> Left $ show e