]> git.rkrishnan.org Git - functorrent.git/blob - src/main/Main.hs
b67b6b282160826689a14fa4b82eb5abf9b9e54f
[functorrent.git] / src / main / Main.hs
1 {-# LANGUAGE OverloadedStrings #-}
2 module Main where
3
4 import Prelude hiding (log, length, readFile, getContents, replicate, writeFile)
5
6 import Control.Concurrent (forkIO)
7 import Data.ByteString.Char8 (ByteString, getContents, readFile, writeFile, unpack, replicate)
8 import Network (PortID (PortNumber))
9 import System.Environment (getArgs)
10 import System.Exit (exitSuccess)
11 import System.Directory (doesFileExist)
12 import System.Random (getStdGen, randomRs)
13
14 import FuncTorrent.Logger (initLogger, logMessage, logStop)
15 import FuncTorrent.Metainfo (Info(..), Metainfo(..), torrentToMetainfo)
16 import FuncTorrent.Peer (initPieceMap, handlePeerMsgs, pieceMapFromFile)
17 import qualified FuncTorrent.Server as Server
18 import FuncTorrent.Tracker (peers, getTrackerResponse)
19
20 logError :: String -> (String -> IO ()) -> IO ()
21 logError e logMsg = logMsg $ "parse error: \n" ++ e
22
23 exit :: IO ByteString
24 exit = exitSuccess
25
26 usage :: IO ()
27 usage = putStrLn "usage: functorrent torrent-file"
28
29 parse :: [String] -> IO ByteString
30 parse [] = getContents
31 parse [a] = do
32   fileExist <- doesFileExist a
33   if fileExist
34     then readFile a
35     else error "file does not exist"
36 parse _ = exit
37
38 -- peer id is exactly 20 bytes long.
39 -- peer id starts with '-', followed by 2 char client id'
40 -- followed by 4 ascii digits for version number, followed by
41 -- a '-'. Rest are random digits to fill the 20 bytes.
42 mkPeerID :: IO String
43 mkPeerID = do
44   stdgen <- getStdGen
45   let digits = randomRs (0, 9) stdgen :: [Integer]
46   return $ "-HS9001-" ++ (concatMap show $ take (20 - 8) digits)
47
48 main :: IO ()
49 main = do
50     args <- getArgs
51     logR <- initLogger
52     peerId <- mkPeerID    
53     let log = logMessage logR
54     log "Starting up functorrent"
55     log $ "Parsing arguments " ++ concat args
56     torrentStr <- parse args
57     case torrentToMetainfo torrentStr of
58      Left e -> logError e log
59      Right m -> do
60        -- if we had downloaded the file before (partly or completely)
61        -- then we should check the current directory for the existence
62        -- of the file and then update the map of each piece' availability.
63        -- This can be donw by reading each piece and verifying the checksum.
64        -- If the checksum does not match, we don't have that piece.
65        let filePath = name (info m) -- really this is just the file name, not file path
66            fileLen = lengthInBytes (info m)
67            pieceHash = pieces (info m)
68            pLen = pieceLength (info m)
69            defaultPieceMap = initPieceMap pieceHash fileLen pLen
70        log $ "Downloading file : " ++ filePath
71        dfe <- doesFileExist filePath
72        pieceMap <- if dfe
73                    then
74                      pieceMapFromFile filePath defaultPieceMap
75                    else do
76                      -- create a dummy file
77                      _ <- writeFile filePath (replicate (fromIntegral fileLen) '\0')
78                      return defaultPieceMap
79        log $ "starting server"
80        (serverSock, (PortNumber portnum)) <- Server.start
81        log $ "server started on " ++ show portnum
82        log "Trying to fetch peers"
83        forkIO $ Server.run serverSock peerId m pieceMap
84        log $ "Trackers: " ++ head (announceList m)
85        trackerResp <- getTrackerResponse portnum peerId m
86        case  trackerResp of
87         Left e -> log $ "Error" ++ unpack e
88         Right peerList -> do
89           log $ "Peers List : " ++ (show . peers $ peerList)
90           let p1 = head (peers peerList)
91           handlePeerMsgs p1 peerId m pieceMap True
92     logStop logR