module Network.IPFS.Peer
  ( all
  , rawList
  , connect
  , connectRetry
  , disconnect
  , getExternalAddress
  ) where

import qualified RIO.List                   as List
import qualified RIO.Text                   as Text

import qualified Network.IP.Addr            as Addr

import           Text.Regex

import qualified Network.IPFS.Internal.UTF8 as UTF8
import           Network.IPFS.Prelude       hiding (all)

import           Network.IPFS.Info.Types
import           Network.IPFS.Local.Class   as IPFS
import           Network.IPFS.Peer.Error    as IPFS.Peer
import           Network.IPFS.Peer.Types
import qualified Network.IPFS.Process.Error as Process
import qualified Network.IPFS.Types         as IPFS

all :: MonadLocalIPFS m => m (Either IPFS.Peer.Error [IPFS.Peer])
all :: forall (m :: * -> *). MonadLocalIPFS m => m (Either Error [Peer])
all = forall (m :: * -> *).
MonadLocalIPFS m =>
m (Either Error RawMessage)
rawList forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
  Right RawMessage
raw -> case forall a. Textable a => a -> Either UnicodeException Text
UTF8.encode RawMessage
raw of
    Left  UnicodeException
_    -> forall a b. a -> Either a b
Left  forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Error
DecodeFailure forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show RawMessage
raw
    Right Text
text -> forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Text -> Peer
IPFS.Peer forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> [Text]
Text.lines Text
text
  Left Error
err -> forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnknownErr forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow Error
err

rawList :: MonadLocalIPFS m => m (Either Process.Error Process.RawMessage)
rawList :: forall (m :: * -> *).
MonadLocalIPFS m =>
m (Either Error RawMessage)
rawList = forall (m :: * -> *).
MonadLocalIPFS m =>
[String] -> RawMessage -> m (Either Error RawMessage)
IPFS.runLocal [String
"bootstrap", String
"list"] RawMessage
""

connect :: MonadLocalIPFS m => Peer -> m (Either IPFS.Peer.Error ())
connect :: forall (m :: * -> *).
MonadLocalIPFS m =>
Peer -> m (Either Error ())
connect peer :: Peer
peer@(Peer Text
peerID) = forall (m :: * -> *).
MonadLocalIPFS m =>
[String] -> RawMessage -> m (Either Error RawMessage)
IPFS.runLocal [String
"swarm", String
"connect"] (Text -> RawMessage
UTF8.textToLazyBS Text
peerID) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
  Left Error
_  -> forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Peer -> Error
CannotConnect Peer
peer
  Right RawMessage
_ -> forall a b. b -> Either a b
Right ()

disconnect :: MonadLocalIPFS m => Peer -> m (Either IPFS.Peer.Error ())
disconnect :: forall (m :: * -> *).
MonadLocalIPFS m =>
Peer -> m (Either Error ())
disconnect peer :: Peer
peer@(Peer Text
peerID) =
  forall (m :: * -> *).
MonadLocalIPFS m =>
[String] -> RawMessage -> m (Either Error RawMessage)
IPFS.runLocal [String
"swarm", String
"disconnect"] (Text -> RawMessage
UTF8.textToLazyBS Text
peerID) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    Left Error
_  -> forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Peer -> Error
CannotDisconnect Peer
peer
    Right RawMessage
_ -> forall a b. b -> Either a b
Right ()

connectRetry :: MonadLocalIPFS m => Peer -> Natural -> m (Either IPFS.Peer.Error ())
connectRetry :: forall (m :: * -> *).
MonadLocalIPFS m =>
Peer -> Natural -> m (Either Error ())
connectRetry Peer
peer Natural
0 = forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Peer -> Error
CannotConnect Peer
peer
connectRetry Peer
peer Natural
tries = forall (m :: * -> *).
MonadLocalIPFS m =>
Peer -> m (Either Error ())
connect Peer
peer forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
  Right ()
_   -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. b -> Either a b
Right ()
  Left Error
_err -> forall (m :: * -> *).
MonadLocalIPFS m =>
Peer -> Natural -> m (Either Error ())
connectRetry Peer
peer (Natural
tries forall a. Num a => a -> a -> a
- Natural
1)

peerAddressRe :: Regex
peerAddressRe :: Regex
peerAddressRe = String -> Regex
mkRegex String
"^/ip[46]/([a-zA-Z0-9.:]*)/"

-- | Retrieve just the ip address from a peer address
extractIPfromPeerAddress :: String -> Maybe String
extractIPfromPeerAddress :: String -> Maybe String
extractIPfromPeerAddress String
peer = Regex -> String -> Maybe [String]
matchRegex Regex
peerAddressRe String
peer forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. [a] -> Maybe a
List.headMaybe

-- | True if a given peer address is externally accessable
isExternalIPv4 :: Text -> Bool
isExternalIPv4 :: Text -> Bool
isExternalIPv4 Text
ip = forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False Bool -> Bool
not Maybe Bool
isReserved
  where
    isReserved :: Maybe Bool
    isReserved :: Maybe Bool
isReserved = do
      String
ipAddress  <- String -> Maybe String
extractIPfromPeerAddress forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack Text
ip
      IP4
normalized <- forall a. Read a => String -> Maybe a
readMaybe String
ipAddress
      return (IP4 -> Range4
Addr.ip4Range IP4
normalized forall a. Eq a => a -> a -> Bool
== Range4
Addr.ReservedIP4)

-- | Filter a list of peers to include only the externally accessable addresses
filterExternalPeers :: [Peer] -> [Peer]
filterExternalPeers :: [Peer] -> [Peer]
filterExternalPeers = forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> Bool
isExternalIPv4 forall b c a. (b -> c) -> (a -> b) -> a -> c
. Peer -> Text
peer)

-- | Get all external ipfs peer addresses
getExternalAddress :: MonadLocalIPFS m => m (Either IPFS.Peer.Error [Peer])
getExternalAddress :: forall (m :: * -> *). MonadLocalIPFS m => m (Either Error [Peer])
getExternalAddress =
  forall (m :: * -> *).
MonadLocalIPFS m =>
[String] -> RawMessage -> m (Either Error RawMessage)
IPFS.runLocal [String
"id"] RawMessage
"" forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Left Error
err ->
      forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnknownErr forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow Error
err

    Right RawMessage
raw ->
      RawMessage
raw
        forall a b. a -> (a -> b) -> b
|> forall a. FromJSON a => RawMessage -> Maybe a
decode
        forall a b. a -> (a -> b) -> b
|> forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] Info -> [Peer]
addresses
        forall a b. a -> (a -> b) -> b
|> forall a b. b -> Either a b
Right forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Peer] -> [Peer]
filterExternalPeers
        forall a b. a -> (a -> b) -> b
|> forall (f :: * -> *) a. Applicative f => a -> f a
pure