{-# Language OverloadedStrings #-}
module Client.EventLoop.Network
( clientResponse
) where
import Client.Commands
import Client.Commands.Interpolation
import Client.Configuration.ServerSettings
import Client.Configuration.Sts
import Client.Network.Async
import Client.Network.Connect
import Client.State
import Client.State.Focus
import Client.State.Network
import Control.Lens
import Control.Monad
import Data.Text (Text)
import Data.Time
import Irc.Codes
import Irc.Commands
import Irc.Identifier
import Irc.Message
import qualified Client.Authentication.Ecdsa as Ecdsa
import qualified Data.Text as Text
import qualified Data.Text.Read as Text
clientResponse :: ZonedTime -> IrcMsg -> NetworkState -> ClientState -> IO ClientState
clientResponse now irc cs st =
case irc of
Reply RPL_WELCOME _ ->
do let focus = NetworkFocus (view csNetwork cs)
st' <- foldM (processConnectCmd now cs)
(set clientFocus focus st)
(view (csSettings . ssConnectCmds) cs)
return $! set clientFocus (view clientFocus st) st'
Reply ERR_LINKCHANNEL (_ : src : dst : _)
| let network = view csNetwork cs
, view clientFocus st == ChannelFocus network (mkId src) ->
return $! set clientFocus (ChannelFocus network (mkId dst)) st
Authenticate challenge
| AS_EcdsaWaitChallenge <- view csAuthenticationState cs ->
processSaslEcdsa now challenge cs st
Cap (CapLs _ caps)
| Just stsVal <- join (lookup "sts" caps) -> processSts stsVal cs st
Cap (CapNew caps)
| Just stsVal <- join (lookup "sts" caps) -> processSts stsVal cs st
_ -> return st
processSts ::
Text ->
NetworkState ->
ClientState ->
IO ClientState
processSts txt cs st =
case view (csSettings . ssTls) cs of
_ | views (csSettings . ssSts) not cs -> return st
UseInsecure | Just port <- mbPort -> upgradeConnection port
UseTls | Just duration <- mbDuration -> setStsPolicy duration
UseInsecureTls | Just duration <- mbDuration -> setStsPolicy duration
_ -> return st
where
entries = splitEntry <$> Text.splitOn "," txt
mbPort = readInt =<< lookup "port" entries
mbDuration = readInt =<< lookup "duration" entries
splitEntry e =
case Text.break ('=' ==) e of
(a, b) -> (a, Text.drop 1 b)
upgradeConnection port =
do abortConnection StsUpgrade (view csSocket cs)
addConnection 0 (view csLastReceived cs) (Just port) (view csNetwork cs) st
setStsPolicy duration =
do now <- getCurrentTime
let host = Text.pack (view (csSettings . ssHostName) cs)
port = fromIntegral (ircPort (view csSettings cs))
policy = StsPolicy
{ _stsExpiration = addUTCTime (fromIntegral duration) now
, _stsPort = port }
st' = st & clientStsPolicy . at host ?~ policy
savePolicyFile (view clientStsPolicy st')
return st'
readInt :: Text -> Maybe Int
readInt x =
case Text.decimal x of
Right (n, t) | Text.null t -> Just n
_ -> Nothing
processSaslEcdsa ::
ZonedTime ->
Text ->
NetworkState ->
ClientState ->
IO ClientState
processSaslEcdsa now challenge cs st =
case view ssSaslEcdsaFile ss of
Nothing ->
do sendMsg cs ircCapEnd
return $! recordError now (view csNetwork cs) "panic: ecdsatool malformed output" st
Just path ->
do res <- Ecdsa.computeResponse path challenge
case res of
Left e ->
do sendMsg cs ircCapEnd
return $! recordError now (view csNetwork cs) (Text.pack e) st
Right resp ->
do sendMsg cs (ircAuthenticate resp)
return $! set asLens AS_None st
where
ss = view csSettings cs
asLens = clientConnection (view csNetwork cs) . csAuthenticationState
processConnectCmd ::
ZonedTime ->
NetworkState ->
ClientState ->
[ExpansionChunk] ->
IO ClientState
processConnectCmd now cs st0 cmdTxt =
do dc <- forM disco $ \t ->
Text.pack . formatTime defaultTimeLocale "%H:%M:%S"
<$> utcToLocalZonedTime t
let failureCase e = recordError now (view csNetwork cs) ("Bad connect-cmd: " <> e)
case resolveMacroExpansions (commandExpansion dc st0) (const Nothing) cmdTxt of
Nothing -> return $! failureCase "Unable to expand connect command" st0
Just cmdTxt' ->
do res <- executeUserCommand dc (Text.unpack cmdTxt') st0
return $! case res of
CommandFailure st -> failureCase cmdTxt' st
CommandSuccess st -> st
CommandQuit st -> st
where
disco =
case view csPingStatus cs of
PingConnecting _ tm -> tm
_ -> Nothing