summaryrefslogtreecommitdiff
path: root/src/Data/Torrent.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Data/Torrent.hs')
-rw-r--r--src/Data/Torrent.hs89
1 files changed, 44 insertions, 45 deletions
diff --git a/src/Data/Torrent.hs b/src/Data/Torrent.hs
index ba71d334..7c56edf7 100644
--- a/src/Data/Torrent.hs
+++ b/src/Data/Torrent.hs
@@ -144,38 +144,37 @@ module Data.Torrent
144 , renderURN 144 , renderURN
145 ) where 145 ) where
146 146
147import Prelude hiding (sum) 147import Prelude
148import Control.Applicative 148import Control.Applicative
149import Control.DeepSeq 149import Control.DeepSeq
150import Control.Exception 150import Control.Exception
151import Control.Lens hiding (unsnoc) 151import Control.Lens
152import Control.Monad 152import Control.Monad
153import qualified Crypto.Hash.SHA1 as C 153import Crypto.Hash.SHA1 as SHA1
154import qualified Crypto.Hash.SHA1 as SHA1
155import Data.BEncode as BE 154import Data.BEncode as BE
156import Data.BEncode.Types as BE 155import Data.BEncode.Types as BE
157import Data.Bits 156import Data.Bits
158import Data.Bits.Extras 157import Data.Bits.Extras
159import Data.ByteString as BS 158import Data.ByteString as BS
160import Data.ByteString.Base16 as Base16 159import Data.ByteString.Base16 as Base16
161import Data.ByteString.Base32 as Base32 160import Data.ByteString.Base32 as Base32
162import Data.ByteString.Base64 as Base64 161import Data.ByteString.Base64 as Base64
163import qualified Data.ByteString.Char8 as BC (pack, unpack) 162import Data.ByteString.Char8 as BC (pack, unpack)
164import qualified Data.ByteString.Lazy as BL 163import Data.ByteString.Lazy as BL
165import Data.Char 164import Data.Char
166import Data.Convertible 165import Data.Convertible
167import Data.Default 166import Data.Default
168import Data.Foldable as F 167import Data.Foldable as F
169import Data.Hashable as Hashable 168import Data.Hashable as Hashable
170import Data.Int 169import Data.Int
171import qualified Data.List as L 170import Data.List as L
172import Data.Map as M 171import Data.Map as M
173import Data.Maybe 172import Data.Maybe
174import Data.Serialize as S 173import Data.Serialize as S
175import Data.String 174import Data.String
176import Data.Text as T 175import Data.Text as T
177import Data.Text.Encoding as T 176import Data.Text.Encoding as T
178import Data.Text.Read 177import Data.Text.Read
179import Data.Time.Clock.POSIX 178import Data.Time.Clock.POSIX
180import Data.Typeable 179import Data.Typeable
181import Network (HostName) 180import Network (HostName)
@@ -330,7 +329,7 @@ data FileInfo a = FileInfo {
330 -- ^ Length of the file in bytes. 329 -- ^ Length of the file in bytes.
331 330
332 -- TODO unpacked MD5 sum 331 -- TODO unpacked MD5 sum
333 , fiMD5Sum :: !(Maybe ByteString) 332 , fiMD5Sum :: !(Maybe BS.ByteString)
334 -- ^ 32 character long MD5 sum of the file. Used by third-party 333 -- ^ 32 character long MD5 sum of the file. Used by third-party
335 -- tools, not by bittorrent protocol itself. 334 -- tools, not by bittorrent protocol itself.
336 335
@@ -361,7 +360,7 @@ instance NFData a => NFData (FileInfo a) where
361 rnf FileInfo {..} = rnf fiName 360 rnf FileInfo {..} = rnf fiName
362 {-# INLINE rnf #-} 361 {-# INLINE rnf #-}
363 362
364instance BEncode (FileInfo [ByteString]) where 363instance BEncode (FileInfo [BS.ByteString]) where
365 toBEncode FileInfo {..} = toDict $ 364 toBEncode FileInfo {..} = toDict $
366 "length" .=! fiLength 365 "length" .=! fiLength
367 .: "md5sum" .=? fiMD5Sum 366 .: "md5sum" .=? fiMD5Sum
@@ -377,20 +376,20 @@ instance BEncode (FileInfo [ByteString]) where
377 376
378type Put a = a -> BDict -> BDict 377type Put a = a -> BDict -> BDict
379 378
380putFileInfoSingle :: Data.Torrent.Put (FileInfo ByteString) 379putFileInfoSingle :: Data.Torrent.Put (FileInfo BS.ByteString)
381putFileInfoSingle FileInfo {..} cont = 380putFileInfoSingle FileInfo {..} cont =
382 "length" .=! fiLength 381 "length" .=! fiLength
383 .: "md5sum" .=? fiMD5Sum 382 .: "md5sum" .=? fiMD5Sum
384 .: "name" .=! fiName 383 .: "name" .=! fiName
385 .: cont 384 .: cont
386 385
387getFileInfoSingle :: BE.Get (FileInfo ByteString) 386getFileInfoSingle :: BE.Get (FileInfo BS.ByteString)
388getFileInfoSingle = do 387getFileInfoSingle = do
389 FileInfo <$>! "length" 388 FileInfo <$>! "length"
390 <*>? "md5sum" 389 <*>? "md5sum"
391 <*>! "name" 390 <*>! "name"
392 391
393instance BEncode (FileInfo ByteString) where 392instance BEncode (FileInfo BS.ByteString) where
394 toBEncode = toDict . (`putFileInfoSingle` endDict) 393 toBEncode = toDict . (`putFileInfoSingle` endDict)
395 {-# INLINE toBEncode #-} 394 {-# INLINE toBEncode #-}
396 395
@@ -406,7 +405,7 @@ instance Pretty (FileInfo BS.ByteString) where
406 ppMD5 md5 = "MD5 : " <> text (show (Base16.encode md5)) 405 ppMD5 md5 = "MD5 : " <> text (show (Base16.encode md5))
407 406
408-- | Join file path. 407-- | Join file path.
409joinFilePath :: FileInfo [ByteString] -> FileInfo ByteString 408joinFilePath :: FileInfo [BS.ByteString] -> FileInfo BS.ByteString
410joinFilePath = fmap (BS.intercalate "/") 409joinFilePath = fmap (BS.intercalate "/")
411 410
412{----------------------------------------------------------------------- 411{-----------------------------------------------------------------------
@@ -422,15 +421,15 @@ joinFilePath = fmap (BS.intercalate "/")
422data LayoutInfo 421data LayoutInfo
423 = SingleFile 422 = SingleFile
424 { -- | Single file info. 423 { -- | Single file info.
425 liFile :: !(FileInfo ByteString) 424 liFile :: !(FileInfo BS.ByteString)
426 } 425 }
427 | MultiFile 426 | MultiFile
428 { -- | List of the all files that torrent contains. 427 { -- | List of the all files that torrent contains.
429 liFiles :: ![FileInfo [ByteString]] 428 liFiles :: ![FileInfo [BS.ByteString]]
430 429
431 -- | The /suggested/ name of the root directory in which to 430 -- | The /suggested/ name of the root directory in which to
432 -- store all the files. 431 -- store all the files.
433 , liDirName :: !ByteString 432 , liDirName :: !BS.ByteString
434 } deriving (Show, Read, Eq, Typeable) 433 } deriving (Show, Read, Eq, Typeable)
435 434
436makeLensesFor 435makeLensesFor
@@ -482,7 +481,7 @@ isMultiFile _ = False
482{-# INLINE isMultiFile #-} 481{-# INLINE isMultiFile #-}
483 482
484-- | Get name of the torrent based on the root path piece. 483-- | Get name of the torrent based on the root path piece.
485suggestedName :: LayoutInfo -> ByteString 484suggestedName :: LayoutInfo -> BS.ByteString
486suggestedName (SingleFile FileInfo {..}) = fiName 485suggestedName (SingleFile FileInfo {..}) = fiName
487suggestedName MultiFile {..} = liDirName 486suggestedName MultiFile {..} = liDirName
488{-# INLINE suggestedName #-} 487{-# INLINE suggestedName #-}
@@ -520,9 +519,9 @@ flatLayout prefixPath SingleFile { liFile = FileInfo {..} }
520 = [(prefixPath </> BC.unpack fiName, fiLength)] 519 = [(prefixPath </> BC.unpack fiName, fiLength)]
521flatLayout prefixPath MultiFile {..} = L.map mkPath liFiles 520flatLayout prefixPath MultiFile {..} = L.map mkPath liFiles
522 where -- TODO use utf8 encoding in name 521 where -- TODO use utf8 encoding in name
523 mkPath FileInfo {..} = (path, fiLength) 522 mkPath FileInfo {..} = (_path, fiLength)
524 where 523 where
525 path = prefixPath </> BC.unpack liDirName 524 _path = prefixPath </> BC.unpack liDirName
526 </> joinPath (L.map BC.unpack fiName) 525 </> joinPath (L.map BC.unpack fiName)
527 526
528-- | Calculate offset of each file based on its length, incrementally. 527-- | Calculate offset of each file based on its length, incrementally.
@@ -597,7 +596,7 @@ defaultPieceSize x = max minPieceSize $ min maxPieceSize $ toPow2 pc
597-- Piece data 596-- Piece data
598-----------------------------------------------------------------------} 597-----------------------------------------------------------------------}
599 598
600type PieceHash = ByteString 599type PieceHash = BS.ByteString
601 600
602hashsize :: Int 601hashsize :: Int
603hashsize = 20 602hashsize = 20
@@ -632,7 +631,7 @@ hashPiece Piece {..} = SHA1.hashlazy pieceData
632-----------------------------------------------------------------------} 631-----------------------------------------------------------------------}
633 632
634-- | A flat array of SHA1 hash for each piece. 633-- | A flat array of SHA1 hash for each piece.
635newtype HashList = HashList { unHashList :: ByteString } 634newtype HashList = HashList { unHashList :: BS.ByteString }
636 deriving (Show, Read, Eq, BEncode, Typeable) 635 deriving (Show, Read, Eq, BEncode, Typeable)
637 636
638-- | Empty hash list. 637-- | Empty hash list.
@@ -688,7 +687,7 @@ instance BEncode PieceInfo where
688instance Pretty PieceInfo where 687instance Pretty PieceInfo where
689 pretty PieceInfo {..} = "Piece size: " <> int piPieceLength 688 pretty PieceInfo {..} = "Piece size: " <> int piPieceLength
690 689
691slice :: Int -> Int -> ByteString -> ByteString 690slice :: Int -> Int -> BS.ByteString -> BS.ByteString
692slice start len = BS.take len . BS.drop start 691slice start len = BS.take len . BS.drop start
693{-# INLINE slice #-} 692{-# INLINE slice #-}
694 693
@@ -773,7 +772,7 @@ putPrivate True = \ cont -> "private" .=! True .: cont
773 772
774-- | Hash lazy bytestring using SHA1 algorithm. 773-- | Hash lazy bytestring using SHA1 algorithm.
775hashLazyIH :: BL.ByteString -> InfoHash 774hashLazyIH :: BL.ByteString -> InfoHash
776hashLazyIH = either (const (error msg)) id . safeConvert . C.hashlazy 775hashLazyIH = either (const (error msg)) id . safeConvert . SHA1.hashlazy
777 where 776 where
778 msg = "Infohash.hash: impossible: SHA1 is always 20 bytes long" 777 msg = "Infohash.hash: impossible: SHA1 is always 20 bytes long"
779 778
@@ -850,7 +849,7 @@ data Torrent = Torrent
850 -- authority to allow new peers onto the swarm. 849 -- authority to allow new peers onto the swarm.
851 850
852 , tPublisherURL :: !(Maybe URI) 851 , tPublisherURL :: !(Maybe URI)
853 , tSignature :: !(Maybe ByteString) 852 , tSignature :: !(Maybe BS.ByteString)
854 -- ^ The RSA signature of the info dictionary (specifically, the 853 -- ^ The RSA signature of the info dictionary (specifically, the
855 -- encrypted SHA-1 hash of the info dictionary). 854 -- encrypted SHA-1 hash of the info dictionary).
856 } deriving (Show, Eq, Typeable) 855 } deriving (Show, Eq, Typeable)
@@ -1049,15 +1048,15 @@ instance QueryValueLike URN where
1049 1048
1050----------------------------------------------------------------------- 1049-----------------------------------------------------------------------
1051 1050
1052unsnoc :: [a] -> Maybe ([a], a) 1051_unsnoc :: [a] -> Maybe ([a], a)
1053unsnoc [] = Nothing 1052_unsnoc [] = Nothing
1054unsnoc xs = Just (L.init xs, L.last xs) 1053_unsnoc xs = Just (L.init xs, L.last xs)
1055 1054
1056instance Convertible Text URN where 1055instance Convertible Text URN where
1057 safeConvert t = case T.split (== ':') t of 1056 safeConvert t = case T.split (== ':') t of
1058 uriScheme : body 1057 uriScheme : body
1059 | T.toLower uriScheme == "urn" -> 1058 | T.toLower uriScheme == "urn" ->
1060 case unsnoc body of 1059 case _unsnoc body of
1061 Just (namespace, val) -> pure URN 1060 Just (namespace, val) -> pure URN
1062 { urnNamespace = namespace 1061 { urnNamespace = namespace
1063 , urnString = val 1062 , urnString = val