]> git.rkrishnan.org Git - functorrent.git/blob - src/FuncTorrent/Metainfo.hs
metainfo: refactoring
[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 DuplicateRecordFields #-}
21
22 module FuncTorrent.Metainfo
23     (Info(..)
24     , Metainfo(..)
25     , FileInfo(..)
26     , torrentToMetainfo
27     ) where
28
29 import Prelude hiding (lookup)
30 import Data.ByteString.Char8 (ByteString, unpack)
31 import Data.Map as M ((!), lookup)
32 import Data.List (intercalate)
33 import Crypto.Hash.SHA1 (hash)
34 import Data.Maybe (maybeToList)
35
36 import FuncTorrent.Bencode (BVal(..), encode, decode, bstrToString, bValToInteger)
37
38 -- only single file mode supported for the time being.
39 data Info = Info { pieceLength :: !Integer
40                  , pieces :: !ByteString
41                  , private :: !(Maybe Integer)
42                  , files :: ![FileInfo]
43                  } deriving (Eq, Show)
44
45 data FileInfo = FileInfo { lengthInBytes :: Integer
46                          , md5sum :: Maybe String
47                          , path :: String
48                          } deriving (Eq, Show)
49
50 data Metainfo = Metainfo { info :: !Info
51                          , announceList :: ![String]
52                          , creationDate :: !(Maybe Integer)
53                          , comment :: !(Maybe String)
54                          , createdBy :: !(Maybe String)
55                          , encoding :: !(Maybe String)
56                          , infoHash :: !ByteString
57                          } deriving (Eq, Show)
58
59 mkPath :: String -> [BVal] -> String
60 mkPath base xs = base ++ "/" ++ (intercalate "/" $ map (\b -> let (Just s) = bstrToString b in s) xs)
61
62 mkInfo :: BVal -> Maybe Info
63 mkInfo (Bdict m) = let (Bint pieceLength') = m ! "piece length"
64                        (Bstr pieces') = m ! "pieces"
65                        private' = Nothing
66                        bdictfiles = lookup "files" m
67                    in
68                      case bdictfiles of
69                        Just fs -> let (Blist files') = fs
70                                       (Bstr name') = m ! "name"
71                                       dname' = unpack name'
72                                   in
73                                     Just Info { pieceLength = pieceLength'
74                                               , pieces = pieces'
75                                               , private = private'
76                                               , files =
77                                                   map (\(Bdict f) ->
78                                                         let (Just len') = bValToInteger (f ! "length")
79                                                             (Blist ds) = f ! "path"
80                                                             path' = mkPath dname' ds
81                                                         in
82                                                           FileInfo { lengthInBytes = len',
83                                                                      md5sum = Nothing,
84                                                                      path = path'
85                                                                    })
86                                                   files' }
87                        Nothing -> let (Bstr name') = m ! "name"
88                                       (Bint length') = m ! "length"
89                                       md5sum' = Nothing
90                                   in
91                                     Just Info { pieceLength = pieceLength'
92                                               , pieces = pieces'
93                                               , private = private'
94                                               , files = [ FileInfo {
95                                                             path = unpack name',
96                                                             lengthInBytes = length',
97                                                             md5sum = md5sum'
98                                                             }
99                                                         ]
100                                               }
101
102 mkInfo _ = Nothing
103
104 mkMetaInfo :: BVal   -> Either String Metainfo
105 mkMetaInfo (Bdict m)  =
106     let (Just info')  = mkInfo $ m ! "info"
107         announce'     = lookup "announce" m
108         announceList' = lookup "announce-list" m
109         creationDate' = lookup "creation date" m
110         comment'      = lookup "comment" m
111         createdBy'    = lookup "created by" m
112         encoding'     = lookup "encoding" m
113     in Right Metainfo {
114                info         = info'
115              , announceList = maybeToList (announce' >>= bstrToString)
116                               ++ getAnnounceList announceList'
117              , creationDate = bValToInteger =<< creationDate'
118              , comment      = bstrToString  =<< comment'
119              , createdBy    = bstrToString  =<< createdBy'
120              , encoding     = bstrToString  =<< encoding'
121              , infoHash     = hash . encode $ (m ! "info")
122              }
123 mkMetaInfo _ = Left "mkMetaInfo: expect an input dict"
124
125 getAnnounceList :: Maybe BVal -> [String]
126 getAnnounceList Nothing          = []
127 getAnnounceList (Just (Bint _))  = []
128 getAnnounceList (Just (Bstr _))  = []
129 getAnnounceList (Just (Blist l)) = map (\s -> case s of
130                                                (Bstr s') ->  unpack s'
131                                                (Blist s') -> case s' of
132                                                               [Bstr s''] -> unpack s''
133                                                               _ -> ""
134                                                _ -> "") l
135 getAnnounceList (Just (Bdict _)) = []
136
137 torrentToMetainfo :: ByteString -> Either String Metainfo
138 torrentToMetainfo s =
139   case decode s of
140    Right d -> mkMetaInfo d
141    Left e -> Left $ show e