2020-12-24 20:43:10 +00:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
2021-01-03 10:42:41 +00:00
|
|
|
{-# LANGUAGE DeriveAnyClass #-}
|
2020-12-24 20:43:10 +00:00
|
|
|
{-# LANGUAGE GADTs #-}
|
2020-12-28 20:42:38 +00:00
|
|
|
{-# LANGUAGE LambdaCase #-}
|
2021-01-09 19:23:32 +00:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
2020-12-29 13:10:55 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2020-12-25 16:46:02 +00:00
|
|
|
{-# LANGUAGE RankNTypes #-}
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
2020-12-24 20:43:10 +00:00
|
|
|
{-# LANGUAGE StandaloneDeriving #-}
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}
|
|
|
|
|
2020-12-28 17:08:48 +00:00
|
|
|
module Simplex.Messaging.Agent.Transmission where
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2021-01-18 20:53:02 +00:00
|
|
|
import Control.Applicative ((<|>))
|
2020-12-25 16:46:02 +00:00
|
|
|
import Control.Monad.IO.Class
|
2021-01-18 20:53:02 +00:00
|
|
|
import Data.Attoparsec.ByteString.Char8 (Parser)
|
|
|
|
import qualified Data.Attoparsec.ByteString.Char8 as A
|
|
|
|
import Data.Bifunctor (first)
|
2020-12-29 13:10:55 +00:00
|
|
|
import Data.ByteString.Base64
|
2020-12-24 20:43:10 +00:00
|
|
|
import Data.ByteString.Char8 (ByteString)
|
2020-12-25 21:36:51 +00:00
|
|
|
import qualified Data.ByteString.Char8 as B
|
2021-01-18 20:53:02 +00:00
|
|
|
import Data.Char (isAlphaNum)
|
|
|
|
import Data.Functor
|
2020-12-24 20:43:10 +00:00
|
|
|
import Data.Kind
|
2020-12-26 10:58:50 +00:00
|
|
|
import Data.Time.Clock (UTCTime)
|
2021-01-17 11:33:26 +00:00
|
|
|
import Data.Time.ISO8601
|
2020-12-29 13:10:55 +00:00
|
|
|
import Data.Type.Equality
|
|
|
|
import Data.Typeable ()
|
2020-12-24 20:43:10 +00:00
|
|
|
import Network.Socket
|
2020-12-29 20:50:33 +00:00
|
|
|
import Numeric.Natural
|
2021-01-09 19:55:42 +00:00
|
|
|
import Simplex.Messaging.Agent.Store.Types
|
2021-01-03 10:42:41 +00:00
|
|
|
import Simplex.Messaging.Server.Transmission
|
|
|
|
( CorrId (..),
|
|
|
|
Encoded,
|
|
|
|
MsgBody,
|
|
|
|
PublicKey,
|
2021-01-09 19:23:32 +00:00
|
|
|
SenderId,
|
2021-01-03 10:42:41 +00:00
|
|
|
errBadParameters,
|
|
|
|
errMessageBody,
|
|
|
|
)
|
|
|
|
import qualified Simplex.Messaging.Server.Transmission as SMP
|
2020-12-25 16:46:02 +00:00
|
|
|
import Simplex.Messaging.Transport
|
2021-01-03 18:05:50 +00:00
|
|
|
import Simplex.Messaging.Util
|
2020-12-25 16:46:02 +00:00
|
|
|
import System.IO
|
2020-12-28 20:42:38 +00:00
|
|
|
import Text.Read
|
2021-01-03 10:42:41 +00:00
|
|
|
import UnliftIO.Exception
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-28 20:42:38 +00:00
|
|
|
type ARawTransmission = (ByteString, ByteString, ByteString)
|
|
|
|
|
|
|
|
type ATransmission p = (CorrId, ConnAlias, ACommand p)
|
|
|
|
|
|
|
|
type ATransmissionOrError p = (CorrId, ConnAlias, Either ErrorType (ACommand p))
|
|
|
|
|
|
|
|
data AParty = Agent | Client
|
|
|
|
deriving (Eq, Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
|
|
|
data SAParty :: AParty -> Type where
|
|
|
|
SAgent :: SAParty Agent
|
2020-12-28 20:42:38 +00:00
|
|
|
SClient :: SAParty Client
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-28 20:42:38 +00:00
|
|
|
deriving instance Show (SAParty p)
|
|
|
|
|
|
|
|
deriving instance Eq (SAParty p)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-29 13:10:55 +00:00
|
|
|
instance TestEquality SAParty where
|
|
|
|
testEquality SAgent SAgent = Just Refl
|
|
|
|
testEquality SClient SClient = Just Refl
|
|
|
|
testEquality _ _ = Nothing
|
|
|
|
|
2020-12-25 16:46:02 +00:00
|
|
|
data ACmd where
|
2020-12-28 20:42:38 +00:00
|
|
|
ACmd :: SAParty p -> ACommand p -> ACmd
|
|
|
|
|
|
|
|
deriving instance Show ACmd
|
|
|
|
|
|
|
|
data ACommand (p :: AParty) where
|
2021-01-17 11:33:26 +00:00
|
|
|
NEW :: SMPServer -> ACommand Client -- response INV
|
2020-12-28 20:42:38 +00:00
|
|
|
INV :: SMPQueueInfo -> ACommand Agent
|
2021-01-17 11:33:26 +00:00
|
|
|
JOIN :: SMPQueueInfo -> ReplyMode -> ACommand Client -- response OK
|
|
|
|
CON :: ACommand Agent -- notification that connection is established
|
|
|
|
-- TODO currently it automatically allows whoever sends the confirmation
|
|
|
|
READY :: ACommand Agent
|
|
|
|
-- CONF :: OtherPartyId -> ACommand Agent
|
|
|
|
-- LET :: OtherPartyId -> ACommand Client
|
2020-12-28 20:42:38 +00:00
|
|
|
SUB :: SubMode -> ACommand Client
|
|
|
|
END :: ACommand Agent
|
2021-01-11 18:31:10 +00:00
|
|
|
-- QST :: QueueDirection -> ACommand Client
|
|
|
|
-- STAT :: QueueDirection -> Maybe QueueStatus -> Maybe SubMode -> ACommand Agent
|
2020-12-28 20:42:38 +00:00
|
|
|
SEND :: MsgBody -> ACommand Client
|
|
|
|
MSG :: AgentMsgId -> UTCTime -> UTCTime -> MsgStatus -> MsgBody -> ACommand Agent
|
|
|
|
ACK :: AgentMsgId -> ACommand Client
|
2021-01-11 18:31:10 +00:00
|
|
|
-- RCVD :: AgentMsgId -> ACommand Agent
|
|
|
|
-- OFF :: ACommand Client
|
|
|
|
-- DEL :: ACommand Client
|
2020-12-28 20:42:38 +00:00
|
|
|
OK :: ACommand Agent
|
2020-12-25 16:46:02 +00:00
|
|
|
ERR :: ErrorType -> ACommand Agent
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-28 20:42:38 +00:00
|
|
|
deriving instance Show (ACommand p)
|
2020-12-25 21:36:51 +00:00
|
|
|
|
2021-01-15 15:30:11 +00:00
|
|
|
type Message = ByteString
|
|
|
|
|
2021-01-17 11:33:26 +00:00
|
|
|
data SMPMessage
|
|
|
|
= SMPConfirmation PublicKey
|
|
|
|
| SMPMessage
|
|
|
|
{ agentMsgId :: Integer,
|
|
|
|
agentTimestamp :: UTCTime,
|
|
|
|
previousMsgHash :: ByteString,
|
|
|
|
agentMessage :: AMessage
|
|
|
|
}
|
2021-01-18 20:53:02 +00:00
|
|
|
deriving (Show)
|
2021-01-17 11:33:26 +00:00
|
|
|
|
2020-12-24 20:43:10 +00:00
|
|
|
data AMessage where
|
|
|
|
HELLO :: VerificationKey -> AckMode -> AMessage
|
|
|
|
REPLY :: SMPQueueInfo -> AMessage
|
2020-12-26 12:55:23 +00:00
|
|
|
A_MSG :: MsgBody -> AMessage
|
2021-01-18 20:53:02 +00:00
|
|
|
deriving (Show)
|
2021-01-11 18:31:10 +00:00
|
|
|
|
2021-01-17 11:33:26 +00:00
|
|
|
parseSMPMessage :: ByteString -> Either ErrorType SMPMessage
|
2021-01-18 20:53:02 +00:00
|
|
|
parseSMPMessage = parse (smpMessageP <* A.endOfLine) $ SYNTAX errBadMessage
|
|
|
|
where
|
|
|
|
smpMessageP :: Parser SMPMessage
|
|
|
|
smpMessageP =
|
|
|
|
smpConfirmationP <* A.endOfLine
|
|
|
|
<|> A.endOfLine *> smpClientMessageP
|
|
|
|
|
|
|
|
smpConfirmationP :: Parser SMPMessage
|
|
|
|
smpConfirmationP = SMPConfirmation <$> ("KEY " *> base64P <* A.endOfLine)
|
|
|
|
|
|
|
|
smpClientMessageP :: Parser SMPMessage
|
|
|
|
smpClientMessageP =
|
|
|
|
SMPMessage
|
|
|
|
<$> A.decimal <* A.space
|
|
|
|
<*> tsIso8601P <* A.space
|
|
|
|
<*> base64P <* A.endOfLine
|
|
|
|
<*> agentMessageP
|
|
|
|
|
|
|
|
tsIso8601P :: Parser UTCTime
|
|
|
|
tsIso8601P = maybe (fail "timestamp") pure . parseISO8601 . B.unpack =<< A.takeTill (== ' ')
|
2021-01-17 11:33:26 +00:00
|
|
|
|
|
|
|
serializeSMPMessage :: SMPMessage -> ByteString
|
|
|
|
serializeSMPMessage = \case
|
2021-01-18 20:53:02 +00:00
|
|
|
SMPConfirmation sKey -> smpMessage ("KEY " <> encode sKey) "" ""
|
2021-01-17 11:33:26 +00:00
|
|
|
SMPMessage {agentMsgId, agentTimestamp, previousMsgHash, agentMessage} ->
|
2021-01-18 20:53:02 +00:00
|
|
|
let header = messageHeader agentMsgId agentTimestamp previousMsgHash
|
|
|
|
body = serializeAgentMessage agentMessage
|
|
|
|
in smpMessage "" header body
|
|
|
|
where
|
|
|
|
messageHeader msgId ts prevMsgHash =
|
|
|
|
B.unwords [B.pack $ show msgId, B.pack $ formatISO8601Millis ts, encode prevMsgHash]
|
|
|
|
smpMessage smpHeader aHeader aBody = B.intercalate "\n" [smpHeader, aHeader, aBody, ""]
|
|
|
|
|
|
|
|
agentMessageP :: Parser AMessage
|
|
|
|
agentMessageP =
|
|
|
|
"HELLO " *> hello
|
|
|
|
<|> "REPLY " *> reply
|
|
|
|
<|> "MSG " *> a_msg
|
2021-01-17 11:33:26 +00:00
|
|
|
where
|
2021-01-18 20:53:02 +00:00
|
|
|
hello = HELLO <$> base64P <*> ackMode
|
|
|
|
reply = REPLY <$> smpQueueInfoP
|
|
|
|
a_msg = do
|
|
|
|
size :: Int <- A.decimal
|
|
|
|
A_MSG <$> (A.endOfLine *> A.take size <* A.endOfLine)
|
|
|
|
ackMode = " NO_ACK" $> AckMode Off <|> pure (AckMode On)
|
|
|
|
|
|
|
|
smpQueueInfoP :: Parser SMPQueueInfo
|
|
|
|
smpQueueInfoP =
|
|
|
|
SMPQueueInfo <$> ("smp::" *> smpServerP <* "::") <*> (base64P <* "::") <*> base64P
|
|
|
|
|
|
|
|
smpServerP :: Parser SMPServer
|
|
|
|
smpServerP = SMPServer <$> server <*> port <*> msgHash
|
|
|
|
where
|
|
|
|
server = B.unpack <$> A.takeTill (A.inClass ":# ")
|
|
|
|
port = A.char ':' *> (Just . show <$> (A.decimal :: Parser Int)) <|> pure Nothing
|
|
|
|
msgHash = A.char '#' *> (Just <$> base64P) <|> pure Nothing
|
|
|
|
|
|
|
|
base64P :: Parser ByteString
|
|
|
|
base64P = do
|
|
|
|
str <- A.takeWhile1 (\c -> isAlphaNum c || c == '+' || c == '/')
|
|
|
|
pad <- A.takeWhile (== '=')
|
|
|
|
either fail pure $ decode (str <> pad)
|
2021-01-17 11:33:26 +00:00
|
|
|
|
|
|
|
parseAgentMessage :: ByteString -> Either ErrorType AMessage
|
2021-01-18 20:53:02 +00:00
|
|
|
parseAgentMessage = parse agentMessageP $ SYNTAX errBadMessage
|
|
|
|
|
|
|
|
parse :: Parser a -> e -> (ByteString -> Either e a)
|
|
|
|
parse parser err = first (const err) . A.parseOnly (parser <* A.endOfInput)
|
2021-01-15 15:30:11 +00:00
|
|
|
|
|
|
|
errParams :: Either ErrorType a
|
|
|
|
errParams = Left $ SYNTAX errBadParameters
|
|
|
|
|
2021-01-17 11:33:26 +00:00
|
|
|
serializeAgentMessage :: AMessage -> ByteString
|
|
|
|
serializeAgentMessage = \case
|
2021-01-15 15:30:11 +00:00
|
|
|
HELLO _verKey _ackMode -> "HELLO" -- TODO
|
2021-01-17 11:33:26 +00:00
|
|
|
REPLY qInfo -> "REPLY " <> serializeSmpQueueInfo qInfo
|
2021-01-18 20:53:02 +00:00
|
|
|
A_MSG msgBody -> "A_MSG " <> msgBody
|
2021-01-15 15:30:11 +00:00
|
|
|
|
|
|
|
serializeSmpQueueInfo :: SMPQueueInfo -> ByteString
|
|
|
|
serializeSmpQueueInfo (SMPQueueInfo srv qId ek) = "smp::" <> serializeServer srv <> "::" <> encode qId <> "::" <> encode ek
|
|
|
|
|
|
|
|
serializeServer :: SMPServer -> ByteString
|
|
|
|
serializeServer SMPServer {host, port, keyHash} = B.pack $ host <> maybe "" (':' :) port <> maybe "" (('#' :) . B.unpack) keyHash
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-29 13:10:55 +00:00
|
|
|
data SMPServer = SMPServer
|
|
|
|
{ host :: HostName,
|
|
|
|
port :: Maybe ServiceName,
|
|
|
|
keyHash :: Maybe KeyHash
|
|
|
|
}
|
2021-01-13 19:32:21 +00:00
|
|
|
deriving (Eq, Ord, Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-29 13:10:55 +00:00
|
|
|
type KeyHash = Encoded
|
2020-12-24 20:43:10 +00:00
|
|
|
|
|
|
|
type ConnAlias = ByteString
|
|
|
|
|
|
|
|
type OtherPartyId = Encoded
|
|
|
|
|
2021-01-04 20:25:02 +00:00
|
|
|
data Mode = On | Off deriving (Eq, Show, Read)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2021-01-04 20:25:02 +00:00
|
|
|
newtype AckMode = AckMode Mode deriving (Eq, Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-25 21:36:51 +00:00
|
|
|
newtype SubMode = SubMode Mode deriving (Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2021-01-09 19:23:32 +00:00
|
|
|
data SMPQueueInfo = SMPQueueInfo SMPServer SenderId EncryptionKey
|
2020-12-25 21:36:51 +00:00
|
|
|
deriving (Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2021-01-18 20:53:02 +00:00
|
|
|
data ReplyMode = ReplyOff | ReplyOn | ReplyVia SMPServer deriving (Show)
|
2021-01-11 18:31:10 +00:00
|
|
|
|
2020-12-24 20:43:10 +00:00
|
|
|
type EncryptionKey = PublicKey
|
|
|
|
|
|
|
|
type VerificationKey = PublicKey
|
|
|
|
|
2020-12-25 21:36:51 +00:00
|
|
|
data QueueDirection = SND | RCV deriving (Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2020-12-26 12:55:23 +00:00
|
|
|
data QueueStatus = New | Confirmed | Secured | Active | Disabled
|
2021-01-04 20:25:02 +00:00
|
|
|
deriving (Eq, Show, Read)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
|
|
|
type AgentMsgId = Int
|
|
|
|
|
|
|
|
data MsgStatus = MsgOk | MsgError MsgErrorType
|
2020-12-25 21:36:51 +00:00
|
|
|
deriving (Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
|
|
|
data MsgErrorType = MsgSkipped AgentMsgId AgentMsgId | MsgBadId AgentMsgId | MsgBadHash
|
2020-12-25 21:36:51 +00:00
|
|
|
deriving (Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
2021-01-03 10:42:41 +00:00
|
|
|
data ErrorType
|
|
|
|
= UNKNOWN
|
|
|
|
| UNSUPPORTED -- TODO remove once all commands implemented
|
|
|
|
| PROHIBITED
|
|
|
|
| SYNTAX Int
|
|
|
|
| BROKER Natural
|
|
|
|
| SMP SMP.ErrorType
|
|
|
|
| SIZE
|
2021-01-09 19:55:42 +00:00
|
|
|
| STORE StoreError
|
2021-01-03 10:42:41 +00:00
|
|
|
| INTERNAL -- etc. TODO SYNTAX Natural
|
|
|
|
deriving (Show, Exception)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
|
|
|
data AckStatus = AckOk | AckError AckErrorType
|
2020-12-25 21:36:51 +00:00
|
|
|
deriving (Show)
|
2020-12-24 20:43:10 +00:00
|
|
|
|
|
|
|
data AckErrorType = AckUnknown | AckProhibited | AckSyntax Int -- etc.
|
2020-12-25 21:36:51 +00:00
|
|
|
deriving (Show)
|
2020-12-25 16:46:02 +00:00
|
|
|
|
2021-01-12 15:08:01 +00:00
|
|
|
errBadEncoding :: Int
|
|
|
|
errBadEncoding = 10
|
|
|
|
|
2021-01-18 20:53:02 +00:00
|
|
|
errBadCommand :: Int
|
|
|
|
errBadCommand = 11
|
|
|
|
|
2020-12-29 13:10:55 +00:00
|
|
|
errBadInvitation :: Int
|
2021-01-12 15:08:01 +00:00
|
|
|
errBadInvitation = 12
|
2020-12-29 13:10:55 +00:00
|
|
|
|
|
|
|
errNoConnAlias :: Int
|
2021-01-12 15:08:01 +00:00
|
|
|
errNoConnAlias = 13
|
2020-12-29 13:10:55 +00:00
|
|
|
|
2021-01-18 20:53:02 +00:00
|
|
|
errBadMessage :: Int
|
|
|
|
errBadMessage = 14
|
|
|
|
|
|
|
|
errBadServer :: Int
|
|
|
|
errBadServer = 15
|
|
|
|
|
2021-01-03 10:42:41 +00:00
|
|
|
smpErrTCPConnection :: Natural
|
|
|
|
smpErrTCPConnection = 1
|
|
|
|
|
2020-12-29 20:50:33 +00:00
|
|
|
smpErrCorrelationId :: Natural
|
2021-01-03 10:42:41 +00:00
|
|
|
smpErrCorrelationId = 2
|
|
|
|
|
|
|
|
smpUnexpectedResponse :: Natural
|
|
|
|
smpUnexpectedResponse = 3
|
2020-12-29 20:50:33 +00:00
|
|
|
|
2021-01-18 20:53:02 +00:00
|
|
|
parseCommandP :: Parser ACmd
|
|
|
|
parseCommandP =
|
|
|
|
"NEW " *> newCmd
|
|
|
|
<|> "INV " *> invResp
|
|
|
|
<|> "JOIN " *> joinCmd
|
|
|
|
<|> "CON" $> ACmd SAgent CON
|
|
|
|
<|> "OK" $> ACmd SAgent OK
|
2020-12-29 13:10:55 +00:00
|
|
|
where
|
2021-01-18 20:53:02 +00:00
|
|
|
newCmd = ACmd SClient . NEW <$> smpServerP
|
|
|
|
invResp = ACmd SAgent . INV <$> smpQueueInfoP
|
|
|
|
joinCmd = ACmd SClient <$> (JOIN <$> smpQueueInfoP <*> replyMode)
|
|
|
|
replyMode =
|
|
|
|
" NO_REPLY" $> ReplyOff
|
|
|
|
<|> A.space *> (ReplyVia <$> smpServerP)
|
|
|
|
<|> pure ReplyOn
|
|
|
|
|
|
|
|
parseCommand :: ByteString -> Either ErrorType ACmd
|
|
|
|
parseCommand = parse parseCommandP $ SYNTAX errBadCommand
|
2020-12-29 13:10:55 +00:00
|
|
|
|
2020-12-25 16:46:02 +00:00
|
|
|
serializeCommand :: ACommand p -> ByteString
|
2021-01-09 19:23:32 +00:00
|
|
|
serializeCommand = \case
|
2021-01-15 15:30:11 +00:00
|
|
|
NEW srv -> "NEW " <> serializeServer srv
|
|
|
|
INV qInfo -> "INV " <> serializeSmpQueueInfo qInfo
|
2021-01-18 20:53:02 +00:00
|
|
|
JOIN qInfo rMode -> "JOIN " <> serializeSmpQueueInfo qInfo <> replyMode rMode
|
2021-01-11 18:31:10 +00:00
|
|
|
CON -> "CON"
|
2021-01-12 15:08:01 +00:00
|
|
|
ERR e -> "ERR " <> B.pack (show e)
|
2021-01-18 20:53:02 +00:00
|
|
|
OK -> "OK"
|
2021-01-09 19:23:32 +00:00
|
|
|
c -> B.pack $ show c
|
2021-01-18 20:53:02 +00:00
|
|
|
where
|
|
|
|
replyMode :: ReplyMode -> ByteString
|
|
|
|
replyMode = \case
|
|
|
|
ReplyOff -> " NO_REPLY"
|
|
|
|
ReplyVia srv -> " " <> serializeServer srv
|
|
|
|
ReplyOn -> ""
|
2021-01-11 18:31:10 +00:00
|
|
|
|
2020-12-28 20:42:38 +00:00
|
|
|
tPutRaw :: MonadIO m => Handle -> ARawTransmission -> m ()
|
|
|
|
tPutRaw h (corrId, connAlias, command) = do
|
|
|
|
putLn h corrId
|
|
|
|
putLn h connAlias
|
|
|
|
putLn h command
|
|
|
|
|
|
|
|
tGetRaw :: MonadIO m => Handle -> m ARawTransmission
|
|
|
|
tGetRaw h = do
|
|
|
|
corrId <- getLn h
|
|
|
|
connAlias <- getLn h
|
|
|
|
command <- getLn h
|
|
|
|
return (corrId, connAlias, command)
|
|
|
|
|
|
|
|
tPut :: MonadIO m => Handle -> ATransmission p -> m ()
|
|
|
|
tPut h (corrId, connAlias, command) = tPutRaw h (bs corrId, connAlias, serializeCommand command)
|
|
|
|
|
|
|
|
-- | get client and agent transmissions
|
2020-12-29 13:10:55 +00:00
|
|
|
tGet :: forall m p. MonadIO m => SAParty p -> Handle -> m (ATransmissionOrError p)
|
|
|
|
tGet party h = tGetRaw h >>= tParseLoadBody
|
|
|
|
where
|
|
|
|
tParseLoadBody :: ARawTransmission -> m (ATransmissionOrError p)
|
|
|
|
tParseLoadBody t@(corrId, connAlias, command) = do
|
|
|
|
let cmd = parseCommand command >>= fromParty >>= tConnAlias t
|
|
|
|
fullCmd <- either (return . Left) cmdWithMsgBody cmd
|
|
|
|
return (CorrId corrId, connAlias, fullCmd)
|
|
|
|
|
|
|
|
fromParty :: ACmd -> Either ErrorType (ACommand p)
|
|
|
|
fromParty (ACmd (p :: p1) cmd) = case testEquality party p of
|
|
|
|
Just Refl -> Right cmd
|
|
|
|
_ -> Left PROHIBITED
|
|
|
|
|
|
|
|
tConnAlias :: ARawTransmission -> ACommand p -> Either ErrorType (ACommand p)
|
|
|
|
tConnAlias (_, connAlias, _) cmd = case cmd of
|
2021-01-12 15:08:01 +00:00
|
|
|
-- NEW and JOIN have optional connAlias
|
2021-01-11 18:31:10 +00:00
|
|
|
NEW _ -> Right cmd
|
2021-01-12 15:08:01 +00:00
|
|
|
JOIN _ _ -> Right cmd
|
2020-12-29 13:10:55 +00:00
|
|
|
-- ERROR response does not always have connAlias
|
|
|
|
ERR _ -> Right cmd
|
|
|
|
-- other responses must have connAlias
|
|
|
|
_
|
|
|
|
| B.null connAlias -> Left $ SYNTAX errNoConnAlias
|
|
|
|
| otherwise -> Right cmd
|
|
|
|
|
|
|
|
cmdWithMsgBody :: ACommand p -> m (Either ErrorType (ACommand p))
|
|
|
|
cmdWithMsgBody = \case
|
|
|
|
SEND body -> SEND <$$> getMsgBody body
|
|
|
|
MSG agentMsgId srvTS agentTS status body -> MSG agentMsgId srvTS agentTS status <$$> getMsgBody body
|
|
|
|
cmd -> return $ Right cmd
|
|
|
|
|
|
|
|
getMsgBody :: MsgBody -> m (Either ErrorType MsgBody)
|
|
|
|
getMsgBody msgBody =
|
|
|
|
case B.unpack msgBody of
|
|
|
|
':' : body -> return . Right $ B.pack body
|
|
|
|
str -> case readMaybe str :: Maybe Int of
|
|
|
|
Just size -> do
|
|
|
|
body <- getBytes h size
|
|
|
|
s <- getLn h
|
|
|
|
return $ if B.null s then Right body else Left SIZE
|
|
|
|
Nothing -> return . Left $ SYNTAX errMessageBody
|