]> git.rkrishnan.org Git - functorrent.git/blob - src/FuncTorrent/Metainfo.hs
metainfo: remove a redundant "show"
[functorrent.git] / src / FuncTorrent / Metainfo.hs
1 {-
2  - Copyright (C) 2015-2016 Ramakrishnan Muthukrishnan <ram@rkrishnan.org>
3  -
4  - This file is part of FuncTorrent.
5  -
6  - FuncTorrent is free software; you can redistribute it and/or modify
7  - it under the terms of the GNU General Public License as published by
8  - the Free Software Foundation; either version 3 of the License, or
9  - (at your option) any later version.
10  -
11  - FuncTorrent is distributed in the hope that it will be useful,
12  - but WITHOUT ANY WARRANTY; without even the implied warranty of
13  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  - GNU General Public License for more details.
15  -
16  - You should have received a copy of the GNU General Public License
17  - along with FuncTorrent; if not,  see <http://www.gnu.org/licenses/>
18  -}
19
20 {-# LANGUAGE OverloadedStrings #-}
21 module FuncTorrent.Metainfo
22     (Info(..),
23      Metainfo(..),
24      torrentToMetainfo
25     ) where
26
27 import Prelude hiding (lookup)
28 import Data.ByteString.Char8 (ByteString, unpack)
29 import Data.Map as M ((!), lookup)
30 import Data.List (intersperse)
31 import Crypto.Hash.SHA1 (hash)
32 import Data.Maybe (maybeToList)
33
34 import FuncTorrent.Bencode (BVal(..), encode, decode, bstrToString, bValToInteger)
35
36 data FileMeta = FileMeta { lengthInBytes :: !Integer
37                          , md5sum :: !(Maybe String)
38                          , path :: String
39                          } deriving (Eq, Show)
40
41 data Info = Info { pieceLength :: !Integer
42                  , pieces :: !ByteString
43                  , private :: !(Maybe Integer)
44                  , name :: !String
45                  , filemeta :: [FileMeta]
46                  } deriving (Eq, Show)
47
48 data Metainfo = Metainfo { info :: !(Maybe Info)
49                          , announceList :: ![String]
50                          , creationDate :: !(Maybe Integer)
51                          , comment :: !(Maybe String)
52                          , createdBy :: !(Maybe String)
53                          , encoding :: !(Maybe String)
54                          , infoHash :: !ByteString
55                          } deriving (Eq, Show)
56
57 bvalToInfo :: BVal -> Maybe Info
58 bvalToInfo (Bdict minfo) = let (Bint pieceLength') = minfo ! "piece length"
59                                (Bstr pieces') = minfo ! "pieces"
60                                private' = Nothing
61                                (Bstr name') = minfo ! "name"
62                                -- is the key "files" present? If so, it is a multi-file torrent
63                                -- if not, it is a single file torrent.
64                                filesIfMulti = lookup "files" minfo
65                                partialInfo = Info { pieceLength = pieceLength'
66                                                   , pieces = pieces'
67                                                   , private = private'
68                                                   , name = unpack name'
69                                                   , filemeta = []
70                                                   }
71                            in
72                              case filesIfMulti of
73                                Nothing -> let (Bint length') = minfo ! "length"
74                                               filemeta' = FileMeta { lengthInBytes = length'
75                                                                    , md5sum = Nothing
76                                                                    , path = unpack name' }
77                                           in Just (partialInfo { filemeta = [filemeta'] })
78                                Just (Blist files) -> mapM toFileMeta files >>=
79                                                      \filemeta' ->
80                                                        Just partialInfo { filemeta = filemeta' }
81                                Just _ -> Nothing
82 bvalToInfo _ = Nothing
83
84 toFileMeta :: BVal -> Maybe FileMeta
85 toFileMeta (Bdict fm) = let (Bint length') = fm ! "length"
86                             (Blist pathElems) = fm ! "path"
87                             pathStrings = fmap bstrToString pathElems
88                         in
89                           sequence pathStrings >>=
90                           \pathList -> let path' = concat $ intersperse "/" pathList
91                                        in Just (FileMeta { lengthInBytes = length'
92                                                          , md5sum = Nothing
93                                                          , path = path' })
94 toFileMeta _ = Nothing
95
96 mkMetaInfo :: BVal   -> Either String Metainfo
97 mkMetaInfo (Bdict minfo)  =
98     let info'         = bvalToInfo $ minfo ! "info"
99         announce'     = lookup "announce" minfo
100         announceList' = lookup "announce-list" minfo
101         creationDate' = lookup "creation date" minfo
102         comment'      = lookup "comment" minfo
103         createdBy'    = lookup "created by" minfo
104         encoding'     = lookup "encoding" minfo
105     in Right Metainfo {
106                info         = info'
107              , announceList = maybeToList (announce' >>= bstrToString)
108                               ++ getAnnounceList announceList'
109              , creationDate = bValToInteger =<< creationDate'
110              , comment      = bstrToString  =<< comment'
111              , createdBy    = bstrToString  =<< createdBy'
112              , encoding     = bstrToString  =<< encoding'
113              , infoHash     = hash . encode $ (minfo ! "info")
114              }
115 mkMetaInfo _ = Left "mkMetaInfo: expect an input dict"
116
117 getAnnounceList :: Maybe BVal -> [String]
118 getAnnounceList Nothing = []
119 getAnnounceList (Just (Bint _)) = []
120 getAnnounceList (Just (Bstr _)) = []
121 getAnnounceList (Just (Blist l)) = map (\s -> case s of
122                                                (Bstr s') ->  unpack s'
123                                                (Blist s') -> case s' of
124                                                               [Bstr s''] -> unpack s''
125                                                               _ -> ""
126                                                _ -> "") l
127 getAnnounceList (Just (Bdict _)) = []
128
129 torrentToMetainfo :: ByteString -> Either String Metainfo
130 torrentToMetainfo s =
131   case decode s of
132     Right d -> mkMetaInfo d
133     Left e -> Left ("Cannot parse the torrent file: " ++ show e)
134