packages feed

torrent 2007.10.27 → 10000.0.0

raw patch · 8 files changed

+134/−388 lines, 8 filesdep +bytestringdep +containersdep +sybdep −networkdep ~basesetup-changednew-uploader

Dependencies added: bytestring, containers, syb

Dependencies removed: network

Dependency ranges changed: base

Files

+ CHANGELOG view
@@ -0,0 +1,11 @@+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 <id@joeyh.name>  Thu, 18 Dec 2014 13:15:42 -0400
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple-main = defaultMainWithHooks defaultUserHooks++main = defaultMain
− cbits/sha1.c
@@ -1,166 +0,0 @@-/*- * sha1.c- *- * Originally witten by Steve Reid <steve@edmweb.com>- * - * Modified by Aaron D. Gifford <agifford@infowest.com>- *- * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN- *- * The original unmodified version is available at:- *    ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c- *- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 AUTHOR(S) 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.- */--#include "sha1.h"-#include <string.h>--#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))--/* blk0() and blk() perform the initial expand. */-/* I got the idea of expanding during the round function from SSLeay */--#ifdef LITTLE_ENDIAN-#define blk0(i) (block->l[i] = (rol(block->l[i],24)&(sha1_quadbyte)0xFF00FF00) \-	|(rol(block->l[i],8)&(sha1_quadbyte)0x00FF00FF))-#else-#define blk0(i) block->l[i]-#endif--#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \-	^block->l[(i+2)&15]^block->l[i&15],1))--/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);--typedef union _BYTE64QUAD16 {-	sha1_byte c[64];-	sha1_quadbyte l[16];-} BYTE64QUAD16;--/* Utility function for easy use in Haskell */-void SHA1(sha1_byte *data, unsigned int len, sha1_byte digest[SHA1_DIGEST_LENGTH])-{-  SHA_CTX context;-  SHA1_Init(&context);-  SHA1_Update(&context,data,len);-  SHA1_Final(digest,&context);-}--/* Hash a single 512-bit block. This is the core of the algorithm. */-void SHA1_Transform(sha1_quadbyte state[5], sha1_byte buffer[64]) {-	sha1_quadbyte	a, b, c, d, e;-	BYTE64QUAD16	*block;--	block = (BYTE64QUAD16*)buffer;-	/* Copy context->state[] to working vars */-	a = state[0];-	b = state[1];-	c = state[2];-	d = state[3];-	e = state[4];-	/* 4 rounds of 20 operations each. Loop unrolled. */-	R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);-	R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);-	R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);-	R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);-	R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);-	R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);-	R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);-	R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);-	R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);-	R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);-	R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);-	R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);-	R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);-	R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);-	R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);-	R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);-	R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);-	R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);-	R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);-	R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);-	/* Add the working vars back into context.state[] */-	state[0] += a;-	state[1] += b;-	state[2] += c;-	state[3] += d;-	state[4] += e;-	/* Wipe variables */-	a = b = c = d = e = 0;-}---/* SHA1_Init - Initialize new context */-void SHA1_Init(SHA_CTX* context) {-	/* SHA1 initialization constants */-	context->state[0] = 0x67452301;-	context->state[1] = 0xEFCDAB89;-	context->state[2] = 0x98BADCFE;-	context->state[3] = 0x10325476;-	context->state[4] = 0xC3D2E1F0;-	context->count[0] = context->count[1] = 0;-}--/* Run your data through this. */-void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len) {-	unsigned int	i, j;--	j = (context->count[0] >> 3) & 63;-	if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;-	context->count[1] += (len >> 29);-	if ((j + len) > 63) {-	    memcpy(&context->buffer[j], data, (i = 64-j));-	    SHA1_Transform(context->state, context->buffer);-	    for ( ; i + 63 < len; i += 64) {-	        SHA1_Transform(context->state, &data[i]);-	    }-	    j = 0;-	}-	else i = 0;-	memcpy(&context->buffer[j], &data[i], len - i);-}---/* Add padding and return the message digest. */-void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context) {-	sha1_quadbyte	i, j;-	sha1_byte	finalcount[8];--	for (i = 0; i < 8; i++) {-	    finalcount[i] = (sha1_byte)((context->count[(i >= 4 ? 0 : 1)]-	     >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */-	}-	SHA1_Update(context, (sha1_byte *)"\200", 1);-	while ((context->count[0] & 504) != 448) {-	    SHA1_Update(context, (sha1_byte *)"\0", 1);-	}-	/* Should cause a SHA1_Transform() */-	SHA1_Update(context, finalcount, 8);-	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {-	    digest[i] = (sha1_byte)-	     ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);-	}-	/* Wipe variables */-	i = j = 0;-	memset(context->buffer, 0, SHA1_BLOCK_LENGTH);-	memset(context->state, 0, SHA1_DIGEST_LENGTH);-	memset(context->count, 0, 8);-	memset(&finalcount, 0, 8);-}-
− cbits/sha1.h
@@ -1,75 +0,0 @@-/*- * sha.h- *- * Originally taken from the public domain SHA1 implementation- * written by by Steve Reid <steve@edmweb.com>- * - * Modified by Aaron D. Gifford <agifford@infowest.com>- *- * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN- *- * The original unmodified version is available at:- *    ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c- *- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 AUTHOR(S) 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.- */--#ifndef __SHA1_H__-#define __SHA1_H__--#ifdef __cplusplus-extern "C" {-#endif--/* Define this if your machine is LITTLE_ENDIAN, otherwise #undef it: */-#define LITTLE_ENDIAN--/* Make sure you define these types for your architecture: */-typedef unsigned int sha1_quadbyte;	/* 4 byte type */-typedef unsigned char sha1_byte;	/* single byte type */--/*- * Be sure to get the above definitions right.  For instance, on my- * x86 based FreeBSD box, I define LITTLE_ENDIAN and use the type- * "unsigned long" for the quadbyte.  On FreeBSD on the Alpha, however,- * while I still use LITTLE_ENDIAN, I must define the quadbyte type- * as "unsigned int" instead.- */--#define SHA1_BLOCK_LENGTH	64-#define SHA1_DIGEST_LENGTH	20--/* The SHA1 structure: */-typedef struct _SHA_CTX {-	sha1_quadbyte	state[5];-	sha1_quadbyte	count[2];-	sha1_byte	buffer[SHA1_BLOCK_LENGTH];-} SHA_CTX;--#ifndef NOPROTO-void SHA1(sha1_byte *data, unsigned int len, sha1_byte digest[SHA1_DIGEST_LENGTH]);-void SHA1_Init(SHA_CTX *context);-void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len);-void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX* context);-#else-void SHA1_Init();-void SHA1_Update();-void SHA1_Final();-#endif--#ifdef	__cplusplus-}-#endif--#endif-
src/Data/Torrent.hs view
@@ -1,119 +1,117 @@-{-# OPTIONS -fglasgow-exts #-}-module Data.Torrent-    ( Torrent(..)-    , TorrentInfo(..)-    , TorrentFile(..)-    , readTorrent      -- :: ByteString -> Either String Torrent-    , serializeTorrent-    , torrentSize      -- :: Torrent -> Int-    ) where+{-# LANGUAGE DeriveDataTypeable #-} --- import Network.URI+module Data.Torrent+	( Torrent(..)+	, TorrentInfo(..)+	, TorrentFile(..)+	, readTorrent+	, serializeTorrent+	, torrentSize+	) where  import Data.BEncode import Data.BEncode.Parser-import Data.Torrent.SHA1-import Data.Maybe import Data.Binary import Data.Generics -import qualified Data.ByteString.Char8 as BS-import Data.ByteString (ByteString)+import qualified Data.ByteString.Lazy as BS+import Data.ByteString.Lazy (ByteString)  import qualified Data.Map as Map  data Torrent-    = Torrent-    { tAnnounce     :: ByteString-    , tAnnounceList :: [ByteString]-    , tComment      :: ByteString-    , tCreatedBy    :: Maybe ByteString-    , tInfo         :: TorrentInfo-    , tInfoHash     :: ByteString-    } deriving (Show, Read, Typeable, Data)+	= Torrent+		{ tAnnounce     :: ByteString+		, tAnnounceList :: [ByteString]+		, tComment      :: ByteString+		, tCreatedBy    :: Maybe ByteString+		, tInfo         :: TorrentInfo+		}+	deriving (Show, Read, Typeable, Data)  data TorrentInfo-    = SingleFile-    { tLength      :: Int-    , tName        :: ByteString-    , tPieceLength :: Int-    , tPieces      :: ByteString }-    | MultiFile-    { tFiles       :: [TorrentFile]-    , tName        :: ByteString-    , tPieceLength :: Int-    , tPieces      :: ByteString-    } deriving (Show, Read, Typeable, Data)+	= 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 :: Int-    , filePath   :: [ByteString]-    } deriving (Show, Read, Typeable, Data)+	= 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---torrentSize :: Torrent -> Int-torrentSize torrent-    = case tInfo torrent of-        s@SingleFile{} -> tLength s-        MultiFile{tFiles=files} -> sum (map fileLength files)+	put = put . serializeTorrent+	get = do+		e <- get+		case readTorrent e of+			Left err -> fail $ "Failed to parse torrent: " ++ err+			Right t  -> return t -{--buri :: BParser BEncode -> BParser URI-buri p = do str <- bstring p-            case parseURI str of-              Nothing -> fail $ "Expected URI: " ++ str-              Just uri -> return uri--}+-- | 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 -> runParser parseTorrent be+readTorrent inp = case bRead inp of+	Nothing -> Left "Not BEncoded"+	Just be -> runParser parseTorrent be  parseTorrent :: BParser Torrent-parseTorrent =-    do announce <- bbytestring $ dict "announce"-       creator  <- optional $ bbytestring $ dict "created by"-       info     <- dict "info"-       setInput info-       name     <- bbytestring $ dict "name"-       pLen     <- bint $ dict "piece length"-       pieces   <- bbytestring $ dict "pieces"-       torrentInfo      <- parseTorrentInfo name pLen pieces-       let infoHash = sha1 (BS.pack $ bShow info "")-       return $ Torrent announce [] BS.empty creator torrentInfo infoHash+parseTorrent = do+	announce <- bbytestring $ dict "announce"+	creator <- optional $ bbytestring $ dict "created by"+	info <- dict "info"+	setInput info+	name <- bbytestring $ dict "name"+	pLen <- bint $ dict "piece length"+	pieces <- bbytestring $ dict "pieces"+	torrentInfo <- parseTorrentInfo name pLen pieces+	return $ Torrent announce [] BS.empty creator torrentInfo -parseTorrentInfo :: ByteString -> Int -> ByteString -> BParser TorrentInfo-parseTorrentInfo name pLen pieces-    = do len <- bint $ dict "length"-         return $ SingleFile len name pLen pieces-      <|>-      do files <- list "files" $ do len <- bint $ dict "length"-                                    filePaths <- list "path" $ bbytestring token-                                    return $ TorrentFile len filePaths-         return $ MultiFile files name pLen pieces+parseTorrentInfo :: ByteString -> Integer -> ByteString -> BParser TorrentInfo+parseTorrentInfo name pLen pieces = single <|> multi+  where+	single = do+		len <- bint $ dict "length"+		return $ SingleFile len name pLen pieces+	multi = do+		files <- list "files" $ do+			len <- bint $ dict "length"+			filePaths <- list "path" $ bbytestring token+			return $ TorrentFile len filePaths+		return $ MultiFile files name pLen pieces  serializeTorrent :: Torrent -> BEncode-serializeTorrent torrent-    = BDict $ Map.fromList [("announce",BString $ tAnnounce torrent)-                           ,("comment", BString $ tComment torrent)-                           ,("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 $ flip map files $ \file ->-                                                                               BDict $ Map.fromList [("length", BInt (fileLength file))-                                                                                                    ,("path", BList (map BString $ filePath file))]-                                                                          )]+serializeTorrent torrent = BDict $ Map.fromList+	[ ("announce", BString $ tAnnounce torrent)+	, ("comment", BString $ tComment torrent)+	, ("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))+		]
− src/Data/Torrent/SHA1.hs
@@ -1,32 +0,0 @@--------------------------------------------------------------------------------- |--- Module      :  Data.Torrent.SHA1--- Copyright   :  (c) Lemmih 2005--- License     :  BSD-like------ Maintainer  :  lemmih@gmail.com--- Stability   :  experimental--- Portability :  portable----------------------------------------------------------------------------------module Data.Torrent.SHA1 (sha1) where--import Foreign-import Foreign.C-import System.IO.Unsafe--import qualified Data.ByteString as BS-import qualified Data.ByteString.Base as BS-import Data.ByteString (ByteString)----void SHA1(sha1_byte *data, unsigned int len, sha1_byte digest[SHA1_DIGEST_LENGTH]);-foreign import ccall safe "SHA1" c_sha1 :: CString -> Int -> CString -> IO ()--sha1 :: ByteString -> ByteString-sha1 fs = unsafePerformIO $-          BS.unsafeUseAsCString fs $ \cstr ->-              do ret <- mallocBytes 20-                 c_sha1 cstr (BS.length fs) ret-                 fp <- newForeignPtr finalizerFree (castPtr ret)-                 return (BS.fromForeignPtr fp 20)-
src/Data/Torrent/Scrape.hs view
@@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- |--- Module      :  Data.Torrent.SHA1+-- Module      :  Data.Torrent.Scrape -- Copyright   :  (c) Lemmih 2005 -- License     :  BSD-like --@@ -8,7 +8,11 @@ -- Stability   :  experimental -- Portability :  portable --+-- <http://en.wikipedia.org/wiki/Tracker_scrape> -----------------------------------------------------------------------------++{-# LANGUAGE PatternGuards #-}+ module Data.Torrent.Scrape     ( ScrapeInfo(..)     , parseScrapeInfo@@ -17,14 +21,14 @@  import Data.Char import Data.BEncode-import qualified Data.ByteString.Char8 as BS-import Data.ByteString (ByteString)+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    :: Int-    , scrapeLeechers :: Int+    { scrapeSeeds    :: Integer+    , scrapeLeechers :: Integer     } deriving (Read,Show)  parseScrapeInfo :: ByteString -> Maybe ScrapeInfo
torrent.cabal view
@@ -1,20 +1,25 @@ Name: torrent-Version: 2007.10.27-Maintainer: Lemmih (lemmih@gmail.com)-Author: Lemmih (lemmih@gmail.com)+Version: 10000.0.0+Cabal-Version: >= 1.6+Maintainer: Joey Hess <id@joeyh.name>+Author: Lemmih <lemmih@gmail.com> Copyright: 2005-2007, Lemmih License-File: LICENSE License: BSD3-Build-Depends: base, binary, bencode >=0.2, filepath, network+Build-Type: Simple Category: Network-Synopsis: BitTorrent file parser.-Tested-with: GHC ==6.6-Extensions: ForeignFunctionInterface, PatternGuards-Exposed-Modules:-  Data.Torrent-  Data.Torrent.Scrape-  Data.Torrent.SHA1-GHC-Options: -Wall -Werror-Hs-Source-Dirs: src-C-Sources: cbits/sha1.c-Extra-source-files: cbits/sha1.h+Extra-Source-Files: CHANGELOG+Synopsis: BitTorrent file parser and generater++Library+  GHC-Options: -Wall+  Hs-Source-Dirs: src+  Exposed-Modules:+    Data.Torrent+    Data.Torrent.Scrape+  Build-Depends: base >= 4.5, base < 5, binary, bencode >=0.2,+    filepath, containers, bytestring, syb++source-repository head+  type: git+  location: git://git.kitenet.net/haskell-torrent.git