torrent-10000.1.3/0000755000000000000000000000000007346545000011702 5ustar0000000000000000torrent-10000.1.3/CHANGELOG0000644000000000000000000000261407346545000013117 0ustar0000000000000000haskell-torrent (10000.1.3) unstable; urgency=medium * Update to cabal-version: >= 1.10 so Hackage will accept uploads again. -- Joey Hess Tue, 21 Mar 2023 20:44:19 -0400 haskell-torrent (10000.1.2) unstable; urgency=medium * Updated to build without deprecation warnings from bencode-0.6.1.0 -- Joey Hess Tue, 21 Mar 2023 20:43:50 -0400 haskell-torrent (10000.1.1) unstable; urgency=medium * Fix build with pre-AMP base. -- Joey Hess Sun, 19 Mar 2017 12:25:49 -0400 haskell-torrent (10000.1.0) unstable; urgency=medium * Don't crash when parsing a torrent with no announce value. Torrents commonly have only an announce-list. * tAnnounce converted to Maybe ByteString (API change) -- Joey Hess Sun, 19 Mar 2017 01:02:16 -0400 haskell-torrent (10000.0.1) unstable; urgency=medium * Added showTorrent. -- Joey Hess Fri, 11 Nov 2016 15:06:46 -0400 haskell-torrent (10000.0.0) unstable; urgency=medium * Taking over upstream maintenance of this package. * Updated to build with the current haskell ecosystem. * Removed SHA1 implementation for simplicity, and removed iInfoHash from Torrent. * Use the PVP rather than date-based version. * Some data type changes, including using Integer rather than Int for file sizes. -- Joey Hess Thu, 18 Dec 2014 13:15:42 -0400 torrent-10000.1.3/LICENSE0000644000000000000000000000302107346545000012703 0ustar0000000000000000Copyright (c) 2005-2007, David Himmelstrup All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of David Himmelstrup nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. torrent-10000.1.3/Setup.hs0000644000000000000000000000005707346545000013340 0ustar0000000000000000import Distribution.Simple main = defaultMain torrent-10000.1.3/src/Data/0000755000000000000000000000000007346545000013342 5ustar0000000000000000torrent-10000.1.3/src/Data/Torrent.hs0000644000000000000000000000717707346545000015347 0ustar0000000000000000-- | BitTorrent metainfo files -- -- {-# LANGUAGE DeriveDataTypeable #-} module Data.Torrent ( Torrent(..) , TorrentInfo(..) , TorrentFile(..) , readTorrent , serializeTorrent , torrentSize , showTorrent ) where import Data.BEncode import Data.BEncode.Reader import Data.Binary import Data.Generics import Data.Maybe import Control.Applicative import qualified Data.ByteString.Lazy as BS import Data.ByteString.Lazy (ByteString) import qualified Data.Map as Map data Torrent = Torrent { tAnnounce :: Maybe ByteString , tAnnounceList :: [ByteString] , tComment :: ByteString , tCreatedBy :: Maybe ByteString , tInfo :: TorrentInfo } deriving (Show, Read, Typeable, Data) data TorrentInfo = SingleFile { tLength :: Integer , tName :: ByteString , tPieceLength :: Integer , tPieces :: ByteString } | MultiFile { tFiles :: [TorrentFile] , tName :: ByteString , tPieceLength :: Integer , tPieces :: ByteString } deriving (Show, Read, Typeable, Data) data TorrentFile = TorrentFile { fileLength :: Integer , filePath :: [ByteString] } deriving (Show, Read, Typeable, Data) instance Binary Torrent where put = put . serializeTorrent get = do e <- get case readTorrent e of Left err -> fail $ "Failed to parse torrent: " ++ err Right t -> return t -- | Size of the files in the torrent. torrentSize :: Torrent -> Integer torrentSize torrent = case tInfo torrent of s@SingleFile{} -> tLength s MultiFile{tFiles=files} -> sum (map fileLength files) readTorrent :: ByteString -> Either String Torrent readTorrent inp = case bRead inp of Nothing -> Left "Not BEncoded" Just be -> runBReader parseTorrent be parseTorrent :: BReader Torrent parseTorrent = do announce <- optional $ dict "announce" bbytestring creator <- optional $ dict "created by" bbytestring dict "info" $ do name <- dict "name" bbytestring pLen <- dict "piece length" bint pieces <- dict "pieces" bbytestring torrentInfo <- parseTorrentInfo name pLen pieces return $ Torrent announce [] BS.empty creator torrentInfo parseTorrentInfo :: ByteString -> Integer -> ByteString -> BReader TorrentInfo parseTorrentInfo name pLen pieces = single <|> multi where single = do len <- dict "length" bint return $ SingleFile len name pLen pieces multi = do files <- dict "files" $ list $ do len <- dict "length" bint filePaths <- dict "path" (list bbytestring) return $ TorrentFile len filePaths return $ MultiFile files name pLen pieces serializeTorrent :: Torrent -> BEncode serializeTorrent torrent = BDict $ Map.fromList $ catMaybes [ fmap (\b -> ("announce", BString b)) (tAnnounce torrent) , Just ("comment", BString $ tComment torrent) , Just ("info", info) ] where info = BDict $ Map.fromList $ [ ("name", BString $ tName (tInfo torrent)) , ("pieces", BString $ tPieces (tInfo torrent)) , ("piece length", BInt $ tPieceLength (tInfo torrent)) ] ++ case tInfo torrent of SingleFile len _ _ _ -> [ ("length", BInt len) ] MultiFile files _ _ _ -> [ ("files", BList $ map serfile files) ] serfile file = BDict $ Map.fromList [ ("length", BInt (fileLength file)) , ("path", BList (map BString $ filePath file)) ] -- | generates a torrent file -- -- Due to lexographical ordering requirements of BEncoded data, this -- should generate the same ByteString that readTorrent read to generate -- the Torrent. However, torrent files may contain extensions and -- nonstandard fields that prevent that from holding for all torrent files. showTorrent :: Torrent -> ByteString showTorrent = bPack . serializeTorrent torrent-10000.1.3/src/Data/Torrent/0000755000000000000000000000000007346545000014777 5ustar0000000000000000torrent-10000.1.3/src/Data/Torrent/Scrape.hs0000644000000000000000000000453207346545000016554 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Data.Torrent.Scrape -- Copyright : (c) Lemmih 2005 -- License : BSD-like -- -- Maintainer : lemmih@gmail.com -- Stability : experimental -- Portability : portable -- -- ----------------------------------------------------------------------------- {-# LANGUAGE PatternGuards #-} module Data.Torrent.Scrape ( ScrapeInfo(..) , parseScrapeInfo , scrapeUrl ) where import Data.Char import Data.BEncode import qualified Data.ByteString.Lazy.Char8 as BS import Data.ByteString.Lazy (ByteString) import System.FilePath import qualified Data.Map as Map data ScrapeInfo = ScrapeInfo { scrapeSeeds :: Integer , scrapeLeechers :: Integer } deriving (Read,Show) parseScrapeInfo :: ByteString -> Maybe ScrapeInfo parseScrapeInfo bs = case bRead bs of Just (BDict dict) -> do BDict files <- Map.lookup "files" dict [BDict dict'] <- return (Map.elems files) BInt seeders <- Map.lookup "complete" dict' BInt peers <- Map.lookup "incomplete" dict' return $ ScrapeInfo { scrapeSeeds = seeders , scrapeLeechers = peers } _ -> Nothing scrapeUrl :: ByteString -> [String] -> Maybe String scrapeUrl _hash [] = Nothing scrapeUrl hash (announce:rs) = case splitFileName announce of (path,file_) | (file,ext) <- splitExtension file_ , ("announce",rest) <- break (=='?') file -> let info_hash = urlEncode (BS.unpack hash) file' = "scrape" ++ if null rest then "?info_hash="++info_hash else "&info_hash="++info_hash in Just (path file' <.> ext) _ -> scrapeUrl hash rs urlEncode :: String -> String urlEncode [] = [] urlEncode s = foldr worker [] s where worker c cs = if isReservedChar c then let (a, b) = ord c `divMod` 16 in '%' : intToDigit a : intToDigit b : cs else c : cs isReservedChar x = x < '0' || x > '9' && x < 'A' || x > 'Z' && x < 'a' || x > 'z' torrent-10000.1.3/torrent.cabal0000644000000000000000000000123107346545000014360 0ustar0000000000000000Name: torrent Version: 10000.1.3 Cabal-Version: >= 1.10 Maintainer: Joey Hess Author: Lemmih Copyright: 2005-2007, Lemmih License-File: LICENSE License: BSD3 Build-Type: Simple Category: Network Extra-Source-Files: CHANGELOG Synopsis: BitTorrent file parser and generater Library GHC-Options: -Wall -fno-warn-tabs Hs-Source-Dirs: src Default-Language: Haskell2010 Exposed-Modules: Data.Torrent Data.Torrent.Scrape Build-Depends: base >= 4.5, base < 5, binary, bencode >= 0.6.1.0, filepath, containers, bytestring, syb source-repository head type: git location: git://git.joeyh.name/haskell-torrent.git