-- |
-- Module      : Network.Connection.Types
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : portable
--
-- connection types
module Network.Connection.Types
where

import Control.Concurrent.MVar (MVar)

import Data.ByteString (ByteString)
import Data.Default
import Data.X509.CertificateStore

import Network.Socket (PortNumber, Socket)
import qualified Network.TLS as TLS
import qualified Network.TLS.Extra as TLS

import System.IO (Handle)

-- | Simple backend enumeration, either using a raw connection or a tls connection.
data ConnectionBackend
    = ConnectionStream Handle
    | ConnectionSocket Socket
    | ConnectionTLS TLS.Context

-- | Hostname This could either be a name string (punycode encoded) or an ipv4/ipv6
type HostName = String

-- | Connection Parameters to establish a Connection.
--
-- The strict minimum is an hostname and the port.
--
-- If you need to establish a TLS connection, you should make sure
-- connectionUseSecure is correctly set.
--
-- If you need to connect through a SOCKS, you should make sure
-- connectionUseSocks is correctly set.
data ConnectionParams = ConnectionParams
    { connectionHostname :: HostName
    -- ^ host name to connect to.
    , connectionPort :: PortNumber
    -- ^ port number to connect to.
    , connectionUseSecure :: Maybe TLSSettings
    -- ^ optional TLS parameters.
    , connectionUseSocks :: Maybe ProxySettings
    -- ^ optional Proxy/Socks configuration.
    }

-- | Proxy settings for the connection.
--
-- OtherProxy handles specific application-level proxies like HTTP proxies.
--
-- The simple SOCKS settings is just the hostname and portnumber of the SOCKS proxy server.
--
-- That's for now the only settings in the SOCKS package,
-- socks password, or any sort of other authentications is not yet implemented.
data ProxySettings
    = SockSettingsSimple HostName PortNumber
    | SockSettingsEnvironment (Maybe String)
    | OtherProxy HostName PortNumber

type SockSettings = ProxySettings

-- | TLS Settings that can be either expressed as simple settings,
-- or as full blown TLS.Params settings.
--
-- Unless you need access to parameters that are not accessible through the
-- simple settings, you should use TLSSettingsSimple.
data TLSSettings
    = TLSSettingsSimple
        { settingDisableCertificateValidation :: Bool
        -- ^ Disable certificate verification completely,
        --   this make TLS/SSL vulnerable to a MITM attack.
        --   not recommended to use, but for testing.
        , settingDisableSession :: Bool
        -- ^ Disable session management. TLS/SSL connections
        --   will always re-established their context.
        --   Not Implemented Yet.
        , settingUseServerName :: Bool
        -- ^ Use server name extension. Not Implemented Yet.
        , settingClientSupported :: TLS.Supported
        -- ^ Used for the 'TLS.clientSupported'
        --   member of 'TLS.ClientParams'.
        }
    | -- \^ Simple TLS settings. recommended to use.

      -- | full blown TLS Settings directly using TLS.Params. for power users.
      TLSSettings TLS.ClientParams
    deriving (Show)

instance Default TLSSettings where
    def =
        TLSSettingsSimple
            False
            False
            False
            def{TLS.supportedCiphers = TLS.ciphersuite_default}

type ConnectionID = (HostName, PortNumber)

-- | This opaque type represent a connection to a destination.
data Connection = Connection
    { connectionBackend :: MVar ConnectionBackend
    , connectionBuffer :: MVar (Maybe ByteString)
    -- ^ this is set to 'Nothing' on EOF
    , connectionID :: ConnectionID
    -- ^ return a simple tuple of the port and hostname that we're connected to.
    }

-- | Shared values (certificate store, sessions, ..) between connections
--
-- At the moment, this is only strictly needed to shared sessions and certificates
-- when using a TLS enabled connection.
data ConnectionContext = ConnectionContext
    { globalCertificateStore :: !CertificateStore
    }
