summaryrefslogtreecommitdiff
path: root/examples/GetTorrent.hs
blob: 9a203bf03abf4efd279ebcc05cc96da15a3d4a99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
{-# LANGUAGE RecordWildCards #-}
module Main (main) where
import Control.Applicative
import Control.Concurrent
import Control.Monad
import Control.Monad.Trans
import Data.Conduit as C
import Data.Conduit.List as C
import Data.Default
import Options.Applicative
import System.Exit
import System.FilePath

import Data.Torrent
import Data.Torrent.InfoHash
import Network.BitTorrent.Core
import Network.BitTorrent.DHT.Session
import Network.BitTorrent.DHT as DHT
import Network.BitTorrent.Exchange.Message
import Network.BitTorrent.Exchange.Wire


data Params = Params
  { topic    :: InfoHash
  , thisNode :: NodeAddr IPv4
  , bootNode :: NodeAddr IPv4
  , buckets  :: Int
  } deriving Show

paramsParser :: Parser Params
paramsParser = Params
  <$> option (long    "infohash" <> short 'i'
           <> metavar "SHA1"     <> help "infohash of torrent file")
  <*> option (long    "port"     <> short 'p'
           <> value def          <> showDefault
           <> metavar "NUM"      <> help "port number to bind"
             )
  <*> option (long    "boot"     <> short 'b'
           <> metavar "NODE"     <> help "bootstrap node address"
             )
  <*> option (long    "bucket"   <> short 'n'
           <> value 2            <> showDefault
           <> metavar "NUM"      <> help "number of buckets to maintain"
             )

programInfo :: ParserInfo Params
programInfo = info (helper <*> paramsParser)
   ( fullDesc
  <> progDesc ""
  <> header   "gettorrent - get torrent file by infohash"
   )

exchangeTorrent :: PeerAddr IP -> InfoHash -> IO InfoDict
exchangeTorrent addr ih = do
  pid <- genPeerId
  var <- newEmptyMVar
  let hs = Handshake def (toCaps [ExtExtended]) ih pid
  connectWire hs addr (toCaps [ExtMetadata]) $ do
    infodict <- getMetadata
    liftIO $ putMVar var infodict
  takeMVar var

getTorrent :: Params -> IO ()
getTorrent Params {..} = do
  dht (def { optBucketCount = buckets }) thisNode $ do
    bootstrap [bootNode]
    DHT.lookup topic $$ C.mapM_ $ \ peers -> do
      liftIO $ forM_ peers $ \ peer -> do
        infodict <- exchangeTorrent (IPv4 <$> peer) topic
        let torrent = nullTorrent infodict -- TODO add tNodes, tCreated, etc?
        toFile (show topic <.> torrentExt) torrent
        exitSuccess

main :: IO ()
main = execParser programInfo >>= getTorrent