From eb2404c9ce24962076271e588fe16aec7af28dad Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 25 Jun 2021 18:18:24 +0100 Subject: [PATCH 01/29] simplex-chat schema, refactor chat to use SMP agent functions (#62) * chat messages namespace and types * initial schema (WIP) * schema for messages (WIP) * fix schema, add migrations, remove broadcast * simplex-chat spike (WIP) * chat client design * update chat schema * more chat schema updates * simplex-chat app structure * chat app layout demo * update schema * refactor dog-food (WIP) * refactor / simplify * refactor output of sent message to avoid separate parsing * refactor inputSubscriber * remove unused simplex-chat code * update simplexmq commit * update schema * remove ncurses --- .gitignore | 1 + apps/dog-food/ChatOptions.hs | 19 +- apps/dog-food/ChatTerminal.hs | 108 ------ apps/dog-food/ChatTerminal/Basic.hs | 89 ----- apps/dog-food/ChatTerminal/Editor.hs | 61 ---- apps/dog-food/Main.hs | 343 +----------------- apps/dog-food/Simplex/Chat.hs | 111 ++++++ apps/dog-food/Simplex/Chat/Controller.hs | 43 +++ apps/dog-food/Simplex/Help.hs | 55 +++ .../Core.hs => Simplex/Input.hs} | 108 ++---- apps/dog-food/Simplex/Terminal.hs | 154 ++++++++ apps/dog-food/Simplex/View.hs | 151 ++++++++ apps/dog-food/Types.hs | 11 +- apps/simplex-chat/Main.hs | 45 +++ apps/simplex-chat/Simplex/Demo.hs | 119 ++++++ apps/simplex-chat/Simplex/Store.hs | 41 +++ migrations/20210612_initial.sql | 160 ++++++++ package.yaml | 25 ++ protocol/types.ts | 74 ++++ src/Simplex/Chat/Protocol._hs | 13 - src/Simplex/Chat/Protocol.hs | 44 +++ {apps/dog-food => src/Simplex/Chat}/Styled.hs | 2 +- stack.yaml | 2 +- 23 files changed, 1078 insertions(+), 701 deletions(-) delete mode 100644 apps/dog-food/ChatTerminal.hs delete mode 100644 apps/dog-food/ChatTerminal/Basic.hs delete mode 100644 apps/dog-food/ChatTerminal/Editor.hs create mode 100644 apps/dog-food/Simplex/Chat.hs create mode 100644 apps/dog-food/Simplex/Chat/Controller.hs create mode 100644 apps/dog-food/Simplex/Help.hs rename apps/dog-food/{ChatTerminal/Core.hs => Simplex/Input.hs} (57%) create mode 100644 apps/dog-food/Simplex/Terminal.hs create mode 100644 apps/dog-food/Simplex/View.hs create mode 100644 apps/simplex-chat/Main.hs create mode 100644 apps/simplex-chat/Simplex/Demo.hs create mode 100644 apps/simplex-chat/Simplex/Store.hs create mode 100644 migrations/20210612_initial.sql create mode 100644 protocol/types.ts delete mode 100644 src/Simplex/Chat/Protocol._hs create mode 100644 src/Simplex/Chat/Protocol.hs rename {apps/dog-food => src/Simplex/Chat}/Styled.hs (98%) diff --git a/.gitignore b/.gitignore index 73b4750e47..2d0711a4ad 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ stack.yaml.lock # chat database *.db +*.db.bak diff --git a/apps/dog-food/ChatOptions.hs b/apps/dog-food/ChatOptions.hs index 935dd55709..11ececded6 100644 --- a/apps/dog-food/ChatOptions.hs +++ b/apps/dog-food/ChatOptions.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module ChatOptions (getChatOpts, ChatOpts (..)) where @@ -11,12 +10,10 @@ import Options.Applicative import Simplex.Messaging.Agent.Protocol (SMPServer (..), smpServerP) import Simplex.Messaging.Parsers (parseAll) import System.FilePath (combine) -import Types data ChatOpts = ChatOpts { dbFile :: String, - smpServers :: NonEmpty SMPServer, - termMode :: TermMode + smpServers :: NonEmpty SMPServer } chatOpts :: FilePath -> Parser ChatOpts @@ -37,14 +34,6 @@ chatOpts appDir = <> help "SMP server(s) to use (smp1.simplex.im#pLdiGvm0jD1CMblnov6Edd/391OrYsShw+RgdfR0ChA=)" <> value (L.fromList ["smp1.simplex.im#pLdiGvm0jD1CMblnov6Edd/391OrYsShw+RgdfR0ChA="]) ) - <*> option - parseTermMode - ( long "term" - <> short 't' - <> metavar "TERM" - <> help ("terminal mode: editor or basic (" <> termModeName TermModeEditor <> ")") - <> value TermModeEditor - ) where defaultDbFilePath = combine appDir "smp-chat.db" @@ -53,12 +42,6 @@ parseSMPServer = eitherReader $ parseAll servers . B.pack where servers = L.fromList <$> smpServerP `A.sepBy1` A.char ',' -parseTermMode :: ReadM TermMode -parseTermMode = maybeReader $ \case - "basic" -> Just TermModeBasic - "editor" -> Just TermModeEditor - _ -> Nothing - getChatOpts :: FilePath -> IO ChatOpts getChatOpts appDir = execParser opts where diff --git a/apps/dog-food/ChatTerminal.hs b/apps/dog-food/ChatTerminal.hs deleted file mode 100644 index 27683c77bc..0000000000 --- a/apps/dog-food/ChatTerminal.hs +++ /dev/null @@ -1,108 +0,0 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} - -module ChatTerminal - ( ChatTerminal (..), - newChatTerminal, - chatTerminal, - ttyContact, - ttyFromContact, - ) -where - -import ChatTerminal.Basic -import ChatTerminal.Core -import ChatTerminal.Editor -import Control.Concurrent (threadDelay) -import Control.Concurrent.Async (race_) -import Control.Monad -import Control.Monad.IO.Class (liftIO) -import Data.Time.Format (defaultTimeLocale, formatTime) -import Data.Time.LocalTime (getZonedTime) -import Numeric.Natural -import Styled -import System.Terminal -import Types -import UnliftIO.STM - -newChatTerminal :: Natural -> TermMode -> IO ChatTerminal -newChatTerminal qSize termMode = do - inputQ <- newTBQueueIO qSize - outputQ <- newTBQueueIO qSize - activeTo <- newTVarIO ActiveNone - termSize <- withTerminal . runTerminalT $ getWindowSize - let lastRow = height termSize - 1 - termState <- newTVarIO newTermState - termLock <- newTMVarIO () - nextMessageRow <- newTVarIO lastRow - threadDelay 500000 -- this delay is the same as timeout in getTerminalSize - return ChatTerminal {inputQ, outputQ, activeTo, termMode, termState, termSize, nextMessageRow, termLock} - -newTermState :: TerminalState -newTermState = - TerminalState - { inputString = "", - inputPosition = 0, - inputPrompt = "> ", - previousInput = "" - } - -chatTerminal :: ChatTerminal -> IO () -chatTerminal ct - | termSize ct == Size 0 0 || termMode ct == TermModeBasic = - run basicReceiveFromTTY basicSendToTTY - | otherwise = do - withTerminal . runTerminalT $ updateInput ct - run receiveFromTTY sendToTTY - where - run receive send = race_ (receive ct) (send ct) - -basicReceiveFromTTY :: ChatTerminal -> IO () -basicReceiveFromTTY ct = - forever $ getLn >>= atomically . writeTBQueue (inputQ ct) - -basicSendToTTY :: ChatTerminal -> IO () -basicSendToTTY ct = forever $ readOutputQ ct >>= mapM_ putStyledLn - -withTermLock :: MonadTerminal m => ChatTerminal -> m () -> m () -withTermLock ChatTerminal {termLock} action = do - _ <- atomically $ takeTMVar termLock - action - atomically $ putTMVar termLock () - -receiveFromTTY :: ChatTerminal -> IO () -receiveFromTTY ct@ChatTerminal {inputQ, activeTo, termSize, termState} = - withTerminal . runTerminalT . forever $ - getKey >>= processKey >> withTermLock ct (updateInput ct) - where - processKey :: MonadTerminal m => (Key, Modifiers) -> m () - processKey = \case - (EnterKey, _) -> submitInput - key -> atomically $ do - ac <- readTVar activeTo - modifyTVar termState $ updateTermState ac (width termSize) key - - submitInput :: MonadTerminal m => m () - submitInput = do - msg <- atomically $ do - ts <- readTVar termState - let s = inputString ts - writeTVar termState $ ts {inputString = "", inputPosition = 0, previousInput = s} - writeTBQueue inputQ s - return s - withTermLock ct $ do - localTime <- liftIO getZonedTime - let localTimeStr = formatTime defaultTimeLocale "%H:%M" localTime - printMessage ct [styleMessage localTimeStr msg] - -sendToTTY :: ChatTerminal -> IO () -sendToTTY ct = forever $ do - -- `readOutputQ` should be outside of `withTerminal` (see #94) - msg <- readOutputQ ct - withTerminal . runTerminalT . withTermLock ct $ do - printMessage ct msg - updateInput ct - -readOutputQ :: ChatTerminal -> IO [StyledString] -readOutputQ = atomically . readTBQueue . outputQ diff --git a/apps/dog-food/ChatTerminal/Basic.hs b/apps/dog-food/ChatTerminal/Basic.hs deleted file mode 100644 index 875313c645..0000000000 --- a/apps/dog-food/ChatTerminal/Basic.hs +++ /dev/null @@ -1,89 +0,0 @@ -{-# LANGUAGE LambdaCase #-} - -module ChatTerminal.Basic where - -import Control.Monad.IO.Class (liftIO) -import Styled -import System.Console.ANSI.Types -import System.Exit (exitSuccess) -import System.Terminal as C - -getLn :: IO String -getLn = withTerminal $ runTerminalT getTermLine - -putStyledLn :: StyledString -> IO () -putStyledLn s = - withTerminal . runTerminalT $ - putStyled s >> C.putLn >> flush - --- Currently it is assumed that the message does not have internal line breaks. --- Previous implementation "kind of" supported them, --- but it was not determining the number of printed lines correctly --- because of accounting for control sequences in length -putStyled :: MonadTerminal m => StyledString -> m () -putStyled (s1 :<>: s2) = putStyled s1 >> putStyled s2 -putStyled (Styled [] s) = putString s -putStyled (Styled sgr s) = setSGR sgr >> putString s >> resetAttributes - -setSGR :: MonadTerminal m => [SGR] -> m () -setSGR = mapM_ $ \case - Reset -> resetAttributes - SetConsoleIntensity BoldIntensity -> setAttribute bold - SetConsoleIntensity _ -> resetAttribute bold - SetItalicized True -> setAttribute italic - SetItalicized _ -> resetAttribute italic - SetUnderlining NoUnderline -> resetAttribute underlined - SetUnderlining _ -> setAttribute underlined - SetSwapForegroundBackground True -> setAttribute inverted - SetSwapForegroundBackground _ -> resetAttribute inverted - SetColor l i c -> setAttribute . layer l . intensity i $ color c - SetBlinkSpeed _ -> pure () - SetVisible _ -> pure () - SetRGBColor _ _ -> pure () - SetPaletteColor _ _ -> pure () - SetDefaultColor _ -> pure () - where - layer = \case - Foreground -> foreground - Background -> background - intensity = \case - Dull -> id - Vivid -> bright - color = \case - Black -> black - Red -> red - Green -> green - Yellow -> yellow - Blue -> blue - Magenta -> magenta - Cyan -> cyan - White -> white - -getKey :: MonadTerminal m => m (Key, Modifiers) -getKey = - flush >> awaitEvent >>= \case - Left Interrupt -> liftIO exitSuccess - Right (KeyEvent key ms) -> pure (key, ms) - _ -> getKey - -getTermLine :: MonadTerminal m => m String -getTermLine = getChars "" - where - getChars s = - getKey >>= \(key, ms) -> case key of - CharKey c - | ms == mempty || ms == shiftKey -> do - C.putChar c - flush - getChars (c : s) - | otherwise -> getChars s - EnterKey -> do - C.putLn - flush - pure $ reverse s - BackspaceKey -> do - moveCursorBackward 1 - eraseChars 1 - flush - getChars $ if null s then s else tail s - _ -> getChars s diff --git a/apps/dog-food/ChatTerminal/Editor.hs b/apps/dog-food/ChatTerminal/Editor.hs deleted file mode 100644 index d4e6a9843a..0000000000 --- a/apps/dog-food/ChatTerminal/Editor.hs +++ /dev/null @@ -1,61 +0,0 @@ -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module ChatTerminal.Editor where - -import ChatTerminal.Basic -import ChatTerminal.Core -import Styled -import System.Terminal -import UnliftIO.STM - --- debug :: MonadTerminal m => String -> m () --- debug s = do --- saveCursor --- setCursorPosition $ Position 0 0 --- putString s --- restoreCursor - -updateInput :: forall m. MonadTerminal m => ChatTerminal -> m () -updateInput ct@ChatTerminal {termSize = Size {height, width}, termState, nextMessageRow} = do - hideCursor - ts <- readTVarIO termState - nmr <- readTVarIO nextMessageRow - let ih = inputHeight ts ct - iStart = height - ih - prompt = inputPrompt ts - Position {row, col} = positionRowColumn width $ length prompt + inputPosition ts - if nmr >= iStart - then atomically $ writeTVar nextMessageRow iStart - else clearLines nmr iStart - setCursorPosition $ Position {row = max nmr iStart, col = 0} - putString $ prompt <> inputString ts <> " " - eraseInLine EraseForward - setCursorPosition $ Position {row = iStart + row, col} - showCursor - flush - where - clearLines :: Int -> Int -> m () - clearLines from till - | from >= till = return () - | otherwise = do - setCursorPosition $ Position {row = from, col = 0} - eraseInLine EraseForward - clearLines (from + 1) till - -printMessage :: forall m. MonadTerminal m => ChatTerminal -> [StyledString] -> m () -printMessage ChatTerminal {termSize = Size {height, width}, nextMessageRow} msg = do - nmr <- readTVarIO nextMessageRow - setCursorPosition $ Position {row = nmr, col = 0} - mapM_ printStyled msg - flush - let lc = sum $ map lineCount msg - atomically . writeTVar nextMessageRow $ min (height - 1) (nmr + lc) - where - lineCount :: StyledString -> Int - lineCount s = sLength s `div` width + 1 - printStyled :: StyledString -> m () - printStyled s = do - putStyled s - eraseInLine EraseForward - putLn diff --git a/apps/dog-food/Main.hs b/apps/dog-food/Main.hs index a3e097e04e..8899d41690 100644 --- a/apps/dog-food/Main.hs +++ b/apps/dog-food/Main.hs @@ -2,7 +2,6 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -10,36 +9,19 @@ module Main where import ChatOptions -import ChatTerminal -import ChatTerminal.Core -import Control.Applicative ((<|>)) import Control.Concurrent.STM import Control.Logger.Simple +import Control.Monad.IO.Unlift import Control.Monad.Reader -import Data.Attoparsec.ByteString.Char8 (Parser) -import qualified Data.Attoparsec.ByteString.Char8 as A -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import Data.Functor (($>)) -import Data.List (intersperse) -import qualified Data.Text as T -import Data.Text.Encoding -import Data.Time.Clock (DiffTime, UTCTime) -import Data.Time.Format (defaultTimeLocale, formatTime) -import Data.Time.LocalTime -import Numeric.Natural -import Simplex.Chat.Markdown -import Simplex.Messaging.Agent (getSMPAgentClient, runSMPAgentClient) -import Simplex.Messaging.Agent.Client (AgentClient (..)) +import Simplex.Chat +import Simplex.Chat.Controller +import Simplex.Input +import Simplex.Messaging.Agent (getSMPAgentClient) import Simplex.Messaging.Agent.Env.SQLite -import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Client (smpDefaultConfig) -import Simplex.Messaging.Parsers (parseAll) -import Simplex.Messaging.Util (raceAny_) -import Styled -import System.Console.ANSI.Types +import Simplex.Terminal import System.Directory (getAppUserDataDirectory) -import Types +import UnliftIO.Async (race_) cfg :: AgentConfig cfg = @@ -56,183 +38,15 @@ cfg = logCfg :: LogConfig logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} -data ChatClient = ChatClient - { inQ :: TBQueue ChatCommand, - outQ :: TBQueue ChatResponse - } - --- | GroupMessage ChatGroup ByteString --- | AddToGroup Contact -data ChatCommand - = ChatHelp - | MarkdownHelp - | AddConnection Contact - | Connect Contact SMPQueueInfo - | DeleteConnection Contact - | SendMessage Contact ByteString - | NewGroup Group - | AddToGroup Group Contact - | RemoveFromGroup Group Contact - | DeleteGroup Group - | ListGroup Group - | SendGroupMessage Group ByteString - deriving (Show) - -chatCommandP :: Parser ChatCommand -chatCommandP = - ("/help" <|> "/h") $> ChatHelp - <|> ("/group #" <|> "/g #") *> (NewGroup <$> group) - <|> ("/add #" <|> "/a #") *> (AddToGroup <$> group <* A.space <*> contact) - <|> ("/remove #" <|> "/rm #") *> (RemoveFromGroup <$> group <* A.space <*> contact) - <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> group) - <|> ("/members #" <|> "/ms #") *> (ListGroup <$> group) - <|> A.char '#' *> (SendGroupMessage <$> group <* A.space <*> A.takeByteString) - <|> ("/add " <|> "/a ") *> (AddConnection <$> contact) - <|> ("/connect " <|> "/c ") *> (Connect <$> contact <* A.space <*> smpQueueInfoP) - <|> ("/delete " <|> "/d ") *> (DeleteConnection <$> contact) - <|> A.char '@' *> (SendMessage <$> contact <* A.space <*> A.takeByteString) - <|> ("/markdown" <|> "/m") $> MarkdownHelp - where - contact = Contact <$> A.takeTill (== ' ') - group = Group <$> A.takeTill (== ' ') - -data ChatResponse - = ChatHelpInfo - | MarkdownInfo - | Invitation SMPQueueInfo - | Connected Contact - | Confirmation Contact - | ReceivedMessage Contact UTCTime ByteString MsgIntegrity - | Disconnected Contact - | GroupMembers Group [Contact] - | ReceivedGroupMessage Group Contact UTCTime ByteString MsgIntegrity - | GroupConfirmation Group - | YesYes - | ContactError ConnectionErrorType Contact - | GroupError AgentErrorType Group - | ErrorInput ByteString - | ChatError AgentErrorType - | NoChatResponse - -serializeChatResponse :: ChatOpts -> TimeZone -> ZonedTime -> ChatResponse -> [StyledString] -serializeChatResponse _ localTz currentTime = \case - ChatHelpInfo -> chatHelpInfo - MarkdownInfo -> markdownInfo - Invitation qInfo -> - [ "pass this invitation to your contact (via any channel): ", - "", - (bPlain . serializeSmpQueueInfo) qInfo, - "", - "and ask them to connect: /c " - ] - Connected c -> [ttyContact c <> " connected"] - Confirmation c -> [ttyContact c <> " ok"] - ReceivedMessage c utcTime t mi -> receivedMessage utcTime t mi $ ttyFromContact c - ReceivedGroupMessage g c utcTime t mi -> receivedMessage utcTime t mi $ ttyFromGroup g c - Disconnected c -> ["disconnected from " <> ttyContact c <> " - restart chat"] - GroupMembers g cs -> [ttyGroup g <> ": " <> plain (B.unpack . B.intercalate ", " $ map toBs cs)] - GroupConfirmation g -> [ttyGroup g <> " ok"] - YesYes -> ["you got it!"] - ContactError e c -> case e of - NOT_FOUND -> ["no contact " <> ttyContact c] - DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] - SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] - GroupError e g -> case e of - BCAST B_NOT_FOUND -> ["no group " <> ttyGroup g] - BCAST B_DUPLICATE -> ["group " <> ttyGroup g <> " already exists"] - CONN NOT_FOUND -> ["cannot add unknown contact to the group " <> ttyGroup g] - CONN DUPLICATE -> ["this contact is already in the group " <> ttyGroup g] - CONN SIMPLEX -> ["this contact did not not accept invitation yet"] - _ -> ["chat error: " <> plain (show e)] - ErrorInput t -> ["invalid input: " <> bPlain t] - ChatError e -> ["chat error: " <> plain (show e)] - NoChatResponse -> [""] - where - receivedMessage :: UTCTime -> ByteString -> MsgIntegrity -> StyledString -> [StyledString] - receivedMessage utcTime t mi from = - prependFirst (formatUTCTime utcTime <> " " <> from) (msgPlain t) ++ showIntegrity mi - prependFirst :: StyledString -> [StyledString] -> [StyledString] - prependFirst s [] = [s] - prependFirst s (s' : ss) = (s <> s') : ss - formatUTCTime :: UTCTime -> StyledString - formatUTCTime utcTime = do - let localTime = utcToLocalTime localTz utcTime - format = - if (localDay localTime < localDay (zonedTimeToLocalTime currentTime)) - && (timeOfDayToTime (localTimeOfDay localTime) > (6 * 60 * 60 :: DiffTime)) - then "%m-%d" -- if message is from yesterday or before and 6 hours has passed since midnight - else "%H:%M" - styleTime $ formatTime defaultTimeLocale format localTime - msgPlain :: ByteString -> [StyledString] - msgPlain = map styleMarkdownText . T.lines . safeDecodeUtf8 - showIntegrity :: MsgIntegrity -> [StyledString] - showIntegrity MsgOk = [] - showIntegrity (MsgError err) = msgError $ case err of - MsgSkipped fromId toId -> - "skipped message ID " <> show fromId - <> if fromId == toId then "" else ".." <> show toId - MsgBadId msgId -> "unexpected message ID " <> show msgId - MsgBadHash -> "incorrect message hash" - MsgDuplicate -> "duplicate message ID" - msgError :: String -> [StyledString] - msgError s = [styled (Colored Red) s] - -chatHelpInfo :: [StyledString] -chatHelpInfo = - map - styleMarkdown - [ Markdown (Colored Cyan) "Using Simplex chat prototype.", - "Follow these steps to set up a connection:", - "", - Markdown (Colored Green) "Step 1: " <> Markdown (Colored Cyan) "/add bob" <> " -- Alice adds her contact, Bob (she can use any name).", - indent <> "Alice should send the invitation printed by the /add command", - indent <> "to her contact, Bob, out-of-band, via any trusted channel.", - "", - Markdown (Colored Green) "Step 2: " <> Markdown (Colored Cyan) "/connect alice " <> " -- Bob accepts the invitation.", - indent <> "Bob also can use any name for his contact, Alice,", - indent <> "followed by the invitation he received out-of-band.", - "", - Markdown (Colored Green) "Step 3: " <> "Bob and Alice are notified that the connection is set up,", - indent <> "both can now send messages:", - indent <> Markdown (Colored Cyan) "@bob Hello, Bob!" <> " -- Alice messages Bob.", - indent <> Markdown (Colored Cyan) "@alice Hey, Alice!" <> " -- Bob replies to Alice.", - "", - Markdown (Colored Green) "Other commands:", - indent <> Markdown (Colored Cyan) "/delete" <> " -- deletes contact and all messages with them.", - indent <> Markdown (Colored Cyan) "/markdown" <> " -- prints the supported markdown syntax.", - "", - "The commands may be abbreviated to a single letter: " <> listCommands ["/a", "/c", "/d", "/m"] - ] - where - listCommands = mconcat . intersperse ", " . map highlight - highlight = Markdown (Colored Cyan) - indent = " " - -markdownInfo :: [StyledString] -markdownInfo = - map - styleMarkdown - [ "Markdown:", - " *bold* - " <> Markdown Bold "bold text", - " _italic_ - " <> Markdown Italic "italic text" <> " (shown as underlined)", - " +underlined+ - " <> Markdown Underline "underlined text", - " ~strikethrough~ - " <> Markdown StrikeThrough "strikethrough text" <> " (shown as inverse)", - " `code snippet` - " <> Markdown Snippet "a + b // no *markdown* here", - " !1 text! - " <> red "red text" <> " (1-6: red, green, blue, yellow, cyan, magenta)", - " #secret# - " <> Markdown Secret "secret text" <> " (can be copy-pasted)" - ] - where - red = Markdown (Colored Red) - main :: IO () main = do - opts@ChatOpts {dbFile, smpServers, termMode} <- welcomeGetOpts - t <- atomically $ newChatClient (tbqSize cfg) - ct <- newChatTerminal (tbqSize cfg) termMode + ChatOpts {dbFile, smpServers} <- welcomeGetOpts + ct <- newChatTerminal + a <- getSMPAgentClient cfg {dbFile, smpServers} + cc <- atomically $ newChatController a ct $ tbqSize cfg -- setLogLevel LogInfo -- LogError -- withGlobalLogging logCfg $ do - env <- newSMPAgentEnv cfg {dbFile, smpServers} - dogFoodChat t ct env opts + runReaderT simplexChat cc welcomeGetOpts :: IO ChatOpts welcomeGetOpts = do @@ -243,134 +57,5 @@ welcomeGetOpts = do putStrLn "type \"/help\" or \"/h\" for usage info" pure opts -dogFoodChat :: ChatClient -> ChatTerminal -> Env -> ChatOpts -> IO () -dogFoodChat t ct env opts = do - c <- runReaderT getSMPAgentClient env - localTz <- liftIO getCurrentTimeZone - raceAny_ - [ runReaderT (runSMPAgentClient c) env, - sendToAgent t ct c, - sendToChatTerm t ct opts localTz, - receiveFromAgent t ct c, - receiveFromChatTerm t ct, - chatTerminal ct - ] - -newChatClient :: Natural -> STM ChatClient -newChatClient qSize = do - inQ <- newTBQueue qSize - outQ <- newTBQueue qSize - return ChatClient {inQ, outQ} - -receiveFromChatTerm :: ChatClient -> ChatTerminal -> IO () -receiveFromChatTerm t ct = forever $ do - atomically (readTBQueue $ inputQ ct) - >>= processOrError . parseAll chatCommandP . encodeUtf8 . T.pack - where - processOrError = \case - Left err -> writeOutQ . ErrorInput $ B.pack err - Right ChatHelp -> writeOutQ ChatHelpInfo - Right MarkdownHelp -> writeOutQ MarkdownInfo - Right cmd -> atomically $ writeTBQueue (inQ t) cmd - writeOutQ = atomically . writeTBQueue (outQ t) - -sendToChatTerm :: ChatClient -> ChatTerminal -> ChatOpts -> TimeZone -> IO () -sendToChatTerm ChatClient {outQ} ChatTerminal {outputQ} opts localTz = forever $ do - atomically (readTBQueue outQ) >>= \case - NoChatResponse -> return () - resp -> do - currentTime <- liftIO getZonedTime - atomically . writeTBQueue outputQ $ serializeChatResponse opts localTz currentTime resp - -sendToAgent :: ChatClient -> ChatTerminal -> AgentClient -> IO () -sendToAgent ChatClient {inQ} ct AgentClient {rcvQ} = do - atomically $ writeTBQueue rcvQ $ ATransmission "1" (Conn "") SUBALL -- hack for subscribing to all - forever . atomically $ do - cmd <- readTBQueue inQ - writeTBQueue rcvQ `mapM_` agentTransmission cmd - setActiveTo cmd - where - setActiveTo :: ChatCommand -> STM () - setActiveTo = \case - SendMessage a _ -> setActive ct $ ActiveC a - SendGroupMessage g _ -> setActive ct $ ActiveG g - DeleteConnection a -> unsetActive ct $ ActiveC a - DeleteGroup g -> unsetActive ct $ ActiveG g - _ -> pure () - agentTransmission :: ChatCommand -> Maybe (ATransmission 'Client) - agentTransmission = \case - AddConnection a -> transmission a NEW - Connect a qInfo -> transmission a $ JOIN qInfo $ ReplyMode On - DeleteConnection a -> transmission a DEL - SendMessage a msg -> transmission a $ SEND msg - NewGroup g -> bTransmission g NEW - AddToGroup g a -> bTransmission g $ ADD (Conn $ toBs a) - RemoveFromGroup g a -> bTransmission g $ REM (Conn $ toBs a) - DeleteGroup g -> bTransmission g DEL - ListGroup g -> bTransmission g LS - SendGroupMessage g msg -> bTransmission g $ SEND $ serializeGroupMessage g msg - ChatHelp -> Nothing - MarkdownHelp -> Nothing - transmission :: EntityCommand 'Conn_ c => Contact -> ACommand 'Client c -> Maybe (ATransmission 'Client) - transmission (Contact a) cmd = Just $ ATransmission "1" (Conn a) cmd - bTransmission :: EntityCommand 'Broadcast_ c => Group -> ACommand 'Client c -> Maybe (ATransmission 'Client) - bTransmission (Group g) cmd = Just $ ATransmission "1" (Broadcast g) cmd - -receiveFromAgent :: ChatClient -> ChatTerminal -> AgentClient -> IO () -receiveFromAgent t ct c = forever . atomically $ do - resp <- chatResponse <$> readTBQueue (sndQ c) - writeTBQueue (outQ t) resp - setActiveTo resp - where - chatResponse :: ATransmission 'Agent -> ChatResponse - chatResponse (ATransmission _ entity resp) = case entity of - Conn a -> connectionResponse a resp - Broadcast g -> broadcastResponse g resp - _ -> NoChatResponse - connectionResponse :: EntityCommand 'Conn_ c => ByteString -> ACommand 'Agent c -> ChatResponse - connectionResponse a = \case - INV qInfo -> Invitation qInfo - CON -> Connected contact - END -> Disconnected contact - MSG {msgBody, msgIntegrity, brokerMeta} -> case parseAll groupMessageP msgBody of - Right (group, msg) -> ReceivedGroupMessage group contact (snd brokerMeta) msg msgIntegrity - _ -> ReceivedMessage contact (snd brokerMeta) msgBody msgIntegrity - SENT _ -> NoChatResponse - OK -> Confirmation contact - ERR (CONN e) -> ContactError e contact - ERR e -> ChatError e - where - contact = Contact a - broadcastResponse :: EntityCommand 'Broadcast_ c => ByteString -> ACommand 'Agent c -> ChatResponse - broadcastResponse g = \case - MS as -> GroupMembers group $ map (Contact . fromConn) as - SENT _ -> NoChatResponse - OK -> GroupConfirmation group - ERR e@(CONN _) -> GroupError e group - ERR e@(BCAST _) -> GroupError e group - ERR e -> ChatError e - where - group = Group g - setActiveTo :: ChatResponse -> STM () - setActiveTo = \case - Connected a -> setActive ct $ ActiveC a - ReceivedMessage a _ _ _ -> setActive ct $ ActiveC a - ReceivedGroupMessage g _ _ _ _ -> setActive ct $ ActiveG g - Disconnected a -> unsetActive ct $ ActiveC a - _ -> pure () - -groupMessageP :: Parser (Group, ByteString) -groupMessageP = - let group = Group <$> A.takeTill (== ' ') - in "####" *> ((,) <$> group <* A.space <*> A.takeByteString) - -serializeGroupMessage :: Group -> ByteString -> ByteString -serializeGroupMessage (Group g) msg = "####" <> g <> " " <> msg - -setActive :: ChatTerminal -> ActiveTo -> STM () -setActive ct = writeTVar (activeTo ct) - -unsetActive :: ChatTerminal -> ActiveTo -> STM () -unsetActive ct a = modifyTVar (activeTo ct) unset - where - unset a' = if a == a' then ActiveNone else a' +simplexChat :: (MonadUnliftIO m, MonadReader ChatController m) => m () +simplexChat = race_ runTerminalInput runChatController diff --git a/apps/dog-food/Simplex/Chat.hs b/apps/dog-food/Simplex/Chat.hs new file mode 100644 index 0000000000..6abea5f858 --- /dev/null +++ b/apps/dog-food/Simplex/Chat.hs @@ -0,0 +1,111 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.Chat where + +import Control.Applicative ((<|>)) +import Control.Monad.Except +import Control.Monad.IO.Unlift +import Control.Monad.Reader +import Data.Attoparsec.ByteString.Char8 (Parser) +import qualified Data.Attoparsec.ByteString.Char8 as A +import Data.Bifunctor (first) +import Data.ByteString.Char8 (ByteString) +import Data.Functor (($>)) +import qualified Data.Text as T +import Data.Text.Encoding (encodeUtf8) +import Simplex.Chat.Controller +import Simplex.Chat.Styled (plain) +import Simplex.Help +import Simplex.Messaging.Agent +import Simplex.Messaging.Agent.Protocol +import Simplex.Messaging.Parsers (parseAll) +import Simplex.Terminal +import Simplex.View +import Types +import UnliftIO.Async (race_) +import UnliftIO.STM + +data ChatCommand + = ChatHelp + | MarkdownHelp + | AddContact Contact + | Connect Contact SMPQueueInfo + | DeleteContact Contact + | SendMessage Contact ByteString + deriving (Show) + +runChatController :: (MonadUnliftIO m, MonadReader ChatController m) => m () +runChatController = race_ inputSubscriber agentSubscriber + +inputSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () +inputSubscriber = do + q <- asks inputQ + forever $ + atomically (readTBQueue q) >>= \case + InputControl _ -> pure () + InputCommand s -> + case parseAll chatCommandP . encodeUtf8 $ T.pack s of + Left e -> printToView [plain s, "invalid input: " <> plain e] + Right cmd -> do + case cmd of + SendMessage c msg -> showSentMessage c msg + _ -> printToView [plain s] + runExceptT (processChatCommand cmd) >>= \case + Left (ChatErrorAgent c e) -> showAgentError c e + _ -> pure () + +processChatCommand :: ChatMonad m => ChatCommand -> m () +processChatCommand = \case + ChatHelp -> printToView chatHelpInfo + MarkdownHelp -> printToView markdownInfo + AddContact c -> do + (_, qInfo) <- withAgent c (`createConnection` Just (fromContact c)) + showInvitation c qInfo + Connect c qInfo -> + void . withAgent c $ \smp -> joinConnection smp (Just $ fromContact c) qInfo + DeleteContact c -> do + withAgent c (`deleteConnection` fromContact c) + showContactDeleted c + unsetActive' $ ActiveC c + SendMessage c msg -> do + void . withAgent c $ \smp -> sendMessage smp (fromContact c) msg + setActive' $ ActiveC c + +agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () +agentSubscriber = do + q <- asks $ subQ . smpAgent + forever $ do + (_, a, resp) <- atomically (readTBQueue q) + case resp of + CON -> do + showContactConnected $ Contact a + setActive' $ ActiveC $ Contact a + END -> do + showContactDisconnected $ Contact a + unsetActive' $ ActiveC $ Contact a + MSG {brokerMeta, msgBody, msgIntegrity} -> do + -- ReceivedMessage contact (snd brokerMeta) msgBody msgIntegrity + showReceivedMessage (Contact a) (snd brokerMeta) msgBody msgIntegrity + setActive' $ ActiveC $ Contact a + _ -> pure () + +withAgent :: ChatMonad m => Contact -> (AgentClient -> ExceptT AgentErrorType m a) -> m a +withAgent c action = + asks smpAgent + >>= runExceptT . action + >>= liftEither . first (ChatErrorAgent c) + +chatCommandP :: Parser ChatCommand +chatCommandP = + ("/help" <|> "/h") $> ChatHelp + <|> ("/add " <|> "/a ") *> (AddContact <$> contact) + <|> ("/connect " <|> "/c ") *> (Connect <$> contact <* A.space <*> smpQueueInfoP) + <|> ("/delete " <|> "/d ") *> (DeleteContact <$> contact) + <|> A.char '@' *> (SendMessage <$> contact <* A.space <*> A.takeByteString) + <|> ("/markdown" <|> "/m") $> MarkdownHelp + where + contact = Contact <$> A.takeTill (== ' ') diff --git a/apps/dog-food/Simplex/Chat/Controller.hs b/apps/dog-food/Simplex/Chat/Controller.hs new file mode 100644 index 0000000000..33801778a9 --- /dev/null +++ b/apps/dog-food/Simplex/Chat/Controller.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Simplex.Chat.Controller where + +import Control.Exception +import Control.Monad.Except +import Control.Monad.IO.Unlift +import Control.Monad.Reader +import Numeric.Natural +import Simplex.Messaging.Agent (AgentClient) +import Simplex.Messaging.Agent.Protocol (AgentErrorType) +import Simplex.Terminal +import Types +import UnliftIO.STM + +data ChatController = ChatController + { smpAgent :: AgentClient, + chatTerminal :: ChatTerminal, + inputQ :: TBQueue InputEvent + } + +data InputEvent = InputCommand String | InputControl Char + +data ChatError = ChatErrorAgent Contact AgentErrorType + deriving (Show, Exception) + +type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) + +newChatController :: AgentClient -> ChatTerminal -> Natural -> STM ChatController +newChatController smpAgent chatTerminal qSize = do + inputQ <- newTBQueue qSize + pure ChatController {smpAgent, chatTerminal, inputQ} + +setActive' :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () +setActive' to = asks (activeTo . chatTerminal) >>= atomically . (`writeTVar` to) + +unsetActive' :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () +unsetActive' a = asks (activeTo . chatTerminal) >>= atomically . (`modifyTVar` unset) + where + unset a' = if a == a' then ActiveNone else a' diff --git a/apps/dog-food/Simplex/Help.hs b/apps/dog-food/Simplex/Help.hs new file mode 100644 index 0000000000..a508b1a76f --- /dev/null +++ b/apps/dog-food/Simplex/Help.hs @@ -0,0 +1,55 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.Help where + +import Data.List (intersperse) +import Simplex.Chat.Markdown +import Simplex.Chat.Styled +import System.Console.ANSI.Types + +chatHelpInfo :: [StyledString] +chatHelpInfo = + map + styleMarkdown + [ Markdown (Colored Cyan) "Using Simplex chat prototype.", + "Follow these steps to set up a connection:", + "", + Markdown (Colored Green) "Step 1: " <> Markdown (Colored Cyan) "/add bob" <> " -- Alice adds her contact, Bob (she can use any name).", + indent <> "Alice should send the invitation printed by the /add command", + indent <> "to her contact, Bob, out-of-band, via any trusted channel.", + "", + Markdown (Colored Green) "Step 2: " <> Markdown (Colored Cyan) "/connect alice " <> " -- Bob accepts the invitation.", + indent <> "Bob also can use any name for his contact, Alice,", + indent <> "followed by the invitation he received out-of-band.", + "", + Markdown (Colored Green) "Step 3: " <> "Bob and Alice are notified that the connection is set up,", + indent <> "both can now send messages:", + indent <> Markdown (Colored Cyan) "@bob Hello, Bob!" <> " -- Alice messages Bob.", + indent <> Markdown (Colored Cyan) "@alice Hey, Alice!" <> " -- Bob replies to Alice.", + "", + Markdown (Colored Green) "Other commands:", + indent <> Markdown (Colored Cyan) "/delete" <> " -- deletes contact and all messages with them.", + indent <> Markdown (Colored Cyan) "/markdown" <> " -- prints the supported markdown syntax.", + "", + "The commands may be abbreviated to a single letter: " <> listCommands ["/a", "/c", "/d", "/m"] + ] + where + listCommands = mconcat . intersperse ", " . map highlight + highlight = Markdown (Colored Cyan) + indent = " " + +markdownInfo :: [StyledString] +markdownInfo = + map + styleMarkdown + [ "Markdown:", + " *bold* - " <> Markdown Bold "bold text", + " _italic_ - " <> Markdown Italic "italic text" <> " (shown as underlined)", + " +underlined+ - " <> Markdown Underline "underlined text", + " ~strikethrough~ - " <> Markdown StrikeThrough "strikethrough text" <> " (shown as inverse)", + " `code snippet` - " <> Markdown Snippet "a + b // no *markdown* here", + " !1 text! - " <> red "red text" <> " (1-6: red, green, blue, yellow, cyan, magenta)", + " #secret# - " <> Markdown Secret "secret text" <> " (can be copy-pasted)" + ] + where + red = Markdown (Colored Red) diff --git a/apps/dog-food/ChatTerminal/Core.hs b/apps/dog-food/Simplex/Input.hs similarity index 57% rename from apps/dog-food/ChatTerminal/Core.hs rename to apps/dog-food/Simplex/Input.hs index d47e822059..494ba412d6 100644 --- a/apps/dog-food/ChatTerminal/Core.hs +++ b/apps/dog-food/Simplex/Input.hs @@ -1,50 +1,51 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -module ChatTerminal.Core where +module Simplex.Input where -import Control.Concurrent.STM -import Data.ByteString.Char8 (ByteString) +import Control.Monad.IO.Unlift +import Control.Monad.Reader import qualified Data.ByteString.Char8 as B import Data.List (dropWhileEnd) -import Data.Text (Text) -import qualified Data.Text as T -import Data.Text.Encoding -import Simplex.Chat.Markdown -import Styled -import System.Console.ANSI.Types +import Simplex.Chat.Controller +import Simplex.Terminal +import System.Exit (exitSuccess) import System.Terminal hiding (insertChars) import Types +import UnliftIO.STM -data ActiveTo = ActiveNone | ActiveC Contact | ActiveG Group - deriving (Eq) +getKey :: MonadTerminal m => m (Key, Modifiers) +getKey = + flush >> awaitEvent >>= \case + Left Interrupt -> liftIO exitSuccess + Right (KeyEvent key ms) -> pure (key, ms) + _ -> getKey -data ChatTerminal = ChatTerminal - { inputQ :: TBQueue String, - outputQ :: TBQueue [StyledString], - activeTo :: TVar ActiveTo, - termMode :: TermMode, - termState :: TVar TerminalState, - termSize :: Size, - nextMessageRow :: TVar Int, - termLock :: TMVar () - } +runTerminalInput :: (MonadUnliftIO m, MonadReader ChatController m) => m () +runTerminalInput = do + ChatController {inputQ, chatTerminal = ct} <- ask + liftIO . withTerminal . runTerminalT $ do + updateInput ct + receiveFromTTY inputQ ct -data TerminalState = TerminalState - { inputPrompt :: String, - inputString :: String, - inputPosition :: Int, - previousInput :: String - } +receiveFromTTY :: MonadTerminal m => TBQueue InputEvent -> ChatTerminal -> m () +receiveFromTTY inputQ ct@ChatTerminal {activeTo, termSize, termState} = + forever $ getKey >>= processKey >> withTermLock ct (updateInput ct) + where + processKey :: MonadTerminal m => (Key, Modifiers) -> m () + processKey = \case + (EnterKey, _) -> submitInput + key -> atomically $ do + ac <- readTVar activeTo + modifyTVar termState $ updateTermState ac (width termSize) key -inputHeight :: TerminalState -> ChatTerminal -> Int -inputHeight ts ct = length (inputPrompt ts <> inputString ts) `div` width (termSize ct) + 1 - -positionRowColumn :: Int -> Int -> Position -positionRowColumn wid pos = - let row = pos `div` wid - col = pos - row * wid - in Position {row, col} + submitInput :: MonadTerminal m => m () + submitInput = atomically $ do + ts <- readTVar termState + let s = inputString ts + writeTVar termState $ ts {inputString = "", inputPosition = 0, previousInput = s} + writeTBQueue inputQ $ InputCommand s updateTermState :: ActiveTo -> Int -> (Key, Modifiers) -> TerminalState -> TerminalState updateTermState ac tw (key, ms) ts@TerminalState {inputString = s, inputPosition = p} = case key of @@ -115,38 +116,3 @@ updateTermState ac tw (key, ms) ts@TerminalState {inputString = s, inputPosition afterWord = dropWhile (/= ' ') $ dropWhile (== ' ') after in min (length s) $ p + length after - length afterWord ts' (s', p') = ts {inputString = s', inputPosition = p'} - -styleMessage :: String -> String -> StyledString -styleMessage time msg = do - case msg of - "" -> "" - s@('@' : _) -> sentMessage s - s@('#' : _) -> sentMessage s - s -> markdown s - where - sentMessage :: String -> StyledString - sentMessage s = - let (c, rest) = span (/= ' ') s - in styleTime time <> " " <> styled (Colored Cyan) c <> markdown rest - markdown :: String -> StyledString - markdown = styleMarkdownText . T.pack - -styleTime :: String -> StyledString -styleTime = Styled [SetColor Foreground Vivid Black] - -safeDecodeUtf8 :: ByteString -> Text -safeDecodeUtf8 = decodeUtf8With onError - where - onError _ _ = Just '?' - -ttyContact :: Contact -> StyledString -ttyContact (Contact a) = styled (Colored Green) a - -ttyFromContact :: Contact -> StyledString -ttyFromContact (Contact a) = styled (Colored Yellow) $ a <> "> " - -ttyGroup :: Group -> StyledString -ttyGroup (Group g) = styled (Colored Blue) $ "#" <> g - -ttyFromGroup :: Group -> Contact -> StyledString -ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " diff --git a/apps/dog-food/Simplex/Terminal.hs b/apps/dog-food/Simplex/Terminal.hs new file mode 100644 index 0000000000..e9cd618aa3 --- /dev/null +++ b/apps/dog-food/Simplex/Terminal.hs @@ -0,0 +1,154 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Simplex.Terminal where + +import Simplex.Chat.Styled +import System.Console.ANSI.Types +import System.Terminal +import Types +import UnliftIO.STM + +data ActiveTo = ActiveNone | ActiveC Contact | ActiveG Group + deriving (Eq) + +data ChatTerminal = ChatTerminal + { activeTo :: TVar ActiveTo, + termState :: TVar TerminalState, + termSize :: Size, + nextMessageRow :: TVar Int, + termLock :: TMVar () + } + +data TerminalState = TerminalState + { inputPrompt :: String, + inputString :: String, + inputPosition :: Int, + previousInput :: String + } + +newChatTerminal :: IO ChatTerminal +newChatTerminal = do + activeTo <- newTVarIO ActiveNone + termSize <- withTerminal . runTerminalT $ getWindowSize + let lastRow = height termSize - 1 + termState <- newTVarIO newTermState + termLock <- newTMVarIO () + nextMessageRow <- newTVarIO lastRow + -- threadDelay 500000 -- this delay is the same as timeout in getTerminalSize + return ChatTerminal {activeTo, termState, termSize, nextMessageRow, termLock} + +newTermState :: TerminalState +newTermState = + TerminalState + { inputString = "", + inputPosition = 0, + inputPrompt = "> ", + previousInput = "" + } + +withTermLock :: MonadTerminal m => ChatTerminal -> m () -> m () +withTermLock ChatTerminal {termLock} action = do + _ <- atomically $ takeTMVar termLock + action + atomically $ putTMVar termLock () + +printToTerminal :: ChatTerminal -> [StyledString] -> IO () +printToTerminal ct s = withTerminal . runTerminalT . withTermLock ct $ do + printMessage ct s + updateInput ct + +updateInput :: forall m. MonadTerminal m => ChatTerminal -> m () +updateInput ChatTerminal {termSize = Size {height, width}, termState, nextMessageRow} = do + hideCursor + ts <- readTVarIO termState + nmr <- readTVarIO nextMessageRow + let ih = inputHeight ts + iStart = height - ih + prompt = inputPrompt ts + Position {row, col} = positionRowColumn width $ length prompt + inputPosition ts + if nmr >= iStart + then atomically $ writeTVar nextMessageRow iStart + else clearLines nmr iStart + setCursorPosition $ Position {row = max nmr iStart, col = 0} + putString $ prompt <> inputString ts <> " " + eraseInLine EraseForward + setCursorPosition $ Position {row = iStart + row, col} + showCursor + flush + where + clearLines :: Int -> Int -> m () + clearLines from till + | from >= till = return () + | otherwise = do + setCursorPosition $ Position {row = from, col = 0} + eraseInLine EraseForward + clearLines (from + 1) till + inputHeight :: TerminalState -> Int + inputHeight ts = length (inputPrompt ts <> inputString ts) `div` width + 1 + positionRowColumn :: Int -> Int -> Position + positionRowColumn wid pos = + let row = pos `div` wid + col = pos - row * wid + in Position {row, col} + +printMessage :: forall m. MonadTerminal m => ChatTerminal -> [StyledString] -> m () +printMessage ChatTerminal {termSize = Size {height, width}, nextMessageRow} msg = do + nmr <- readTVarIO nextMessageRow + setCursorPosition $ Position {row = nmr, col = 0} + mapM_ printStyled msg + flush + let lc = sum $ map lineCount msg + atomically . writeTVar nextMessageRow $ min (height - 1) (nmr + lc) + where + lineCount :: StyledString -> Int + lineCount s = sLength s `div` width + 1 + printStyled :: StyledString -> m () + printStyled s = do + putStyled s + eraseInLine EraseForward + putLn + +-- Currently it is assumed that the message does not have internal line breaks. +-- Previous implementation "kind of" supported them, +-- but it was not determining the number of printed lines correctly +-- because of accounting for control sequences in length +putStyled :: MonadTerminal m => StyledString -> m () +putStyled (s1 :<>: s2) = putStyled s1 >> putStyled s2 +putStyled (Styled [] s) = putString s +putStyled (Styled sgr s) = setSGR sgr >> putString s >> resetAttributes + +setSGR :: MonadTerminal m => [SGR] -> m () +setSGR = mapM_ $ \case + Reset -> resetAttributes + SetConsoleIntensity BoldIntensity -> setAttribute bold + SetConsoleIntensity _ -> resetAttribute bold + SetItalicized True -> setAttribute italic + SetItalicized _ -> resetAttribute italic + SetUnderlining NoUnderline -> resetAttribute underlined + SetUnderlining _ -> setAttribute underlined + SetSwapForegroundBackground True -> setAttribute inverted + SetSwapForegroundBackground _ -> resetAttribute inverted + SetColor l i c -> setAttribute . layer l . intensity i $ color c + SetBlinkSpeed _ -> pure () + SetVisible _ -> pure () + SetRGBColor _ _ -> pure () + SetPaletteColor _ _ -> pure () + SetDefaultColor _ -> pure () + where + layer = \case + Foreground -> foreground + Background -> background + intensity = \case + Dull -> id + Vivid -> bright + color = \case + Black -> black + Red -> red + Green -> green + Yellow -> yellow + Blue -> blue + Magenta -> magenta + Cyan -> cyan + White -> white diff --git a/apps/dog-food/Simplex/View.hs b/apps/dog-food/Simplex/View.hs new file mode 100644 index 0000000000..7b431540cd --- /dev/null +++ b/apps/dog-food/Simplex/View.hs @@ -0,0 +1,151 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.View + ( printToView, + showInvitation, + showAgentError, + showContactDeleted, + showContactConnected, + showContactDisconnected, + showReceivedMessage, + showSentMessage, + ttyContact, + ttyFromContact, + ttyGroup, + ttyFromGroup, + ) +where + +import Control.Monad.IO.Unlift +import Control.Monad.Reader +import Data.ByteString.Char8 (ByteString) +import Data.Composition ((.:)) +import Data.Text (Text) +import qualified Data.Text as T +import Data.Text.Encoding (decodeUtf8With) +import Data.Time.Clock (DiffTime, UTCTime) +import Data.Time.Format (defaultTimeLocale, formatTime) +import Data.Time.LocalTime (TimeZone, ZonedTime, getCurrentTimeZone, getZonedTime, localDay, localTimeOfDay, timeOfDayToTime, utcToLocalTime, zonedTimeToLocalTime) +import Simplex.Chat.Controller +import Simplex.Chat.Markdown +import Simplex.Chat.Styled +import Simplex.Messaging.Agent.Protocol +import Simplex.Terminal (printToTerminal) +import System.Console.ANSI.Types +import Types + +type ChatReader m = (MonadUnliftIO m, MonadReader ChatController m) + +showInvitation :: ChatReader m => Contact -> SMPQueueInfo -> m () +showInvitation = printToView .: invitation + +showAgentError :: ChatReader m => Contact -> AgentErrorType -> m () +showAgentError = printToView .: agentError + +showContactDeleted :: ChatReader m => Contact -> m () +showContactDeleted = printToView . contactDeleted + +showContactConnected :: ChatReader m => Contact -> m () +showContactConnected = printToView . contactConnected + +showContactDisconnected :: ChatReader m => Contact -> m () +showContactDisconnected = printToView . contactDisconnected + +showReceivedMessage :: ChatReader m => Contact -> UTCTime -> ByteString -> MsgIntegrity -> m () +showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage c utcTime msg mOk) + +showSentMessage :: ChatReader m => Contact -> ByteString -> m () +showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) + +invitation :: Contact -> SMPQueueInfo -> [StyledString] +invitation c qInfo = + [ "pass this invitation to your contact " <> ttyContact c <> " (via any channel): ", + "", + (bPlain . serializeSmpQueueInfo) qInfo, + "", + "and ask them to connect: /c " + ] + +contactDeleted :: Contact -> [StyledString] +contactDeleted c = [ttyContact c <> " is deleted"] + +contactConnected :: Contact -> [StyledString] +contactConnected c = [ttyContact c <> " is connected"] + +contactDisconnected :: Contact -> [StyledString] +contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] + +receivedMessage :: Contact -> UTCTime -> ByteString -> MsgIntegrity -> IO [StyledString] +receivedMessage c utcTime msg mOk = do + t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime + pure $ prependFirst (t <> " " <> ttyFromContact c) (msgPlain msg) ++ showIntegrity mOk + where + formatUTCTime :: TimeZone -> ZonedTime -> StyledString + formatUTCTime localTz currentTime = + let localTime = utcToLocalTime localTz utcTime + format = + if (localDay localTime < localDay (zonedTimeToLocalTime currentTime)) + && (timeOfDayToTime (localTimeOfDay localTime) > (6 * 60 * 60 :: DiffTime)) + then "%m-%d" -- if message is from yesterday or before and 6 hours has passed since midnight + else "%H:%M" + in styleTime $ formatTime defaultTimeLocale format localTime + showIntegrity :: MsgIntegrity -> [StyledString] + showIntegrity MsgOk = [] + showIntegrity (MsgError err) = msgError $ case err of + MsgSkipped fromId toId -> + "skipped message ID " <> show fromId + <> if fromId == toId then "" else ".." <> show toId + MsgBadId msgId -> "unexpected message ID " <> show msgId + MsgBadHash -> "incorrect message hash" + MsgDuplicate -> "duplicate message ID" + msgError :: String -> [StyledString] + msgError s = [styled (Colored Red) s] + +sentMessage :: Contact -> ByteString -> IO [StyledString] +sentMessage c msg = do + time <- formatTime defaultTimeLocale "%H:%M" <$> getZonedTime + pure $ prependFirst (styleTime time <> " " <> ttyToContact c) (msgPlain msg) + +prependFirst :: StyledString -> [StyledString] -> [StyledString] +prependFirst s [] = [s] +prependFirst s (s' : ss) = (s <> s') : ss + +msgPlain :: ByteString -> [StyledString] +msgPlain = map styleMarkdownText . T.lines . safeDecodeUtf8 + +agentError :: Contact -> AgentErrorType -> [StyledString] +agentError c = \case + CONN e -> case e of + NOT_FOUND -> ["no contact " <> ttyContact c] + DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] + SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] + e -> ["chat error: " <> plain (show e)] + +printToView :: (MonadUnliftIO m, MonadReader ChatController m) => [StyledString] -> m () +printToView s = asks chatTerminal >>= liftIO . (`printToTerminal` s) + +ttyContact :: Contact -> StyledString +ttyContact (Contact a) = styled (Colored Green) a + +ttyToContact :: Contact -> StyledString +ttyToContact (Contact a) = styled (Colored Cyan) $ a <> " " + +ttyFromContact :: Contact -> StyledString +ttyFromContact (Contact a) = styled (Colored Yellow) $ a <> "> " + +ttyGroup :: Group -> StyledString +ttyGroup (Group g) = styled (Colored Blue) $ "#" <> g + +ttyFromGroup :: Group -> Contact -> StyledString +ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " + +styleTime :: String -> StyledString +styleTime = Styled [SetColor Foreground Vivid Black] + +safeDecodeUtf8 :: ByteString -> Text +safeDecodeUtf8 = decodeUtf8With onError + where + onError _ _ = Just '?' diff --git a/apps/dog-food/Types.hs b/apps/dog-food/Types.hs index f31c97d9bd..4dc0314325 100644 --- a/apps/dog-food/Types.hs +++ b/apps/dog-food/Types.hs @@ -1,16 +1,7 @@ -{-# LANGUAGE LambdaCase #-} - module Types where import Data.ByteString.Char8 (ByteString) -newtype Contact = Contact {toBs :: ByteString} deriving (Eq, Show) +newtype Contact = Contact {fromContact :: ByteString} deriving (Eq, Show) newtype Group = Group {fromGroup :: ByteString} deriving (Eq, Show) - -data TermMode = TermModeBasic | TermModeEditor deriving (Eq) - -termModeName :: TermMode -> String -termModeName = \case - TermModeBasic -> "basic" - TermModeEditor -> "editor" diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs new file mode 100644 index 0000000000..e6d0f30cdd --- /dev/null +++ b/apps/simplex-chat/Main.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import Control.Concurrent (threadDelay) +import Control.Concurrent.STM (readTVarIO, retry) +import Control.Monad (forever, void) +import Simplex.Demo (chatLayoutDemo) +import Simplex.Store (createStore) +import System.IO (hFlush, stdout) +import System.Terminal (putStringLn, runTerminalT, withTerminal) +import qualified System.Terminal as C +import qualified System.Terminal.Internal as C + +defaultSettings :: C.Size -> C.VirtualTerminalSettings +defaultSettings size = + C.VirtualTerminalSettings + { C.virtualType = "xterm", + C.virtualWindowSize = pure size, + C.virtualEvent = retry, + C.virtualInterrupt = retry + } + +main :: IO () +main = do + void $ createStore "simplex-chat.db" 4 + + hFlush stdout + -- ChatTerminal {termSize} <- newChatTerminal + -- pos <- C.withVirtualTerminal (defaultSettings termSize) $ + -- \t -> runTerminalT (C.setAlternateScreenBuffer True >> C.putString "a" >> C.flush >> C.getCursorPosition) t + -- print pos + -- race_ (printEvents t) (updateTerminal t) + void . withTerminal . runTerminalT $ chatLayoutDemo >> C.flush >> C.awaitEvent + +printEvents :: C.VirtualTerminal -> IO () +printEvents t = forever $ do + event <- withTerminal . runTerminalT $ C.flush >> C.awaitEvent + runTerminalT (putStringLn $ show event) t + +updateTerminal :: C.VirtualTerminal -> IO () +updateTerminal t = forever $ do + threadDelay 10000 + win <- readTVarIO $ C.virtualWindow t + withTerminal . runTerminalT $ mapM_ C.putStringLn win >> C.flush diff --git a/apps/simplex-chat/Simplex/Demo.hs b/apps/simplex-chat/Simplex/Demo.hs new file mode 100644 index 0000000000..4bc5cb9e69 --- /dev/null +++ b/apps/simplex-chat/Simplex/Demo.hs @@ -0,0 +1,119 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.Demo where + +import Simplex.Chat.Styled +import System.Console.ANSI.Types +import System.Terminal + +someViewUpdate :: Monad m => m () +someViewUpdate = pure () + +chatLayoutDemo :: MonadTerminal m => m () +chatLayoutDemo = + mapM_ + putStyledLn + [ " search " <> Styled gray "(ctrl-s) " <> lineV <> Styled toContact " @bob " <> "Bob Roberts " <> Styled greenColor "@john" <> "", + " " <> lineV <> Styled gray " 14:15 online profile (ctrl-p)", + lineH 20 <> crossover <> lineH 59, + "* " <> Styled [SetConsoleIntensity BoldIntensity] "all chats " <> " " <> lineV <> "", + Styled gray " (ctrl-a) " <> lineV <> "", + "*" <> Styled toContact " @alice " <> Styled darkGray "14:37 " <> lineV <> "", + Styled gray " Hello there! ... " <> lineV <> "", + Styled selected " " <> Styled (toContact <> selected) " @bob " <> Styled (selected <> gray) "12:35 " <> lineV <> "", + Styled selected " All good, John... " <> lineV <> "", + "*" <> Styled group " #team " <> Styled darkGray "10:55 " <> lineV <> "", + Styled gray " What's up ther... " <> lineV <> "", + " " <> Styled toContact " @tom " <> Styled darkGray "Wed " <> lineV <> "", + Styled gray " Have you seen ... " <> lineV <> "", + " " <> lineV, + " " <> lineV, + " " <> lineV, + " " <> lineV, + " " <> lineV, + " " <> lineV <> Styled greenColor " ✔︎" <> Styled darkGray " 12:30" <> Styled toContact " @bob" <> " hey bob - how is it going?", + " " <> lineV <> Styled greenColor " ✔︎" <> Styled darkGray " " <> Styled toContact " " <> " let's meet soon!", + " " <> lineV <> " *" <> Styled darkGray " 12:35" <> Styled contact " bob>" <> " All good, John! How are you?", + " " <> teeL <> lineH 59, + " " <> lineV <> " > " <> Styled toContact "@bob" <> " 😀 This is the message that will be sent to @bob" + ] + >> putStyled (Styled ctrlKeys " help (ctrl-h) new contact (ctrl-n) choose chat (ctrl-↓↑) new group (ctrl-g) ") + +contact :: [SGR] +contact = [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Yellow] + +toContact :: [SGR] +toContact = [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Cyan] + +group :: [SGR] +group = [SetConsoleIntensity BoldIntensity, SetColor Foreground Vivid Cyan] + +selected :: [SGR] +selected = [SetColor Background Vivid Black] + +ctrlKeys :: [SGR] +ctrlKeys = [SetColor Background Dull White, SetColor Foreground Dull Black] + +gray :: [SGR] +gray = [SetColor Foreground Dull White] + +darkGray :: [SGR] +darkGray = [SetColor Foreground Vivid Black] + +greenColor :: [SGR] +greenColor = [SetColor Foreground Vivid Green] + +lineV :: StyledString +lineV = Styled selected " " -- "\x2502" + +lineH :: Int -> StyledString +lineH n = Styled darkGray $ replicate n '\x2500' + +teeL :: StyledString +teeL = Styled selected " " -- "\x251C" + +crossover :: StyledString +crossover = Styled selected " " -- "\x253C" + +putStyledLn :: MonadTerminal m => StyledString -> m () +putStyledLn s = putStyled s >> putLn + +putStyled :: MonadTerminal m => StyledString -> m () +putStyled (s1 :<>: s2) = putStyled s1 >> putStyled s2 +putStyled (Styled [] s) = putString s +putStyled (Styled sgr s) = setSGR sgr >> putString s >> resetAttributes + +setSGR :: MonadTerminal m => [SGR] -> m () +setSGR = mapM_ $ \case + Reset -> resetAttributes + SetConsoleIntensity BoldIntensity -> setAttribute bold + SetConsoleIntensity _ -> resetAttribute bold + SetItalicized True -> setAttribute italic + SetItalicized _ -> resetAttribute italic + SetUnderlining NoUnderline -> resetAttribute underlined + SetUnderlining _ -> setAttribute underlined + SetSwapForegroundBackground True -> setAttribute inverted + SetSwapForegroundBackground _ -> resetAttribute inverted + SetColor l i c -> setAttribute . layer l . intensity i $ color c + SetBlinkSpeed _ -> pure () + SetVisible _ -> pure () + SetRGBColor _ _ -> pure () + SetPaletteColor _ _ -> pure () + SetDefaultColor _ -> pure () + where + layer = \case + Foreground -> foreground + Background -> background + intensity = \case + Dull -> id + Vivid -> bright + color = \case + Black -> black + Red -> red + Green -> green + Yellow -> yellow + Blue -> blue + Magenta -> magenta + Cyan -> cyan + White -> white diff --git a/apps/simplex-chat/Simplex/Store.hs b/apps/simplex-chat/Simplex/Store.hs new file mode 100644 index 0000000000..4d2638d252 --- /dev/null +++ b/apps/simplex-chat/Simplex/Store.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE TemplateHaskell #-} + +module Simplex.Store where + +import Control.Concurrent.STM +import Control.Monad (replicateM_) +import Data.FileEmbed (embedDir, makeRelativeToProject) +import Data.Function (on) +import Data.List (sortBy) +import Data.Text.Encoding (decodeUtf8) +import qualified Database.SQLite.Simple as DB +import Numeric.Natural (Natural) +import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), connectSQLiteStore, createSQLiteStore) +import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) +import System.FilePath (takeBaseName, takeExtension) + +-- | The list of migrations in ascending order by date +migrations :: [Migration] +migrations = + sortBy (compare `on` name) . map migration . filter sqlFile $ + $(makeRelativeToProject "migrations" >>= embedDir) + where + sqlFile (file, _) = takeExtension file == ".sql" + migration (file, qStr) = Migration {name = takeBaseName file, up = decodeUtf8 qStr} + +data SQLitePool = SQLitePool + { dbFilePath :: FilePath, + dbPool :: TBQueue DB.Connection, + dbNew :: Bool + } + +createStore :: FilePath -> Natural -> IO SQLitePool +createStore dbFilePath poolSize = do + SQLiteStore {dbConn = c, dbNew} <- createSQLiteStore dbFilePath migrations + dbPool <- newTBQueueIO poolSize + atomically $ writeTBQueue dbPool c + replicateM_ (fromInteger $ toInteger $ poolSize - 1) $ + connectSQLiteStore dbFilePath >>= atomically . writeTBQueue dbPool . dbConn + pure SQLitePool {dbFilePath, dbPool, dbNew} diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql new file mode 100644 index 0000000000..4c8ac3a254 --- /dev/null +++ b/migrations/20210612_initial.sql @@ -0,0 +1,160 @@ +CREATE TABLE contact_profiles ( -- remote user profile + contact_profile_id INTEGER PRIMARY KEY, + contact_ref TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces + properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties +); + +-- the first record (id = 1) is reserved for the first local user +INSERT INTO contact_profiles (contact_profile_id, contact_ref) VALUES (1, ''); + + +CREATE TABLE users ( + user_id INTEGER PRIMARY KEY, + contact_profile_id INTEGER NOT NULL UNIQUE REFERENCES contact_profiles -- user's profile +); + +-- the first record (id = 1) is reserved for the first local user +INSERT INTO users (user_id, contact_profile_id) VALUES (1, 1); + +CREATE TABLE known_servers( + server_id INTEGER PRIMARY KEY, + host TEXT NOT NULL, + port TEXT NOT NULL, + key_hash BLOB, + user_id INTEGER NOT NULL REFERENCES user_id, + UNIQUE (user_id, host, port) +) WITHOUT ROWID; + +CREATE TABLE contacts ( + contact_id INTEGER PRIMARY KEY, + local_contact_ref TEXT NOT NULL UNIQUE, -- contact name set by local user - must be unique + local_properties TEXT NOT NULL DEFAULT '{}', -- JSON set by local user + contact_profile_id INTEGER UNIQUE REFERENCES contact_profiles, -- profile sent by remote contact, NULL for incognito contacts + contact_status TEXT NOT NULL DEFAULT '', + user_id INTEGER NOT NULL REFERENCES user_id +); + +CREATE TABLE connections ( -- all SMP agent connections + connection_id INTEGER PRIMARY KEY, + agent_conn_id BLOB NOT NULL UNIQUE, + conn_level INTEGER NOT NULL DEFAULT 0, + via_conn BLOB REFERENCES contact_connections (connection_id), + conn_status TEXT NOT NULL DEFAULT '', + user_id INTEGER NOT NULL REFERENCES user_id +); + +CREATE TABLE contact_connections ( -- connections only for direct messages, many per contact + connection_id INTEGER NOT NULL UNIQUE REFERENCES connections ON DELETE CASCADE, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT -- connection must be removed first via the agent +); + +CREATE TABLE contact_invitations ( + invitation_id INTEGER PRIMARY KEY, + agent_inv_id BLOB UNIQUE, + invitation TEXT, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, + invitation_status TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE group_profiles ( -- shared group profiles + group_profile_id INTEGER PRIMARY KEY, + group_ref TEXT NOT NULL, -- this name must not contain spaces + properties TEXT NOT NULL DEFAULT '{}' -- JSON with user or contact profile +); + +CREATE TABLE groups ( + group_id INTEGER PRIMARY KEY, -- local group ID + local_group_ref TEXT NOT NULL UNIQUE, -- local group name without spaces + local_properties TEXT NOT NULL, -- local JSON group properties + group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile + user_group_member_details_id INTEGER NOT NULL + REFERENCES group_member_details (group_member_details_id) ON DELETE RESTRICT, + user_id INTEGER NOT NULL REFERENCES user_id +); + +CREATE TABLE group_members ( -- group members, excluding the local user + group_member_id INTEGER PRIMARY KEY, + group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, + group_member_details_id INTEGER NOT NULL REFERENCES group_member_details ON DELETE RESTRICT, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, + connection_id INTEGER UNIQUE REFERENCES connections +); + +CREATE TABLE group_member_details ( + group_member_details_id INTEGER PRIMARY KEY, + group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, + member_id BLOB NOT NULL, -- shared member ID, unique per group + member_role TEXT NOT NULL DEFAULT '', -- owner, admin, moderator, '' + member_status TEXT NOT NULL DEFAULT '', -- inv | con | full | off + invited_by INTEGER REFERENCES contacts ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator + UNIQUE (group_id, member_id) +); + +CREATE TABLE events ( -- messages received by the agent, append only + event_id INTEGER PRIMARY KEY, + agent_msg_id INTEGER NOT NULL, -- internal message ID + external_msg_id INTEGER NOT NULL, -- external message ID (sent or received) + agent_meta TEXT NOT NULL, -- JSON with timestamps etc. sent in MSG + connection_id INTEGER NOT NULL REFERENCES connections, + received INTEGER NOT NULL, -- 0 for received, 1 for sent + event_type TEXT NOT NULL, -- event type - see protocol/types.ts + event_encoding INTEGER NOT NULL, -- format of event_body: 0 - binary, 1 - text utf8, 2 - JSON (utf8) + content_type TEXT NOT NULL, -- content type - see protocol/types.ts + event_body BLOB, -- agent message body as sent + event_hash BLOB NOT NULL, + integrity TEXT NOT NULL DEFAULT '', + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE INDEX events_external_msg_id_index ON events (connection_id, external_msg_id); + +CREATE TABLE contact_profile_events ( + event_id INTEGER NOT NULL UNIQUE REFERENCES events, + contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles +); + +CREATE TABLE group_profile_events ( + event_id INTEGER NOT NULL UNIQUE REFERENCES events, + group_profile_id INTEGER NOT NULL REFERENCES group_profiles +); + +CREATE TABLE group_events ( + event_id INTEGER NOT NULL UNIQUE REFERENCES events, + group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, + group_member_id INTEGER REFERENCES group_members -- NULL for current user +); + +CREATE TABLE group_event_parents ( + group_event_parent_id INTEGER PRIMARY KEY, + event_id INTEGER NOT NULL REFERENCES group_events (event_id), + parent_group_member_id INTEGER REFERENCES group_members (group_member_id), -- can be NULL if parent_member_id is incorrect + parent_member_id BLOB, -- shared member ID, unique per group + parent_event_id INTEGER REFERENCES events (event_id) ON DELETE CASCADE, -- this can be NULL if received event references another event that's not received yet + parent_external_msg_id INTEGER NOT NULL, + parent_event_hash BLOB NOT NULL +); + +CREATE INDEX group_event_parents_parent_external_msg_id_index + ON group_event_parents (parent_member_id, parent_external_msg_id); + +CREATE TABLE blobs ( + blob_id INTEGER PRIMARY KEY, + content BLOB NOT NULL +); + +CREATE TABLE messages ( -- mutable messages presented to user + message_id INTEGER PRIMARY KEY, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, -- 1 for sent messages + group_id INTEGER REFERENCES groups ON DELETE RESTRICT, -- NULL for direct messages + deleted INTEGER NOT NULL, -- 1 for deleted + msg_type TEXT NOT NULL, + content_type TEXT NOT NULL, + msg_text TEXT NOT NULL, -- textual representation + msg_props TEXT NOT NULL, -- JSON + msg_blob_id INTEGER REFERENCES blobs (blob_id) ON DELETE RESTRICT -- optional binary content +); + +CREATE TABLE message_events ( + event_id INTEGER NOT NULL UNIQUE REFERENCES events, + message_id INTEGER NOT NULL REFERENCES messages +); diff --git a/package.yaml b/package.yaml index cd8ba49cda..6941856819 100644 --- a/package.yaml +++ b/package.yaml @@ -15,7 +15,9 @@ dependencies: - ansi-terminal == 0.10.* - attoparsec == 0.13.* - base >= 4.7 && < 5 + - bytestring == 0.10.* - containers == 0.6.* + - simplexmq == 0.3.* - text == 1.2.* library: @@ -29,6 +31,7 @@ executables: - simplex-chat - async == 2.2.* - bytestring == 0.10.* + - composition == 1.0.* - directory == 1.3.* - filepath == 1.4.* - mtl == 2.2.* @@ -39,6 +42,28 @@ executables: - terminal == 0.2.* - time == 1.9.* - unliftio == 0.2.* + - unliftio-core == 0.2.* + ghc-options: + - -threaded + + simplex-chat: + source-dirs: apps/simplex-chat + main: Main.hs + dependencies: + - simplex-chat + - async == 2.2.* + - bytestring == 0.10.* + - composition == 1.0.* + - cryptonite == 0.27.* + - file-embed == 0.0.14.* + - filepath == 1.4.* + - mtl == 2.2.* + - simplexmq == 0.3.* + - sqlite-simple == 0.4.* + - stm == 2.5.* + - terminal == 0.2.* + - unliftio == 0.2.* + - unliftio-core == 0.2.* ghc-options: - -threaded diff --git a/protocol/types.ts b/protocol/types.ts new file mode 100644 index 0000000000..f2084480e1 --- /dev/null +++ b/protocol/types.ts @@ -0,0 +1,74 @@ +// x. namespace is for chat messages transmitted inside SMP agent MSG +type MemberMessageType = + | "x.grp.info" // group profile information or update + | "x.grp.off" // disable group + | "x.grp.del" // group deleted + | "x.grp.mem.new" // new group member + | "x.grp.mem.acl" // group member permissions (ACL) + | "x.grp.mem.leave" // group member left + | "x.grp.mem.off" // suspend group member + | "x.grp.mem.on" // enable group member + | "x.grp.mem.del" // group member removed + +type ProfileMessageType = + | "x.info" // profile information or update + | "x.info.grp" // information about group in profile + | "x.info.con" // information about contact in profile + +type NotificationMessageType = "x.msg.read" + +type OpenConnMessageType = + | "x.open.grp" // open invitation to the group + | "x.open.con" // open invitation to the contact + +type ContentMessageType = + | "x.msg.new" // new message + | "x.msg.append" // additional part of the message + | "x.msg.del" // delete message + | "x.msg.update" // update message + | "x.msg.fwd" // forward message + | "x.msg.reply" // reply to message + +// TODO namespace for chat messages transmitted as other agent messages + +type DirectMessageType = + | ProfileMessageType + | NotificationMessageType + | OpenConnMessageType + | ContentMessageType + +type GroupMessageType = MemberMessageType | DirectMessageType + +type ContentType = + | "c.text" + | "c.html" + | "c.image" + | "c.audio" + | "c.video" + | "c.doc" + | "c.sticker" + | "c.file" + | "c.link" + | "c.form" + | "c.poll" + | "c.applet" + +// the type of message data transmitted inside SMP agent MSG +interface MessageData { + type: T + sent: Date + data: unknown +} + +interface DirectMessageData extends MessageData {} + +interface GroupMessageData extends MessageData { + msgId: number + parents: ParentMessage[] +} + +interface ParentMessage { + memberId: Uint8Array + msgId: number + msgHash: Uint8Array +} diff --git a/src/Simplex/Chat/Protocol._hs b/src/Simplex/Chat/Protocol._hs deleted file mode 100644 index c635702f5e..0000000000 --- a/src/Simplex/Chat/Protocol._hs +++ /dev/null @@ -1,13 +0,0 @@ -module Simplex.Chat.Protocol where - -data Profile = Profile - { displayName :: Text, - fullName :: Text - } - -data Contact = Contact - { profile :: Profile, - connection :: ConnAlias - } - -data ChatMessage = ContentMessage | ReadNotification | FileTransfer diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs new file mode 100644 index 0000000000..f0635279e9 --- /dev/null +++ b/src/Simplex/Chat/Protocol.hs @@ -0,0 +1,44 @@ +{-# LANGUAGE DuplicateRecordFields #-} + +module Simplex.Chat.Protocol where + +import Data.ByteString (ByteString) +import Data.Text (Text) +import Simplex.Messaging.Agent.Protocol (ConnId) + +data ChatEvent = GroupEvent | MessageEvent | InfoEvent + +data Profile = Profile + { profileId :: ByteString, + displayName :: Text + } + +data Contact = Contact + { contactId :: ByteString, + profile :: Profile, + connections :: [Connection] + } + +data Connection = Connection + { connId :: ConnId, + connLevel :: Int, + viaConn :: ConnId + } + +data GroupMember = GroupMember + { groupId :: ByteString, + sharedMemberId :: ByteString, + contact :: Contact, + memberRole :: GroupMemberRole, + memberStatus :: GroupMemberStatus + } + +data GroupMemberRole = GROwner | GRAdmin | GRStandard + +data GroupMemberStatus = GSInvited | GSConnected | GSConnectedAll + +data Group = Group + { groupId :: ByteString, + displayName :: Text, + members :: [GroupMember] + } diff --git a/apps/dog-food/Styled.hs b/src/Simplex/Chat/Styled.hs similarity index 98% rename from apps/dog-food/Styled.hs rename to src/Simplex/Chat/Styled.hs index 90341b5e9e..45e7f87c21 100644 --- a/apps/dog-food/Styled.hs +++ b/src/Simplex/Chat/Styled.hs @@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} -module Styled +module Simplex.Chat.Styled ( StyledString (..), bPlain, plain, diff --git a/stack.yaml b/stack.yaml index 0023ad2b1b..d0635751fb 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: dffa7a61006aa5c4050c954857aaf1357fe33242 + commit: 09c6adeabc533537dcc039e2195123c6f7167ebe # # extra-deps: [] From 5a2ded775d0f1c87d228b40817a568b1e6e7e12f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 25 Jun 2021 18:34:29 +0100 Subject: [PATCH 02/29] rename app folder (#63) * rename app folder * clean up package.yaml --- apps/dog-food/Main.hs | 61 ---------- .../{dog-food => simplex-chat}/ChatOptions.hs | 0 apps/simplex-chat/Main.hs | 109 +++++++++++++----- .../Simplex/Chat.hs | 0 .../Simplex/Chat/Controller.hs | 0 .../Simplex/Help.hs | 0 .../Simplex/Input.hs | 0 .../Simplex/Terminal.hs | 0 .../Simplex/View.hs | 0 apps/{dog-food => simplex-chat}/Types.hs | 0 package.yaml | 27 +---- 11 files changed, 82 insertions(+), 115 deletions(-) delete mode 100644 apps/dog-food/Main.hs rename apps/{dog-food => simplex-chat}/ChatOptions.hs (100%) rename apps/{dog-food => simplex-chat}/Simplex/Chat.hs (100%) rename apps/{dog-food => simplex-chat}/Simplex/Chat/Controller.hs (100%) rename apps/{dog-food => simplex-chat}/Simplex/Help.hs (100%) rename apps/{dog-food => simplex-chat}/Simplex/Input.hs (100%) rename apps/{dog-food => simplex-chat}/Simplex/Terminal.hs (100%) rename apps/{dog-food => simplex-chat}/Simplex/View.hs (100%) rename apps/{dog-food => simplex-chat}/Types.hs (100%) diff --git a/apps/dog-food/Main.hs b/apps/dog-food/Main.hs deleted file mode 100644 index 8899d41690..0000000000 --- a/apps/dog-food/Main.hs +++ /dev/null @@ -1,61 +0,0 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Main where - -import ChatOptions -import Control.Concurrent.STM -import Control.Logger.Simple -import Control.Monad.IO.Unlift -import Control.Monad.Reader -import Simplex.Chat -import Simplex.Chat.Controller -import Simplex.Input -import Simplex.Messaging.Agent (getSMPAgentClient) -import Simplex.Messaging.Agent.Env.SQLite -import Simplex.Messaging.Client (smpDefaultConfig) -import Simplex.Terminal -import System.Directory (getAppUserDataDirectory) -import UnliftIO.Async (race_) - -cfg :: AgentConfig -cfg = - AgentConfig - { tcpPort = undefined, -- agent does not listen to TCP - smpServers = undefined, -- filled in from options - rsaKeySize = 2048 `div` 8, - connIdBytes = 12, - tbqSize = 16, - dbFile = "smp-chat.db", - smpCfg = smpDefaultConfig - } - -logCfg :: LogConfig -logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} - -main :: IO () -main = do - ChatOpts {dbFile, smpServers} <- welcomeGetOpts - ct <- newChatTerminal - a <- getSMPAgentClient cfg {dbFile, smpServers} - cc <- atomically $ newChatController a ct $ tbqSize cfg - -- setLogLevel LogInfo -- LogError - -- withGlobalLogging logCfg $ do - runReaderT simplexChat cc - -welcomeGetOpts :: IO ChatOpts -welcomeGetOpts = do - appDir <- getAppUserDataDirectory "simplex" - opts@ChatOpts {dbFile} <- getChatOpts appDir - putStrLn "SimpleX chat prototype v0.3.1" - putStrLn $ "db: " <> dbFile - putStrLn "type \"/help\" or \"/h\" for usage info" - pure opts - -simplexChat :: (MonadUnliftIO m, MonadReader ChatController m) => m () -simplexChat = race_ runTerminalInput runChatController diff --git a/apps/dog-food/ChatOptions.hs b/apps/simplex-chat/ChatOptions.hs similarity index 100% rename from apps/dog-food/ChatOptions.hs rename to apps/simplex-chat/ChatOptions.hs diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index e6d0f30cdd..858ee83faf 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -1,45 +1,92 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Main where -import Control.Concurrent (threadDelay) -import Control.Concurrent.STM (readTVarIO, retry) -import Control.Monad (forever, void) -import Simplex.Demo (chatLayoutDemo) +import ChatOptions +import Control.Concurrent.STM +import Control.Logger.Simple +import Control.Monad.IO.Unlift +import Control.Monad.Reader +import Simplex.Chat +import Simplex.Chat.Controller +import Simplex.Input +import Simplex.Messaging.Agent (getSMPAgentClient) +import Simplex.Messaging.Agent.Env.SQLite +import Simplex.Messaging.Client (smpDefaultConfig) import Simplex.Store (createStore) -import System.IO (hFlush, stdout) -import System.Terminal (putStringLn, runTerminalT, withTerminal) -import qualified System.Terminal as C -import qualified System.Terminal.Internal as C +import Simplex.Terminal +import System.Directory (getAppUserDataDirectory) +import UnliftIO.Async (race_) -defaultSettings :: C.Size -> C.VirtualTerminalSettings -defaultSettings size = - C.VirtualTerminalSettings - { C.virtualType = "xterm", - C.virtualWindowSize = pure size, - C.virtualEvent = retry, - C.virtualInterrupt = retry +cfg :: AgentConfig +cfg = + AgentConfig + { tcpPort = undefined, -- agent does not listen to TCP + smpServers = undefined, -- filled in from options + rsaKeySize = 2048 `div` 8, + connIdBytes = 12, + tbqSize = 16, + dbFile = "smp-chat.db", + smpCfg = smpDefaultConfig } +logCfg :: LogConfig +logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} + main :: IO () main = do + ChatOpts {dbFile, smpServers} <- welcomeGetOpts void $ createStore "simplex-chat.db" 4 + ct <- newChatTerminal + a <- getSMPAgentClient cfg {dbFile, smpServers} + cc <- atomically $ newChatController a ct $ tbqSize cfg + -- setLogLevel LogInfo -- LogError + -- withGlobalLogging logCfg $ do + runReaderT simplexChat cc - hFlush stdout - -- ChatTerminal {termSize} <- newChatTerminal - -- pos <- C.withVirtualTerminal (defaultSettings termSize) $ - -- \t -> runTerminalT (C.setAlternateScreenBuffer True >> C.putString "a" >> C.flush >> C.getCursorPosition) t - -- print pos - -- race_ (printEvents t) (updateTerminal t) - void . withTerminal . runTerminalT $ chatLayoutDemo >> C.flush >> C.awaitEvent +welcomeGetOpts :: IO ChatOpts +welcomeGetOpts = do + appDir <- getAppUserDataDirectory "simplex" + opts@ChatOpts {dbFile} <- getChatOpts appDir + putStrLn "SimpleX chat prototype v0.3.1" + putStrLn $ "db: " <> dbFile + putStrLn "type \"/help\" or \"/h\" for usage info" + pure opts -printEvents :: C.VirtualTerminal -> IO () -printEvents t = forever $ do - event <- withTerminal . runTerminalT $ C.flush >> C.awaitEvent - runTerminalT (putStringLn $ show event) t +simplexChat :: (MonadUnliftIO m, MonadReader ChatController m) => m () +simplexChat = race_ runTerminalInput runChatController -updateTerminal :: C.VirtualTerminal -> IO () -updateTerminal t = forever $ do - threadDelay 10000 - win <- readTVarIO $ C.virtualWindow t - withTerminal . runTerminalT $ mapM_ C.putStringLn win >> C.flush +-- defaultSettings :: C.Size -> C.VirtualTerminalSettings +-- defaultSettings size = +-- C.VirtualTerminalSettings +-- { C.virtualType = "xterm", +-- C.virtualWindowSize = pure size, +-- C.virtualEvent = retry, +-- C.virtualInterrupt = retry +-- } + +-- main :: IO () +-- main = do +-- void $ createStore "simplex-chat.db" 4 + +-- hFlush stdout +-- -- ChatTerminal {termSize} <- newChatTerminal +-- -- pos <- C.withVirtualTerminal (defaultSettings termSize) $ +-- -- \t -> runTerminalT (C.setAlternateScreenBuffer True >> C.putString "a" >> C.flush >> C.getCursorPosition) t +-- -- print pos +-- -- race_ (printEvents t) (updateTerminal t) +-- void . withTerminal . runTerminalT $ chatLayoutDemo >> C.flush >> C.awaitEvent + +-- printEvents :: C.VirtualTerminal -> IO () +-- printEvents t = forever $ do +-- event <- withTerminal . runTerminalT $ C.flush >> C.awaitEvent +-- runTerminalT (putStringLn $ show event) t + +-- updateTerminal :: C.VirtualTerminal -> IO () +-- updateTerminal t = forever $ do +-- threadDelay 10000 +-- win <- readTVarIO $ C.virtualWindow t +-- withTerminal . runTerminalT $ mapM_ C.putStringLn win >> C.flush diff --git a/apps/dog-food/Simplex/Chat.hs b/apps/simplex-chat/Simplex/Chat.hs similarity index 100% rename from apps/dog-food/Simplex/Chat.hs rename to apps/simplex-chat/Simplex/Chat.hs diff --git a/apps/dog-food/Simplex/Chat/Controller.hs b/apps/simplex-chat/Simplex/Chat/Controller.hs similarity index 100% rename from apps/dog-food/Simplex/Chat/Controller.hs rename to apps/simplex-chat/Simplex/Chat/Controller.hs diff --git a/apps/dog-food/Simplex/Help.hs b/apps/simplex-chat/Simplex/Help.hs similarity index 100% rename from apps/dog-food/Simplex/Help.hs rename to apps/simplex-chat/Simplex/Help.hs diff --git a/apps/dog-food/Simplex/Input.hs b/apps/simplex-chat/Simplex/Input.hs similarity index 100% rename from apps/dog-food/Simplex/Input.hs rename to apps/simplex-chat/Simplex/Input.hs diff --git a/apps/dog-food/Simplex/Terminal.hs b/apps/simplex-chat/Simplex/Terminal.hs similarity index 100% rename from apps/dog-food/Simplex/Terminal.hs rename to apps/simplex-chat/Simplex/Terminal.hs diff --git a/apps/dog-food/Simplex/View.hs b/apps/simplex-chat/Simplex/View.hs similarity index 100% rename from apps/dog-food/Simplex/View.hs rename to apps/simplex-chat/Simplex/View.hs diff --git a/apps/dog-food/Types.hs b/apps/simplex-chat/Types.hs similarity index 100% rename from apps/dog-food/Types.hs rename to apps/simplex-chat/Types.hs diff --git a/package.yaml b/package.yaml index 6941856819..1084dac95a 100644 --- a/package.yaml +++ b/package.yaml @@ -24,28 +24,6 @@ library: source-dirs: src executables: - dog-food: - source-dirs: apps/dog-food - main: Main.hs - dependencies: - - simplex-chat - - async == 2.2.* - - bytestring == 0.10.* - - composition == 1.0.* - - directory == 1.3.* - - filepath == 1.4.* - - mtl == 2.2.* - - optparse-applicative == 0.15.* - - simple-logger == 0.1.* - - simplexmq == 0.3.* - - stm == 2.5.* - - terminal == 0.2.* - - time == 1.9.* - - unliftio == 0.2.* - - unliftio-core == 0.2.* - ghc-options: - - -threaded - simplex-chat: source-dirs: apps/simplex-chat main: Main.hs @@ -54,14 +32,17 @@ executables: - async == 2.2.* - bytestring == 0.10.* - composition == 1.0.* - - cryptonite == 0.27.* + - directory == 1.3.* - file-embed == 0.0.14.* - filepath == 1.4.* - mtl == 2.2.* + - optparse-applicative == 0.15.* + - simple-logger == 0.1.* - simplexmq == 0.3.* - sqlite-simple == 0.4.* - stm == 2.5.* - terminal == 0.2.* + - time == 1.9.* - unliftio == 0.2.* - unliftio-core == 0.2.* ghc-options: From 2f604d91ba97a5a120005464e479503060c64df7 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 4 Jul 2021 18:42:24 +0100 Subject: [PATCH 03/29] use chat protocol and contacts in chat commands/messages (#66) * chat types, chat protocol syntax idea * chat message syntax, raw message type * chat message format and parsing * raw chat message parsing test * add message parsing tests * interpret RawChatMessage * use chat message format when sending messages * save contacts and related connections to DB (WIP) * use contacts in all chat commands (add, connect, send, delete) * use contacts when receiving messages and notifications * handle contact not found error * automatically accept connection when CONF is received from the agent --- apps/simplex-chat/Main.hs | 7 +- apps/simplex-chat/Simplex/Chat.hs | 167 ++++++++--- apps/simplex-chat/Simplex/Chat/Controller.hs | 33 ++- apps/simplex-chat/Simplex/Input.hs | 7 +- apps/simplex-chat/Simplex/Notification.hs | 10 +- apps/simplex-chat/Simplex/Store.hs | 174 ++++++++++-- apps/simplex-chat/Simplex/Terminal.hs | 4 +- apps/simplex-chat/Simplex/View.hs | 80 +++--- apps/simplex-chat/Types.hs | 7 - migrations/20210612_initial.sql | 55 ++-- package.yaml | 3 + src/Simplex/Chat/Protocol.hs | 279 +++++++++++++++++-- src/Simplex/Chat/Protocol_.hs | 44 +++ src/Simplex/Chat/Types.hs | 80 ++++++ src/Simplex/Chat/protocol.md | 70 +++++ stack.yaml | 3 +- tests/ProtocolTests.hs | 57 ++++ tests/Test.hs | 2 + 18 files changed, 903 insertions(+), 179 deletions(-) delete mode 100644 apps/simplex-chat/Types.hs create mode 100644 src/Simplex/Chat/Protocol_.hs create mode 100644 src/Simplex/Chat/Types.hs create mode 100644 src/Simplex/Chat/protocol.md create mode 100644 tests/ProtocolTests.hs diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index 577edc9db7..041ba7d721 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -31,6 +31,7 @@ cfg = connIdBytes = 12, tbqSize = 16, dbFile = "smp-chat.db", + dbPoolSize = 4, smpCfg = smpDefaultConfig } @@ -40,11 +41,11 @@ logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} main :: IO () main = do ChatOpts {dbFile, smpServers} <- welcomeGetOpts - void $ createStore "simplex-chat.db" 4 + st <- createStore (dbFile <> ".chat.db") 4 ct <- newChatTerminal - a <- getSMPAgentClient cfg {dbFile, smpServers} + a <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} notify <- initializeNotifications - cc <- atomically $ newChatController a ct notify $ tbqSize cfg + cc <- atomically $ newChatController a ct st notify $ tbqSize cfg -- setLogLevel LogInfo -- LogError -- withGlobalLogging logCfg $ do runReaderT simplexChat cc diff --git a/apps/simplex-chat/Simplex/Chat.hs b/apps/simplex-chat/Simplex/Chat.hs index 57157e0d7d..fa7ebfc1fe 100644 --- a/apps/simplex-chat/Simplex/Chat.hs +++ b/apps/simplex-chat/Simplex/Chat.hs @@ -1,12 +1,15 @@ +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} module Simplex.Chat where -import Control.Applicative ((<|>)) +import Control.Applicative (optional, (<|>)) import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader @@ -14,29 +17,36 @@ import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (first) import Data.ByteString.Char8 (ByteString) +import qualified Data.ByteString.Char8 as B import Data.Functor (($>)) +import Data.List (find) +import Data.Maybe (fromMaybe) +import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Controller +import Simplex.Chat.Protocol import Simplex.Chat.Styled (plain) +import Simplex.Chat.Types import Simplex.Help import Simplex.Messaging.Agent import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Parsers (parseAll) -import Simplex.Messaging.Util (raceAny_) +import Simplex.Messaging.Util (bshow, raceAny_) import Simplex.Notification +import Simplex.Store import Simplex.Terminal import Simplex.View -import Types +import qualified UnliftIO.Exception as E import UnliftIO.STM data ChatCommand = ChatHelp | MarkdownHelp - | AddContact Contact - | Connect Contact SMPQueueInfo - | DeleteContact Contact - | SendMessage Contact ByteString + | AddContact (Maybe ContactRef) + | Connect (Maybe ContactRef) SMPQueueInfo + | DeleteContact ContactRef + | SendMessage ContactRef ByteString deriving (Show) runChatController :: (MonadUnliftIO m, MonadReader ChatController m) => m () @@ -44,6 +54,7 @@ runChatController = raceAny_ [ inputSubscriber, agentSubscriber, + chatSubscriber, notificationSubscriber ] @@ -61,66 +72,136 @@ inputSubscriber = do SendMessage c msg -> showSentMessage c msg _ -> printToView [plain s] runExceptT (processChatCommand cmd) >>= \case - Left (ChatErrorAgent c e) -> showAgentError c e + Left e -> showChatError e _ -> pure () processChatCommand :: ChatMonad m => ChatCommand -> m () processChatCommand = \case ChatHelp -> printToView chatHelpInfo MarkdownHelp -> printToView markdownInfo - AddContact c -> do - (_, qInfo) <- withAgent c (`createConnection` Just (fromContact c)) - showInvitation c qInfo - Connect c qInfo -> - void . withAgent c $ \smp -> joinConnection smp (Just $ fromContact c) qInfo - DeleteContact c -> do - withAgent c (`deleteConnection` fromContact c) - showContactDeleted c - unsetActive' $ ActiveC c - SendMessage c msg -> do - void . withAgent c $ \smp -> sendMessage smp (fromContact c) msg - setActive' $ ActiveC c + AddContact cRef -> do + (connId, qInfo) <- withAgent (fromMaybe "" cRef) createConnection + userId <- asks currentUserId + contact <- withStore $ \st -> createDirectContact st userId connId cRef + showInvitation (localContactRef contact) qInfo + Connect cRef qInfo -> do + userId <- asks currentUserId + connId <- withAgent (fromMaybe "" cRef) $ \agent -> joinConnection agent qInfo "user profile here" + void $ withStore $ \st -> createDirectContact st userId connId cRef + DeleteContact cRef -> do + userId <- asks currentUserId + conns <- withStore $ \st -> getContactConnections st userId cRef + withAgent cRef $ \smp -> forM_ conns $ \Connection {agentConnId} -> + deleteConnection smp agentConnId `catchError` \(_ :: AgentErrorType) -> pure () + void $ withStore $ \st -> deleteContact st userId cRef + unsetActive $ ActiveC cRef + when (null conns) . throwError . ChatErrorContact $ CENotFound cRef + showContactDeleted cRef + SendMessage cRef msg -> do + userId <- asks currentUserId + Connection {agentConnId} <- withStore $ \st -> getContactConnection st userId cRef + let body = MsgBodyContent {contentType = SimplexContentType XCText, contentHash = Nothing, contentData = MBFull $ MsgData msg} + rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent = XMsgNew MTText, chatMsgBody = [body], chatDAGIdx = Nothing} + void . withAgent cRef $ \smp -> sendMessage smp agentConnId $ serializeRawChatMessage rawMsg + setActive $ ActiveC cRef agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do - q <- asks $ subQ . smpAgent - nQ <- asks notifyQ + aQ <- asks $ subQ . smpAgent + cQ <- asks chatQ forever $ do - (_, a, resp) <- atomically (readTBQueue q) - let notify = \text -> atomically $ writeTBQueue nQ Notification {title = "@" <> a, text} - case resp of - CON -> do - showContactConnected $ Contact a - setActive' $ ActiveC $ Contact a - END -> do - showContactDisconnected $ Contact a - notify "disconnected" - unsetActive' $ ActiveC $ Contact a - MSG {brokerMeta, msgBody, msgIntegrity} -> do - -- ReceivedMessage contact (snd brokerMeta) msgBody msgIntegrity - showReceivedMessage (Contact a) (snd brokerMeta) msgBody msgIntegrity - notify msgBody - setActive' $ ActiveC $ Contact a + (_, agentConnId, resp) <- atomically (readTBQueue aQ) + userId <- asks currentUserId + runExceptT (withStore $ \st -> getConnectionChatDirection st userId agentConnId) >>= \case + -- TODO handle errors + Left e -> liftIO $ print e + Right chatDirection -> do + case resp of + MSG agentMsgMeta msgBody -> do + atomically . writeTBQueue cQ $ + case first B.pack (parseAll rawChatMessageP msgBody) >>= toChatMessage of + Right chatMessage -> ChatTransmission {agentMsgMeta, chatDirection, chatMessage} + Left msgError -> ChatTransmissionError {agentMsgMeta, chatDirection, msgBody, msgError} + agentMessage -> + atomically $ writeTBQueue cQ AgentTransmission {agentConnId, chatDirection, agentMessage} + +chatSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () +chatSubscriber = do + cQ <- asks chatQ + forever $ + atomically (readTBQueue cQ) >>= \case + ChatTransmission + { agentMsgMeta = meta, + chatDirection = ReceivedDirectMessage Contact {localContactRef = c}, + chatMessage = ChatMessage {chatMsgEvent, chatMsgBody} + } -> + case chatMsgEvent of + XMsgNew MTText -> do + case find (isSimplexContentType XCText) chatMsgBody of + Just MsgBodyContent {contentData = MBFull (MsgData bs)} -> do + let text = safeDecodeUtf8 bs + showReceivedMessage c (snd $ broker meta) text (integrity meta) + showToast ("@" <> c) text + setActive $ ActiveC c + _ -> pure () + _ -> pure () + AgentTransmission {agentConnId, chatDirection = ReceivedDirectMessage Contact {localContactRef = c}, agentMessage} -> + case agentMessage of + CONF confId _ -> + -- TODO save profile? Show confirmation? + void . runExceptT . withAgent c $ \a -> allowConnection a agentConnId confId "user profile here" + CON -> do + showContactConnected c + showToast ("@" <> c) "connected" + setActive $ ActiveC c + END -> do + showContactDisconnected c + showToast ("@" <> c) "disconnected" + unsetActive $ ActiveC c + _ -> pure () _ -> pure () +showToast :: (MonadUnliftIO m, MonadReader ChatController m) => Text -> Text -> m () +showToast title text = atomically . (`writeTBQueue` Notification {title, text}) =<< asks notifyQ + notificationSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () notificationSubscriber = do ChatController {notifyQ, sendNotification} <- ask forever $ atomically (readTBQueue notifyQ) >>= liftIO . sendNotification -withAgent :: ChatMonad m => Contact -> (AgentClient -> ExceptT AgentErrorType m a) -> m a +withAgent :: ChatMonad m => ContactRef -> (AgentClient -> ExceptT AgentErrorType m a) -> m a withAgent c action = asks smpAgent >>= runExceptT . action >>= liftEither . first (ChatErrorAgent c) +withStore :: + ChatMonad m => + (forall m'. (MonadUnliftIO m', MonadError StoreError m') => SQLiteStore -> m' a) -> + m a +withStore action = do + st <- asks chatStore + runExceptT (action st `E.catch` handleInternal) >>= \case + Right c -> pure c + Left e -> throwError $ storeError e + where + -- TODO when parsing exception happens in store, the agent hangs; + -- changing SQLError to SomeException does not help + handleInternal :: (MonadError StoreError m') => E.SomeException -> m' a + handleInternal e = throwError . SEInternal $ bshow e + storeError :: StoreError -> ChatError + storeError = \case + SEContactNotFound c -> ChatErrorContact $ CENotFound c + e -> ChatErrorStore e + chatCommandP :: Parser ChatCommand chatCommandP = ("/help" <|> "/h") $> ChatHelp - <|> ("/add " <|> "/a ") *> (AddContact <$> contact) - <|> ("/connect " <|> "/c ") *> (Connect <$> contact <* A.space <*> smpQueueInfoP) - <|> ("/delete " <|> "/d ") *> (DeleteContact <$> contact) - <|> A.char '@' *> (SendMessage <$> contact <* A.space <*> A.takeByteString) + <|> ("/add" <|> "/a") *> (AddContact <$> optional (A.space *> contactRef)) + <|> ("/connect" <|> "/c") *> ((Connect <$> optional (A.space *> contactRef) <*> qInfo) <|> (Connect Nothing <$> qInfo)) + <|> ("/delete " <|> "/d ") *> (DeleteContact <$> contactRef) + <|> A.char '@' *> (SendMessage <$> contactRef <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp where - contact = Contact <$> A.takeTill (== ' ') + contactRef = safeDecodeUtf8 <$> A.takeTill (== ' ') + qInfo = A.space *> smpQueueInfoP diff --git a/apps/simplex-chat/Simplex/Chat/Controller.hs b/apps/simplex-chat/Simplex/Chat/Controller.hs index 79e6f08f0b..43d9e5e780 100644 --- a/apps/simplex-chat/Simplex/Chat/Controller.hs +++ b/apps/simplex-chat/Simplex/Chat/Controller.hs @@ -10,16 +10,22 @@ import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader import Numeric.Natural +import Simplex.Chat.Protocol +import Simplex.Chat.Types import Simplex.Messaging.Agent (AgentClient) import Simplex.Messaging.Agent.Protocol (AgentErrorType) +import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) import Simplex.Notification +import Simplex.Store (StoreError) import Simplex.Terminal -import Types import UnliftIO.STM data ChatController = ChatController - { smpAgent :: AgentClient, + { currentUserId :: UserId, + smpAgent :: AgentClient, chatTerminal :: ChatTerminal, + chatStore :: SQLiteStore, + chatQ :: TBQueue ChatTransmission, inputQ :: TBQueue InputEvent, notifyQ :: TBQueue Notification, sendNotification :: Notification -> IO () @@ -27,21 +33,28 @@ data ChatController = ChatController data InputEvent = InputCommand String | InputControl Char -data ChatError = ChatErrorAgent Contact AgentErrorType +data ChatError + = ChatErrorContact ContactError + | ChatErrorAgent ContactRef AgentErrorType + | ChatErrorStore StoreError + deriving (Show, Exception) + +newtype ContactError = CENotFound ContactRef deriving (Show, Exception) type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) -newChatController :: AgentClient -> ChatTerminal -> (Notification -> IO ()) -> Natural -> STM ChatController -newChatController smpAgent chatTerminal sendNotification qSize = do +newChatController :: AgentClient -> ChatTerminal -> SQLiteStore -> (Notification -> IO ()) -> Natural -> STM ChatController +newChatController smpAgent chatTerminal chatStore sendNotification qSize = do inputQ <- newTBQueue qSize notifyQ <- newTBQueue qSize - pure ChatController {smpAgent, chatTerminal, inputQ, notifyQ, sendNotification} + chatQ <- newTBQueue qSize + pure ChatController {currentUserId = 1, smpAgent, chatTerminal, chatStore, chatQ, inputQ, notifyQ, sendNotification} -setActive' :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () -setActive' to = asks (activeTo . chatTerminal) >>= atomically . (`writeTVar` to) +setActive :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () +setActive to = asks (activeTo . chatTerminal) >>= atomically . (`writeTVar` to) -unsetActive' :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () -unsetActive' a = asks (activeTo . chatTerminal) >>= atomically . (`modifyTVar` unset) +unsetActive :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () +unsetActive a = asks (activeTo . chatTerminal) >>= atomically . (`modifyTVar` unset) where unset a' = if a == a' then ActiveNone else a' diff --git a/apps/simplex-chat/Simplex/Input.hs b/apps/simplex-chat/Simplex/Input.hs index 494ba412d6..9984597179 100644 --- a/apps/simplex-chat/Simplex/Input.hs +++ b/apps/simplex-chat/Simplex/Input.hs @@ -6,13 +6,12 @@ module Simplex.Input where import Control.Monad.IO.Unlift import Control.Monad.Reader -import qualified Data.ByteString.Char8 as B import Data.List (dropWhileEnd) +import qualified Data.Text as T import Simplex.Chat.Controller import Simplex.Terminal import System.Exit (exitSuccess) import System.Terminal hiding (insertChars) -import Types import UnliftIO.STM getKey :: MonadTerminal m => m (Key, Modifiers) @@ -80,8 +79,8 @@ updateTermState ac tw (key, ms) ts@TerminalState {inputString = s, inputPosition insert cs = let (b, a) = splitAt p s in (b <> cs <> a, p + length cs) contactPrefix = case ac of ActiveNone -> "" - ActiveC (Contact c) -> "@" <> B.unpack c <> " " - ActiveG (Group g) -> "#" <> B.unpack g <> " " + ActiveC c -> "@" <> T.unpack c <> " " + -- ActiveG (Group g) -> "#" <> B.unpack g <> " " backDeleteChar | p == 0 || null s = ts | p >= length s = ts' (init s, length s - 1) diff --git a/apps/simplex-chat/Simplex/Notification.hs b/apps/simplex-chat/Simplex/Notification.hs index 7c1f3966a6..6d96eb0d89 100644 --- a/apps/simplex-chat/Simplex/Notification.hs +++ b/apps/simplex-chat/Simplex/Notification.hs @@ -5,18 +5,16 @@ module Simplex.Notification (Notification (..), initializeNotifications) where import Control.Monad (void) -import Data.ByteString.Char8 (ByteString) import Data.Char (toLower) import Data.List (isInfixOf) import Data.Text (Text) import qualified Data.Text as T -import Simplex.Util (safeDecodeUtf8) import System.Directory (doesFileExist, getAppUserDataDirectory) import System.FilePath (combine) import System.Info (os) import System.Process (readCreateProcess, shell) -data Notification = Notification {title :: ByteString, text :: ByteString} +data Notification = Notification {title :: Text, text :: Text} initializeNotifications :: IO (Notification -> IO ()) initializeNotifications = case os of @@ -37,16 +35,16 @@ notify script notification = void $ readCreateProcess (shell . T.unpack $ script notification) "" linuxScript :: Notification -> Text -linuxScript Notification {title, text} = "notify-send \"" <> safeDecodeUtf8 title <> "\" \"" <> safeDecodeUtf8 text <> "\"" +linuxScript Notification {title, text} = "notify-send \"" <> title <> "\" \"" <> text <> "\"" macScript :: Notification -> Text -macScript Notification {title, text} = "osascript -e 'display notification \"" <> safeDecodeUtf8 text <> "\" with title \"" <> safeDecodeUtf8 title <> "\"'" +macScript Notification {title, text} = "osascript -e 'display notification \"" <> text <> "\" with title \"" <> title <> "\"'" initWinNotify :: IO (Notification -> IO ()) initWinNotify = notify . winScript <$> savePowershellScript winScript :: FilePath -> Notification -> Text -winScript path Notification {title, text} = "powershell.exe \"" <> T.pack path <> " \'" <> safeDecodeUtf8 title <> "\' \'" <> safeDecodeUtf8 text <> "\'\"" +winScript path Notification {title, text} = "powershell.exe \"" <> T.pack path <> " \'" <> title <> "\' \'" <> text <> "\'\"" savePowershellScript :: IO FilePath savePowershellScript = do diff --git a/apps/simplex-chat/Simplex/Store.hs b/apps/simplex-chat/Simplex/Store.hs index 4d2638d252..37a54c85ae 100644 --- a/apps/simplex-chat/Simplex/Store.hs +++ b/apps/simplex-chat/Simplex/Store.hs @@ -1,19 +1,46 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} -module Simplex.Store where +module Simplex.Store + ( SQLiteStore, + StoreError (..), + createStore, + createDirectContact, + deleteContact, + getContactConnection, + getContactConnections, + getConnectionChatDirection, + ) +where -import Control.Concurrent.STM -import Control.Monad (replicateM_) +import Control.Exception +import Control.Monad.Except +import Control.Monad.IO.Unlift +import Data.ByteString.Char8 (ByteString) import Data.FileEmbed (embedDir, makeRelativeToProject) import Data.Function (on) +import Data.Int (Int64) import Data.List (sortBy) +import Data.Maybe (fromMaybe) +import Data.Text (Text) import Data.Text.Encoding (decodeUtf8) +import Database.SQLite.Simple (NamedParam (..), Only (..)) import qualified Database.SQLite.Simple as DB -import Numeric.Natural (Natural) -import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), connectSQLiteStore, createSQLiteStore) +import Database.SQLite.Simple.QQ (sql) +import Simplex.Chat.Protocol +import Simplex.Chat.Types +import Simplex.Messaging.Agent.Protocol (AParty (..), ConnId) +import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore, withTransaction) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) +import Simplex.Messaging.Util (liftIOEither) import System.FilePath (takeBaseName, takeExtension) -- | The list of migrations in ascending order by date @@ -25,17 +52,128 @@ migrations = sqlFile (file, _) = takeExtension file == ".sql" migration (file, qStr) = Migration {name = takeBaseName file, up = decodeUtf8 qStr} -data SQLitePool = SQLitePool - { dbFilePath :: FilePath, - dbPool :: TBQueue DB.Connection, - dbNew :: Bool - } +createStore :: FilePath -> Int -> IO SQLiteStore +createStore dbFilePath poolSize = createSQLiteStore dbFilePath poolSize migrations -createStore :: FilePath -> Natural -> IO SQLitePool -createStore dbFilePath poolSize = do - SQLiteStore {dbConn = c, dbNew} <- createSQLiteStore dbFilePath migrations - dbPool <- newTBQueueIO poolSize - atomically $ writeTBQueue dbPool c - replicateM_ (fromInteger $ toInteger $ poolSize - 1) $ - connectSQLiteStore dbFilePath >>= atomically . writeTBQueue dbPool . dbConn - pure SQLitePool {dbFilePath, dbPool, dbNew} +insertedRowId :: DB.Connection -> IO Int64 +insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid();" + +createDirectContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> Maybe Text -> m Contact +createDirectContact st userId agentConnId contactRef = + liftIO . withTransaction st $ \db -> do + DB.execute db "INSERT INTO connections (user_id, agent_conn_id, conn_status) VALUES (?,?,?);" (userId, agentConnId, ConnNew) + connId <- insertedRowId db + let activeConn = Connection {connId, agentConnId, connLevel = 0, viaContact = Nothing, connStatus = ConnNew} + -- TODO support undefined localContactRef (Nothing) - currently it would fail + let localContactRef = fromMaybe "" contactRef + DB.execute db "INSERT INTO contacts (user_id, local_contact_ref) VALUES (?,?);" (userId, localContactRef) + contactId <- insertedRowId db + DB.execute db "INSERT INTO contact_connections (connection_id, contact_id, active) VALUES (?,?,1);" (connId, contactId) + pure Contact {contactId, localContactRef, profile = Nothing, activeConn} + +deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactRef -> m () +deleteContact st userId contactRef = + liftIO . withTransaction st $ \db -> + forM_ + [ [sql| + DELETE FROM connections + WHERE user_id = :user_id AND connection_id IN ( + SELECT cc.connection_id + FROM contact_connections AS cc + JOIN contacts AS cs ON cs.contact_id = cc.contact_id + WHERE local_contact_ref = :contact_ref + ); + |], + [sql| + DELETE FROM contacts + WHERE user_id = :user_id AND local_contact_ref = :contact_ref; + |] + ] + $ \q -> DB.executeNamed db q [":user_id" := userId, ":contact_ref" := contactRef] + +getContactConnection :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m Connection +getContactConnection st userId contactRef = + liftIOEither . withTransaction st $ \db -> + connection + <$> DB.queryNamed + db + [sql| + SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status + FROM connections AS c + JOIN contact_connections AS cc ON cc.connection_id == c.connection_id + JOIN contacts AS cs ON cc.contact_id == cs.contact_id + WHERE c.user_id = :user_id + AND cs.user_id = :user_id + AND cs.local_contact_ref == :contact_ref + AND cc.active == 1; + |] + [":user_id" := userId, ":contact_ref" := contactRef] + where + connection (connRow : _) = Right $ toConnection connRow + connection _ = Left $ SEContactNotFound contactRef + +getContactConnections :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactRef -> m [Connection] +getContactConnections st userId contactRef = + liftIO . withTransaction st $ \db -> + map toConnection + <$> DB.queryNamed + db + [sql| + SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status + FROM connections AS c + JOIN contact_connections AS cc ON cc.connection_id == c.connection_id + JOIN contacts AS cs ON cc.contact_id == cs.contact_id + WHERE c.user_id = :user_id + AND cs.user_id = :user_id + AND cs.local_contact_ref == :contact_ref + AND cc.active == 1; + |] + [":user_id" := userId, ":contact_ref" := contactRef] + +toConnection :: (Int64, ConnId, Int, Maybe Int64, ConnStatus) -> Connection +toConnection (connId, agentConnId, connLevel, viaContact, connStatus) = + Connection {connId, agentConnId, connLevel, viaContact, connStatus} + +getConnectionChatDirection :: + (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ConnId -> m (ChatDirection 'Agent) +getConnectionChatDirection st userId agentConnId = + liftIOEither . withTransaction st $ \db -> + chatDirection + <$> DB.queryNamed + db + [sql| + SELECT + cs.contact_id, cs.local_contact_ref, + a.connection_id, a.conn_level, a.via_contact, a.conn_status, + p.contact_profile_id, p.contact_ref, p.display_name + FROM contacts AS cs + JOIN contact_connections AS cc ON cs.contact_id = cc.contact_id + JOIN contact_connections AS ac ON cs.contact_id = ac.contact_id + JOIN connections AS c ON c.connection_id = cc.connection_id + JOIN connections AS a ON a.connection_id = ac.connection_id + LEFT JOIN contact_profiles AS p ON p.contact_profile_id = cs.contact_profile_id + WHERE cs.user_id = :user_id + AND c.agent_conn_id = :agent_conn_id + AND ac.active = 1 + |] + [":user_id" := userId, ":agent_conn_id" := agentConnId] + where + chatDirection :: [ChatDirRow] -> Either StoreError (ChatDirection 'Agent) + chatDirection [d] = Right $ toChatDirection agentConnId d + chatDirection _ = Left SEConnectionNotFound + +type ChatDirRow = (Int64, Text, Int64, Int, Maybe Int64, ConnStatus, Maybe Int64, Maybe ContactRef, Maybe Text) + +toChatDirection :: ConnId -> ChatDirRow -> ChatDirection 'Agent +toChatDirection + agentConnId + (contactId, localContactRef, connId, connLevel, viaContact, connStatus, profileId, contactRef, displayName) = + let profile = Profile <$> profileId <*> contactRef <*> displayName + activeConn = Connection {connId, agentConnId, connLevel, viaContact, connStatus} + in ReceivedDirectMessage $ Contact {contactId, localContactRef, profile, activeConn} + +data StoreError + = SEContactNotFound ContactRef + | SEConnectionNotFound + | SEInternal ByteString + deriving (Show, Exception) diff --git a/apps/simplex-chat/Simplex/Terminal.hs b/apps/simplex-chat/Simplex/Terminal.hs index e9cd618aa3..b8e77ea9c7 100644 --- a/apps/simplex-chat/Simplex/Terminal.hs +++ b/apps/simplex-chat/Simplex/Terminal.hs @@ -5,12 +5,12 @@ module Simplex.Terminal where import Simplex.Chat.Styled +import Simplex.Chat.Types import System.Console.ANSI.Types import System.Terminal -import Types import UnliftIO.STM -data ActiveTo = ActiveNone | ActiveC Contact | ActiveG Group +data ActiveTo = ActiveNone | ActiveC ContactRef deriving (Eq) data ChatTerminal = ChatTerminal diff --git a/apps/simplex-chat/Simplex/View.hs b/apps/simplex-chat/Simplex/View.hs index 1e701c7bd8..ab3c980de1 100644 --- a/apps/simplex-chat/Simplex/View.hs +++ b/apps/simplex-chat/Simplex/View.hs @@ -6,16 +6,12 @@ module Simplex.View ( printToView, showInvitation, - showAgentError, + showChatError, showContactDeleted, showContactConnected, showContactDisconnected, showReceivedMessage, showSentMessage, - ttyContact, - ttyFromContact, - ttyGroup, - ttyFromGroup, safeDecodeUtf8, ) where @@ -24,6 +20,7 @@ import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.ByteString.Char8 (ByteString) import Data.Composition ((.:)) +import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (DiffTime, UTCTime) import Data.Time.Format (defaultTimeLocale, formatTime) @@ -31,36 +28,36 @@ import Data.Time.LocalTime (TimeZone, ZonedTime, getCurrentTimeZone, getZonedTim import Simplex.Chat.Controller import Simplex.Chat.Markdown import Simplex.Chat.Styled +import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol import Simplex.Terminal (printToTerminal) import Simplex.Util (safeDecodeUtf8) import System.Console.ANSI.Types -import Types type ChatReader m = (MonadUnliftIO m, MonadReader ChatController m) -showInvitation :: ChatReader m => Contact -> SMPQueueInfo -> m () +showInvitation :: ChatReader m => ContactRef -> SMPQueueInfo -> m () showInvitation = printToView .: invitation -showAgentError :: ChatReader m => Contact -> AgentErrorType -> m () -showAgentError = printToView .: agentError +showChatError :: ChatReader m => ChatError -> m () +showChatError = printToView . chatError -showContactDeleted :: ChatReader m => Contact -> m () +showContactDeleted :: ChatReader m => ContactRef -> m () showContactDeleted = printToView . contactDeleted -showContactConnected :: ChatReader m => Contact -> m () +showContactConnected :: ChatReader m => ContactRef -> m () showContactConnected = printToView . contactConnected -showContactDisconnected :: ChatReader m => Contact -> m () +showContactDisconnected :: ChatReader m => ContactRef -> m () showContactDisconnected = printToView . contactDisconnected -showReceivedMessage :: ChatReader m => Contact -> UTCTime -> ByteString -> MsgIntegrity -> m () +showReceivedMessage :: ChatReader m => ContactRef -> UTCTime -> Text -> MsgIntegrity -> m () showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage c utcTime msg mOk) -showSentMessage :: ChatReader m => Contact -> ByteString -> m () +showSentMessage :: ChatReader m => ContactRef -> ByteString -> m () showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) -invitation :: Contact -> SMPQueueInfo -> [StyledString] +invitation :: ContactRef -> SMPQueueInfo -> [StyledString] invitation c qInfo = [ "pass this invitation to your contact " <> ttyContact c <> " (via any channel): ", "", @@ -69,16 +66,16 @@ invitation c qInfo = "and ask them to connect: /c " ] -contactDeleted :: Contact -> [StyledString] +contactDeleted :: ContactRef -> [StyledString] contactDeleted c = [ttyContact c <> " is deleted"] -contactConnected :: Contact -> [StyledString] +contactConnected :: ContactRef -> [StyledString] contactConnected c = [ttyContact c <> " is connected"] -contactDisconnected :: Contact -> [StyledString] +contactDisconnected :: ContactRef -> [StyledString] contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] -receivedMessage :: Contact -> UTCTime -> ByteString -> MsgIntegrity -> IO [StyledString] +receivedMessage :: ContactRef -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage c utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime pure $ prependFirst (t <> " " <> ttyFromContact c) (msgPlain msg) ++ showIntegrity mOk @@ -104,43 +101,48 @@ receivedMessage c utcTime msg mOk = do msgError :: String -> [StyledString] msgError s = [styled (Colored Red) s] -sentMessage :: Contact -> ByteString -> IO [StyledString] +sentMessage :: ContactRef -> ByteString -> IO [StyledString] sentMessage c msg = do time <- formatTime defaultTimeLocale "%H:%M" <$> getZonedTime - pure $ prependFirst (styleTime time <> " " <> ttyToContact c) (msgPlain msg) + pure $ prependFirst (styleTime time <> " " <> ttyToContact c) (msgPlain $ safeDecodeUtf8 msg) prependFirst :: StyledString -> [StyledString] -> [StyledString] prependFirst s [] = [s] prependFirst s (s' : ss) = (s <> s') : ss -msgPlain :: ByteString -> [StyledString] -msgPlain = map styleMarkdownText . T.lines . safeDecodeUtf8 +msgPlain :: Text -> [StyledString] +msgPlain = map styleMarkdownText . T.lines -agentError :: Contact -> AgentErrorType -> [StyledString] -agentError c = \case - CONN e -> case e of - NOT_FOUND -> ["no contact " <> ttyContact c] - DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] - SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] +chatError :: ChatError -> [StyledString] +chatError = \case + ChatErrorContact e -> case e of + CENotFound c -> ["no contact " <> ttyContact c] + ChatErrorAgent c err -> case err of + CONN e -> case e of + -- TODO replace with ChatErrorContact errors, these errors should never happen + NOT_FOUND -> ["no contact " <> ttyContact c] + DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] + SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] + e -> ["smp agent error: " <> plain (show e)] e -> ["chat error: " <> plain (show e)] printToView :: (MonadUnliftIO m, MonadReader ChatController m) => [StyledString] -> m () printToView s = asks chatTerminal >>= liftIO . (`printToTerminal` s) -ttyContact :: Contact -> StyledString -ttyContact (Contact a) = styled (Colored Green) a +ttyContact :: ContactRef -> StyledString +ttyContact = styled (Colored Green) -ttyToContact :: Contact -> StyledString -ttyToContact (Contact a) = styled (Colored Cyan) $ a <> " " +ttyToContact :: ContactRef -> StyledString +ttyToContact c = styled (Colored Cyan) $ c <> " " -ttyFromContact :: Contact -> StyledString -ttyFromContact (Contact a) = styled (Colored Yellow) $ a <> "> " +ttyFromContact :: ContactRef -> StyledString +ttyFromContact c = styled (Colored Yellow) $ c <> "> " -ttyGroup :: Group -> StyledString -ttyGroup (Group g) = styled (Colored Blue) $ "#" <> g +-- ttyGroup :: Group -> StyledString +-- ttyGroup (Group g) = styled (Colored Blue) $ "#" <> g -ttyFromGroup :: Group -> Contact -> StyledString -ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " +-- ttyFromGroup :: Group -> Contact -> StyledString +-- ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " styleTime :: String -> StyledString styleTime = Styled [SetColor Foreground Vivid Black] diff --git a/apps/simplex-chat/Types.hs b/apps/simplex-chat/Types.hs deleted file mode 100644 index 4dc0314325..0000000000 --- a/apps/simplex-chat/Types.hs +++ /dev/null @@ -1,7 +0,0 @@ -module Types where - -import Data.ByteString.Char8 (ByteString) - -newtype Contact = Contact {fromContact :: ByteString} deriving (Eq, Show) - -newtype Group = Group {fromGroup :: ByteString} deriving (Eq, Show) diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index 4c8ac3a254..22fcae5b22 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -1,6 +1,7 @@ CREATE TABLE contact_profiles ( -- remote user profile contact_profile_id INTEGER PRIMARY KEY, contact_ref TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces + display_name TEXT NOT NULL DEFAULT '', properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties ); @@ -21,7 +22,7 @@ CREATE TABLE known_servers( host TEXT NOT NULL, port TEXT NOT NULL, key_hash BLOB, - user_id INTEGER NOT NULL REFERENCES user_id, + user_id INTEGER NOT NULL REFERENCES users, UNIQUE (user_id, host, port) ) WITHOUT ROWID; @@ -30,22 +31,22 @@ CREATE TABLE contacts ( local_contact_ref TEXT NOT NULL UNIQUE, -- contact name set by local user - must be unique local_properties TEXT NOT NULL DEFAULT '{}', -- JSON set by local user contact_profile_id INTEGER UNIQUE REFERENCES contact_profiles, -- profile sent by remote contact, NULL for incognito contacts - contact_status TEXT NOT NULL DEFAULT '', - user_id INTEGER NOT NULL REFERENCES user_id + user_id INTEGER NOT NULL REFERENCES users ); CREATE TABLE connections ( -- all SMP agent connections connection_id INTEGER PRIMARY KEY, agent_conn_id BLOB NOT NULL UNIQUE, conn_level INTEGER NOT NULL DEFAULT 0, - via_conn BLOB REFERENCES contact_connections (connection_id), - conn_status TEXT NOT NULL DEFAULT '', - user_id INTEGER NOT NULL REFERENCES user_id + via_contact INTEGER REFERENCES contacts (contact_id), + conn_status TEXT NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ); CREATE TABLE contact_connections ( -- connections only for direct messages, many per contact connection_id INTEGER NOT NULL UNIQUE REFERENCES connections ON DELETE CASCADE, - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT -- connection must be removed first via the agent + contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, -- connection must be removed first via the agent + active INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE contact_invitations ( @@ -59,17 +60,21 @@ CREATE TABLE contact_invitations ( CREATE TABLE group_profiles ( -- shared group profiles group_profile_id INTEGER PRIMARY KEY, group_ref TEXT NOT NULL, -- this name must not contain spaces + display_name TEXT NOT NULL DEFAULT '', properties TEXT NOT NULL DEFAULT '{}' -- JSON with user or contact profile ); CREATE TABLE groups ( group_id INTEGER PRIMARY KEY, -- local group ID + invited_by INTEGER REFERENCES contacts ON DELETE RESTRICT, + external_group_id BLOB NOT NULL, local_group_ref TEXT NOT NULL UNIQUE, -- local group name without spaces local_properties TEXT NOT NULL, -- local JSON group properties group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile user_group_member_details_id INTEGER NOT NULL REFERENCES group_member_details (group_member_details_id) ON DELETE RESTRICT, - user_id INTEGER NOT NULL REFERENCES user_id + user_id INTEGER NOT NULL REFERENCES users, + UNIQUE (invited_by, external_group_id) ); CREATE TABLE group_members ( -- group members, excluding the local user @@ -97,6 +102,8 @@ CREATE TABLE events ( -- messages received by the agent, append only agent_meta TEXT NOT NULL, -- JSON with timestamps etc. sent in MSG connection_id INTEGER NOT NULL REFERENCES connections, received INTEGER NOT NULL, -- 0 for received, 1 for sent + chat_event_id INTEGER, + continuation_of INTEGER, -- references chat_event_id, but can be incorrect event_type TEXT NOT NULL, -- event type - see protocol/types.ts event_encoding INTEGER NOT NULL, -- format of event_body: 0 - binary, 1 - text utf8, 2 - JSON (utf8) content_type TEXT NOT NULL, -- content type - see protocol/types.ts @@ -108,6 +115,15 @@ CREATE TABLE events ( -- messages received by the agent, append only CREATE INDEX events_external_msg_id_index ON events (connection_id, external_msg_id); +CREATE TABLE event_body_parts ( + event_body_part_id INTEGER PRIMARY KEY, + event_id REFERENCES events, + full_size INTEGER NOT NULL, + part_status TEXT, -- full, partial + content_type TEXT NOT NULL, + event_part BLOB +); + CREATE TABLE contact_profile_events ( event_id INTEGER NOT NULL UNIQUE REFERENCES events, contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles @@ -130,17 +146,12 @@ CREATE TABLE group_event_parents ( parent_group_member_id INTEGER REFERENCES group_members (group_member_id), -- can be NULL if parent_member_id is incorrect parent_member_id BLOB, -- shared member ID, unique per group parent_event_id INTEGER REFERENCES events (event_id) ON DELETE CASCADE, -- this can be NULL if received event references another event that's not received yet - parent_external_msg_id INTEGER NOT NULL, + parent_chat_event_id INTEGER NOT NULL, parent_event_hash BLOB NOT NULL ); -CREATE INDEX group_event_parents_parent_external_msg_id_index - ON group_event_parents (parent_member_id, parent_external_msg_id); - -CREATE TABLE blobs ( - blob_id INTEGER PRIMARY KEY, - content BLOB NOT NULL -); +CREATE INDEX group_event_parents_parent_chat_event_id_index + ON group_event_parents (parent_member_id, parent_chat_event_id); CREATE TABLE messages ( -- mutable messages presented to user message_id INTEGER PRIMARY KEY, @@ -150,8 +161,16 @@ CREATE TABLE messages ( -- mutable messages presented to user msg_type TEXT NOT NULL, content_type TEXT NOT NULL, msg_text TEXT NOT NULL, -- textual representation - msg_props TEXT NOT NULL, -- JSON - msg_blob_id INTEGER REFERENCES blobs (blob_id) ON DELETE RESTRICT -- optional binary content + msg_props TEXT NOT NULL -- JSON +); + +CREATE TABLE message_content ( + message_content_id INTEGER PRIMARY KEY, + message_id INTEGER REFERENCES messages ON DELETE CASCADE, + content_type TEXT NOT NULL, + content_size INTEGER, -- full expected content size + content_status TEXT, -- empty, part, full + content BLOB NOT NULL ); CREATE TABLE message_events ( diff --git a/package.yaml b/package.yaml index d27fa5f324..e5991df418 100644 --- a/package.yaml +++ b/package.yaml @@ -17,7 +17,9 @@ dependencies: - base >= 4.7 && < 5 - bytestring == 0.10.* - containers == 0.6.* + - mtl == 2.2.* - simplexmq == 0.3.* + - sqlite-simple == 0.4.* - text == 1.2.* library: @@ -29,6 +31,7 @@ executables: main: Main.hs dependencies: - simplex-chat + - aeson == 1.5.* - async == 2.2.* - bytestring == 0.10.* - composition == 1.0.* diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index f0635279e9..5f1c69ebbc 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -1,44 +1,267 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE StandaloneDeriving #-} module Simplex.Chat.Protocol where -import Data.ByteString (ByteString) +import Control.Applicative (optional, (<|>)) +import Control.Monad.Except (throwError) +import Data.Attoparsec.ByteString.Char8 (Parser) +import qualified Data.Attoparsec.ByteString.Char8 as A +import Data.ByteString.Char8 (ByteString) +import qualified Data.ByteString.Char8 as B +import Data.Int (Int64) +import Data.List (findIndex) import Data.Text (Text) -import Simplex.Messaging.Agent.Protocol (ConnId) +import Simplex.Chat.Types +import Simplex.Messaging.Agent.Protocol +import Simplex.Messaging.Parsers (base64P) +import Simplex.Messaging.Protocol (MsgBody) +import Simplex.Messaging.Util (bshow) -data ChatEvent = GroupEvent | MessageEvent | InfoEvent +data ChatTransmission + = ChatTransmission + { agentMsgMeta :: MsgMeta, + chatDirection :: ChatDirection 'Agent, + chatMessage :: ChatMessage + } + | ChatTransmissionError + { agentMsgMeta :: MsgMeta, + chatDirection :: ChatDirection 'Agent, + msgBody :: MsgBody, + msgError :: ByteString + } + | AgentTransmission + { agentConnId :: ConnId, + chatDirection :: ChatDirection 'Agent, + agentMessage :: ACommand 'Agent + } + deriving (Eq, Show) -data Profile = Profile - { profileId :: ByteString, - displayName :: Text +data ChatDirection (p :: AParty) where + ReceivedDirectMessage :: Contact -> ChatDirection 'Agent + SentDirectMessage :: Contact -> ChatDirection 'Client + ReceivedGroupMessage :: Group -> Contact -> ChatDirection 'Agent + SentGroupMessage :: Group -> ChatDirection 'Client + +deriving instance Eq (ChatDirection p) + +deriving instance Show (ChatDirection p) + +newtype ChatMsgEvent = XMsgNew MessageType + deriving (Eq, Show) + +data MessageType = MTText | MTImage deriving (Eq, Show) + +toMsgType :: ByteString -> Either ByteString MessageType +toMsgType = \case + "c.text" -> Right MTText + "c.image" -> Right MTImage + t -> Left $ "invalid message type " <> t + +rawMsgType :: MessageType -> ByteString +rawMsgType = \case + MTText -> "c.text" + MTImage -> "c.image" + +data ChatMessage = ChatMessage + { chatMsgId :: Maybe Int64, + chatMsgEvent :: ChatMsgEvent, + chatMsgBody :: [MsgBodyContent], + chatDAGIdx :: Maybe Int } + deriving (Eq, Show) -data Contact = Contact - { contactId :: ByteString, - profile :: Profile, - connections :: [Connection] +toChatMessage :: RawChatMessage -> Either ByteString ChatMessage +toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} = do + body <- mapM toMsgBodyContent chatMsgBody + case chatMsgEvent of + "x.msg.new" -> case chatMsgParams of + [mt] -> do + t <- toMsgType mt + pure ChatMessage {chatMsgId, chatMsgEvent = XMsgNew t, chatMsgBody = body, chatDAGIdx = findDAG body} + _ -> throwError "x.msg.new expects one parameter" + _ -> throwError $ "unsupported event " <> chatMsgEvent +toChatMessage _ = Left "message continuation" + +findDAG :: [MsgBodyContent] -> Maybe Int +findDAG = findIndex $ isContentType SimplexDAG + +isContentType :: ContentType -> MsgBodyContent -> Bool +isContentType t MsgBodyContent {contentType = t'} = t == t' + +isSimplexContentType :: XContentType -> MsgBodyContent -> Bool +isSimplexContentType = isContentType . SimplexContentType + +rawChatMessage :: ChatMessage -> RawChatMessage +rawChatMessage ChatMessage {chatMsgId, chatMsgEvent = event, chatMsgBody = body} = + case event of + XMsgNew t -> + let chatMsgBody = map rawMsgBodyContent body + in RawChatMessage {chatMsgId, chatMsgEvent = "x.msg.new", chatMsgParams = [rawMsgType t], chatMsgBody} + +toMsgBodyContent :: RawMsgBodyContent -> Either ByteString MsgBodyContent +toMsgBodyContent RawMsgBodyContent {contentType, contentHash, contentData} = do + cType <- toContentType contentType + pure MsgBodyContent {contentType = cType, contentHash, contentData} + +rawMsgBodyContent :: MsgBodyContent -> RawMsgBodyContent +rawMsgBodyContent MsgBodyContent {contentType = t, contentHash, contentData} = + RawMsgBodyContent {contentType = rawContentType t, contentHash, contentData} + +data MsgBodyContent = MsgBodyContent + { contentType :: ContentType, + contentHash :: Maybe ByteString, + contentData :: MsgBodyPartData } + deriving (Eq, Show) -data Connection = Connection - { connId :: ConnId, - connLevel :: Int, - viaConn :: ConnId +data ContentType + = SimplexContentType XContentType + | MimeContentType MContentType + | SimplexDAG + deriving (Eq, Show) + +data XContentType = XCText | XCImage deriving (Eq, Show) + +data MContentType = MCImageJPG | MCImagePNG deriving (Eq, Show) + +toContentType :: RawContentType -> Either ByteString ContentType +toContentType (RawContentType ns cType) = case ns of + "x" -> case cType of + "text" -> Right $ SimplexContentType XCText + "image" -> Right $ SimplexContentType XCImage + "dag" -> Right SimplexDAG + _ -> err + "m" -> case cType of + "image/jpg" -> Right $ MimeContentType MCImageJPG + "image/png" -> Right $ MimeContentType MCImagePNG + _ -> err + _ -> err + where + err = Left $ "invalid content type " <> ns <> "." <> cType + +rawContentType :: ContentType -> RawContentType +rawContentType t = case t of + SimplexContentType t' -> RawContentType "x" $ case t' of + XCText -> "text" + XCImage -> "image" + MimeContentType t' -> RawContentType "m" $ case t' of + MCImageJPG -> "image/jpg" + MCImagePNG -> "image/png" + SimplexDAG -> RawContentType "x" "dag" + +newtype ContentMsg = NewContentMsg ContentData + +newtype ContentData = ContentText Text + +data RawChatMessage + = RawChatMessage + { chatMsgId :: Maybe Int64, + chatMsgEvent :: ByteString, + chatMsgParams :: [ByteString], + chatMsgBody :: [RawMsgBodyContent] + } + | RawChatMsgContinuation + { prevChatMsgId :: Int64, + continuationId :: Int, + continuationData :: ByteString + } + deriving (Eq, Show) + +data RawMsgBodyContent = RawMsgBodyContent + { contentType :: RawContentType, + contentHash :: Maybe ByteString, + contentData :: MsgBodyPartData } + deriving (Eq, Show) -data GroupMember = GroupMember - { groupId :: ByteString, - sharedMemberId :: ByteString, - contact :: Contact, - memberRole :: GroupMemberRole, - memberStatus :: GroupMemberStatus - } +data RawContentType = RawContentType NameSpace ByteString + deriving (Eq, Show) -data GroupMemberRole = GROwner | GRAdmin | GRStandard +type NameSpace = ByteString -data GroupMemberStatus = GSInvited | GSConnected | GSConnectedAll +data MsgBodyPartData + = -- | fully loaded + MBFull MsgData + | -- | partially loaded + MBPartial Int MsgData + | -- | not loaded yet + MBEmpty Int + deriving (Eq, Show) -data Group = Group - { groupId :: ByteString, - displayName :: Text, - members :: [GroupMember] - } +data MsgData + = MsgData ByteString + | MsgDataRec {dataId :: Int64, dataSize :: Int} + deriving (Eq, Show) + +class DataLength a where + dataLength :: a -> Int + +instance DataLength MsgBodyPartData where + dataLength (MBFull d) = dataLength d + dataLength (MBPartial l _) = l + dataLength (MBEmpty l) = l + +instance DataLength MsgData where + dataLength (MsgData s) = B.length s + dataLength MsgDataRec {dataSize} = dataSize + +rawChatMessageP :: Parser RawChatMessage +rawChatMessageP = A.char '#' *> chatMsgContP <|> chatMsgP + where + chatMsgContP :: Parser RawChatMessage + chatMsgContP = do + prevChatMsgId <- A.decimal <* A.char '.' + continuationId <- A.decimal <* A.space + continuationData <- A.takeByteString + pure RawChatMsgContinuation {prevChatMsgId, continuationId, continuationData} + chatMsgP :: Parser RawChatMessage + chatMsgP = do + chatMsgId <- optional A.decimal <* A.space + chatMsgEvent <- B.intercalate "." <$> identifier `A.sepBy1'` A.char '.' <* A.space + chatMsgParams <- A.takeWhile1 (not . A.inClass ", ") `A.sepBy'` A.char ',' <* A.space + chatMsgBody <- msgBodyContent =<< contentInfo `A.sepBy'` A.char ',' <* A.space + pure RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} + identifier :: Parser ByteString + identifier = B.cons <$> A.letter_ascii <*> A.takeWhile (\c -> A.isAlpha_ascii c || A.isDigit c) + contentInfo :: Parser RawMsgBodyContent + contentInfo = do + contentType <- RawContentType <$> identifier <* A.char '.' <*> A.takeTill (A.inClass ":, ") + contentSize <- A.char ':' *> A.decimal + contentHash <- optional (A.char ':' *> base64P) + pure RawMsgBodyContent {contentType, contentHash, contentData = MBEmpty contentSize} + msgBodyContent :: [RawMsgBodyContent] -> Parser [RawMsgBodyContent] + msgBodyContent [] = pure [] + msgBodyContent (p@RawMsgBodyContent {contentData = MBEmpty size} : ps) = do + s <- A.take size <* A.space <|> A.takeByteString + if B.length s == size + then ((p {contentData = MBFull $ MsgData s} :: RawMsgBodyContent) :) <$> msgBodyContent ps + else pure $ (if B.null s then p else p {contentData = MBPartial size $ MsgData s} :: RawMsgBodyContent) : ps + msgBodyContent _ = fail "expected contentData = MBEmpty" + +serializeRawChatMessage :: RawChatMessage -> ByteString +serializeRawChatMessage = \case + RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} -> + B.unwords + [ maybe "" bshow chatMsgId, + chatMsgEvent, + B.intercalate "," chatMsgParams, + B.unwords $ map serializeContentInfo chatMsgBody, + B.unwords $ map serializeContentData chatMsgBody + ] + RawChatMsgContinuation {prevChatMsgId, continuationId, continuationData} -> + bshow prevChatMsgId <> "." <> bshow continuationId <> " " <> continuationData + +serializeContentInfo :: RawMsgBodyContent -> ByteString +serializeContentInfo RawMsgBodyContent {contentType = RawContentType ns cType, contentHash, contentData} = + ns <> "." <> cType <> ":" <> bshow (dataLength contentData) <> maybe "" (":" <>) contentHash + +serializeContentData :: RawMsgBodyContent -> ByteString +serializeContentData RawMsgBodyContent {contentData = MBFull (MsgData s)} = s +serializeContentData _ = "" diff --git a/src/Simplex/Chat/Protocol_.hs b/src/Simplex/Chat/Protocol_.hs new file mode 100644 index 0000000000..b241ac42b2 --- /dev/null +++ b/src/Simplex/Chat/Protocol_.hs @@ -0,0 +1,44 @@ +{-# LANGUAGE DuplicateRecordFields #-} + +module Simplex.Chat.Protocol_ where + +import Data.ByteString (ByteString) +import Data.Text (Text) +import Simplex.Messaging.Agent.Protocol (ConnId) + +data ChatEvent = GroupEvent | MessageEvent | InfoEvent + +data Profile = Profile + { profileId :: ByteString, + displayName :: Text + } + +data Contact = Contact + { contactId :: ByteString, + profile :: Profile, + connections :: [Connection] + } + +data Connection = Connection + { connId :: ConnId, + connLevel :: Int, + viaConn :: ConnId + } + +data GroupMember = GroupMember + { groupId :: ByteString, + sharedMemberId :: ByteString, + contact :: Contact, + memberRole :: GroupMemberRole, + memberStatus :: GroupMemberStatus + } + +data GroupMemberRole = GROwner | GRAdmin | GRStandard + +data GroupMemberStatus = GSInvited | GSConnected | GSConnectedAll + +data Group = Group + { groupId :: ByteString, + displayName :: Text, + members :: [GroupMember] + } diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs new file mode 100644 index 0000000000..6cf6a44c3f --- /dev/null +++ b/src/Simplex/Chat/Types.hs @@ -0,0 +1,80 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.Chat.Types where + +import Data.ByteString.Char8 (ByteString) +import Data.Int (Int64) +import Data.Text (Text) +import Database.SQLite.Simple.FromField (FromField (..)) +import Database.SQLite.Simple.ToField (ToField (..)) +import Simplex.Messaging.Agent.Protocol (ConnId) +import Simplex.Messaging.Agent.Store.SQLite (fromTextField_) + +data User = User + { userId :: UserId, + profile :: Profile + } + +type UserId = Int64 + +data Contact = Contact + { contactId :: Int64, + localContactRef :: ContactRef, + profile :: Maybe Profile, + activeConn :: Connection + } + deriving (Eq, Show) + +type ContactRef = Text + +data Group = Group + { groupId :: Int64, + localGroupRef :: Text + } + deriving (Eq, Show) + +data Profile = Profile + { profileId :: Int64, + contactRef :: ContactRef, + displayName :: Text + } + deriving (Eq, Show) + +data Connection = Connection + { connId :: Int64, + agentConnId :: ConnId, + connLevel :: Int, + viaContact :: Maybe Int64, + connStatus :: ConnStatus + } + deriving (Eq, Show) + +data ConnStatus = ConnNew | ConnConfirmed | ConnAccepted | ConnReady + deriving (Eq, Show) + +instance FromField ConnStatus where fromField = fromTextField_ connStatusT + +instance ToField ConnStatus where toField = toField . serializeConnStatus + +connStatusT :: Text -> Maybe ConnStatus +connStatusT = \case + "NEW" -> Just ConnNew + "CONF" -> Just ConnConfirmed + "ACPT" -> Just ConnAccepted + "READY" -> Just ConnReady + _ -> Nothing + +serializeConnStatus :: ConnStatus -> Text +serializeConnStatus = \case + ConnNew -> "NEW" + ConnConfirmed -> "CONF" + ConnAccepted -> "ACPT" + ConnReady -> "READY" + +data NewConnection = NewConnection + { agentConnId :: ByteString, + connLevel :: Int, + viaConn :: Maybe Int64 + } diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md new file mode 100644 index 0000000000..7ed41ce9cb --- /dev/null +++ b/src/Simplex/Chat/protocol.md @@ -0,0 +1,70 @@ +# Chat protocol + +## Design constraints + +- the transport message has a fixed size (8 or 16kb) +- the chat message can have multiple parts/attachments +- the chat message including attachments can be of any size +- if the message is partially received, it should be possible to parse and display the received parts + +## Questions + +- should content types be: + - limited to MIME-types + - separate content types vocabulary + - both MIME types and extensions + - allow additional content types namespaces + +## Message syntax + +The syntax of the message inside agent MSG: + +```abnf +agentMessageBody = message / msgContinuation +message = [chatMsgId] SP msgEvent SP [parameters] SP [contentParts [SP msgBodyParts]] +chatMsgId = 1*DIGIT ; used to refer to previous message; + ; in the group should only be used in messages sent to all members, + ; which is the main reason not to use external agent ID - + ; some messages are sent only to one member +msgEvent = protocolNamespace 1*("." msgTypeName) +protocolNamespace = 1*ALPHA ; "x" for all events defined in the protocol +msgTypeName = 1*ALPHA +parameters = parameter *("," parameter) +parameter = 1*(%x21-2B / %x2D-7E) ; exclude control characters, space, comma (%x2C) +contentParts = contentPart *("," contentPart) +contentPart = contentTypeNamespace "." contentType ":" contentSize [":" contentHash] +contentType = "i." / contentTypeNamespace "." 1*("." contentTypeName) +contentTypeNamespace = 1*ALPHA +contentTypeName = 1*ALPHA +contentHash = +msgBodyParts = msgBodyPart *(SP msgBodyPart) +msgEventParents = msgEventParent *msgEventParent ; binary body part for content type "x.dag" +msgEventParent = memberId refMsgId refMsgHash +memberId = 8*8(OCTET) ; shared member ID +refMsgId = 8*8(OCTET) ; sequential message number - external agent message ID +refMsgHash = 16*16(OCTET) ; SHA256 of agent message body +msgContinuation = "#" prevMsgId "." continuationId continuationData +``` + +### Example: messages, updates, groups + +``` +"3 x.msg.new c.text c.text:5 hello " +"4 x.msg.new c.image i.image/jpg:256,i.image/png:4096 abcd abcd " +"4 x.msg.new c.image x.dag:32,i.image/jpg:8000:hash1,i.image/png:16000:hash2 binary1" +"#4.1 binary1end binary2" +"#4.2 binary2continued" +"#4.3 binary2end " +"5 x.msg.new c.image i.image/jpg:256,i.image/url:160 abcd https://media.example.com/asdf#abcd " +'6 x.msg.update 3 c.text:11,x.dag:16 hello there abcd ' +'7 x.msg.delete 3' +'8 x.msg.new app/v1 i.text/html:NNN,i.text/css:NNN,c.js:NNN,c.json:NNN ... ... ... {...} ' +'9 x.msg.eval 8 c.json:NNN {...} ' +'10 x.msg.new c.text 2 c.text:16,x.dag:32 hello there @123 abcd ' +' x.grp.mem.inv 23456,123 1 c.json NNN {...} ' +' x.grp.mem.acpt 23456 1 c.text NNN ' +' x.grp.mem.intro 23456,234 1 c.json NNN {...} ' +' x.grp.mem.inv 23456,234 1 c.text NNN ' +' x.grp.mem.req 23456,123 1 c.json NNN {...} ' +' x.grp.mem.direct.inv 23456,234 1 text NNN ' +``` diff --git a/stack.yaml b/stack.yaml index d0635751fb..732c3d6e25 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,8 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 09c6adeabc533537dcc039e2195123c6f7167ebe + commit: 8ba3e3e45a6006d173738db9eac1068edad74df7 + # this commit is in PR #164 # # extra-deps: [] diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs new file mode 100644 index 0000000000..8dbfe18b75 --- /dev/null +++ b/tests/ProtocolTests.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +module ProtocolTests where + +import Data.ByteString.Char8 (ByteString) +import Simplex.Chat.Protocol +import Simplex.Messaging.Parsers (parseAll) +import Test.Hspec + +protocolTests :: Spec +protocolTests = do + parseChatMessageTest + +(#==) :: ByteString -> RawChatMessage -> Expectation +s #== msg = parseAll rawChatMessageP s `shouldBe` Right msg + +parseChatMessageTest :: Spec +parseChatMessageTest = describe "Raw chat message format" $ do + it "no parameters and content" $ + "5 x.grp.mem.leave " #== RawChatMessage (Just 5) "x.grp.mem.leave" [] [] + it "one parameter, no content" $ + "6 x.msg.del 3 " #== RawChatMessage (Just 6) "x.msg.del" ["3"] [] + it "with content that fits the message and optional trailing space" $ + "7 x.msg.new c.text c.text:11 hello there " + #== RawChatMessage + (Just 7) + "x.msg.new" + ["c.text"] + [RawMsgBodyContent (RawContentType "c" "text") Nothing $ MBFull (MsgData "hello there")] + it "with content that fits the message, without trailing space" $ + "7 x.msg.new c.text c.text:11 hello there" + #== RawChatMessage + (Just 7) + "x.msg.new" + ["c.text"] + [RawMsgBodyContent (RawContentType "c" "text") Nothing $ MBFull (MsgData "hello there")] + it "with DAG reference and partial content" $ + "8 x.msg.new c.image x.dag:16,c.text:7,m.image/jpg:64:MDEyMzQ1Njc=,m.image/png:4000:MDEyMzQ1Njc= 0123456789012345 picture abcdef" + #== RawChatMessage + (Just 8) + "x.msg.new" + ["c.image"] + [ RawMsgBodyContent (RawContentType "x" "dag") Nothing $ MBFull (MsgData "0123456789012345"), + RawMsgBodyContent (RawContentType "c" "text") Nothing $ MBFull (MsgData "picture"), + RawMsgBodyContent (RawContentType "m" "image/jpg") (Just "01234567") $ MBPartial 64 (MsgData "abcdef"), + RawMsgBodyContent (RawContentType "m" "image/png") (Just "01234567") $ MBEmpty 4000 + ] + it "message continuation" $ + "#8.1 abcdef" #== RawChatMsgContinuation 8 1 "abcdef" + it "without message id" $ + " x.grp.mem.inv 23456,123 c.json:46 {\"contactRef\":\"john\",\"displayName\":\"John Doe\"}" + #== RawChatMessage + Nothing + "x.grp.mem.inv" + ["23456", "123"] + [RawMsgBodyContent (RawContentType "c" "json") Nothing $ MBFull (MsgData "{\"contactRef\":\"john\",\"displayName\":\"John Doe\"}")] diff --git a/tests/Test.hs b/tests/Test.hs index b4d6ff9275..2575ec5eb9 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -1,7 +1,9 @@ import MarkdownTests +import ProtocolTests import Test.Hspec main :: IO () main = do hspec $ do describe "SimpleX chat markdown" markdownTests + describe "SimpleX chat protocol" protocolTests From 58889be83d7ac0cfe8b7859fb0d187441584d03d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 5 Jul 2021 19:54:44 +0100 Subject: [PATCH 04/29] establish connection using user profiles (#69) * establish connection using user profiles (TODO: delete contact and send message) * delete contact and send message with the updated schema * comment * refactor, remove old code --- apps/simplex-chat/ChatOptions.hs | 2 +- apps/simplex-chat/Main.hs | 3 +- apps/simplex-chat/Simplex/Chat.hs | 137 +++++++--- apps/simplex-chat/Simplex/Chat/Controller.hs | 14 +- apps/simplex-chat/Simplex/Store.hs | 252 +++++++++++++------ apps/simplex-chat/Simplex/View.hs | 24 +- migrations/20210612_initial.sql | 87 +++---- package.yaml | 2 + src/Simplex/Chat/Types.hs | 71 ++++-- 9 files changed, 400 insertions(+), 192 deletions(-) diff --git a/apps/simplex-chat/ChatOptions.hs b/apps/simplex-chat/ChatOptions.hs index 11ececded6..b646d66d02 100644 --- a/apps/simplex-chat/ChatOptions.hs +++ b/apps/simplex-chat/ChatOptions.hs @@ -35,7 +35,7 @@ chatOpts appDir = <> value (L.fromList ["smp1.simplex.im#pLdiGvm0jD1CMblnov6Edd/391OrYsShw+RgdfR0ChA="]) ) where - defaultDbFilePath = combine appDir "smp-chat.db" + defaultDbFilePath = combine appDir "simplex" parseSMPServer :: ReadM (NonEmpty SMPServer) parseSMPServer = eitherReader $ parseAll servers . B.pack diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index 041ba7d721..8d24c0d4d2 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -42,10 +42,11 @@ main :: IO () main = do ChatOpts {dbFile, smpServers} <- welcomeGetOpts st <- createStore (dbFile <> ".chat.db") 4 + user <- getCreateActiveUser st ct <- newChatTerminal a <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} notify <- initializeNotifications - cc <- atomically $ newChatController a ct st notify $ tbqSize cfg + cc <- atomically $ newChatController a ct st user notify $ tbqSize cfg -- setLogLevel LogInfo -- LogError -- withGlobalLogging logCfg $ do runReaderT simplexChat cc diff --git a/apps/simplex-chat/Simplex/Chat.hs b/apps/simplex-chat/Simplex/Chat.hs index fa7ebfc1fe..25a3b340d8 100644 --- a/apps/simplex-chat/Simplex/Chat.hs +++ b/apps/simplex-chat/Simplex/Chat.hs @@ -9,18 +9,20 @@ module Simplex.Chat where -import Control.Applicative (optional, (<|>)) +import Control.Applicative ((<|>)) import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader +import qualified Data.Aeson as J import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (first) import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.Lazy.Char8 as LB import Data.Functor (($>)) import Data.List (find) -import Data.Maybe (fromMaybe) +import Data.Maybe (isJust) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) @@ -37,14 +39,17 @@ import Simplex.Notification import Simplex.Store import Simplex.Terminal import Simplex.View +import System.Exit (exitFailure) +import System.IO (hFlush, stdout) +import Text.Read (readMaybe) import qualified UnliftIO.Exception as E import UnliftIO.STM data ChatCommand = ChatHelp | MarkdownHelp - | AddContact (Maybe ContactRef) - | Connect (Maybe ContactRef) SMPQueueInfo + | AddContact + | Connect SMPQueueInfo | DeleteContact ContactRef | SendMessage ContactRef ByteString deriving (Show) @@ -71,38 +76,35 @@ inputSubscriber = do case cmd of SendMessage c msg -> showSentMessage c msg _ -> printToView [plain s] - runExceptT (processChatCommand cmd) >>= \case + user <- asks currentUser + runExceptT (processChatCommand user cmd) >>= \case Left e -> showChatError e _ -> pure () -processChatCommand :: ChatMonad m => ChatCommand -> m () -processChatCommand = \case +processChatCommand :: ChatMonad m => User -> ChatCommand -> m () +processChatCommand User {userId, profile} = \case ChatHelp -> printToView chatHelpInfo MarkdownHelp -> printToView markdownInfo - AddContact cRef -> do - (connId, qInfo) <- withAgent (fromMaybe "" cRef) createConnection - userId <- asks currentUserId - contact <- withStore $ \st -> createDirectContact st userId connId cRef - showInvitation (localContactRef contact) qInfo - Connect cRef qInfo -> do - userId <- asks currentUserId - connId <- withAgent (fromMaybe "" cRef) $ \agent -> joinConnection agent qInfo "user profile here" - void $ withStore $ \st -> createDirectContact st userId connId cRef + AddContact -> do + (connId, qInfo) <- withAgent createConnection + withStore $ \st -> createDirectConnection st userId connId + showInvitation qInfo + Connect qInfo -> do + connId <- withAgent $ \agent -> joinConnection agent qInfo $ LB.toStrict (J.encode profile) + withStore $ \st -> createDirectConnection st userId connId DeleteContact cRef -> do - userId <- asks currentUserId conns <- withStore $ \st -> getContactConnections st userId cRef - withAgent cRef $ \smp -> forM_ conns $ \Connection {agentConnId} -> + withAgent $ \smp -> forM_ conns $ \Connection {agentConnId} -> deleteConnection smp agentConnId `catchError` \(_ :: AgentErrorType) -> pure () - void $ withStore $ \st -> deleteContact st userId cRef + withStore $ \st -> deleteContact st userId cRef unsetActive $ ActiveC cRef when (null conns) . throwError . ChatErrorContact $ CENotFound cRef showContactDeleted cRef SendMessage cRef msg -> do - userId <- asks currentUserId Connection {agentConnId} <- withStore $ \st -> getContactConnection st userId cRef let body = MsgBodyContent {contentType = SimplexContentType XCText, contentHash = Nothing, contentData = MBFull $ MsgData msg} rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent = XMsgNew MTText, chatMsgBody = [body], chatDAGIdx = Nothing} - void . withAgent cRef $ \smp -> sendMessage smp agentConnId $ serializeRawChatMessage rawMsg + void . withAgent $ \smp -> sendMessage smp agentConnId $ serializeRawChatMessage rawMsg setActive $ ActiveC cRef agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () @@ -111,7 +113,7 @@ agentSubscriber = do cQ <- asks chatQ forever $ do (_, agentConnId, resp) <- atomically (readTBQueue aQ) - userId <- asks currentUserId + User {userId} <- asks currentUser runExceptT (withStore $ \st -> getConnectionChatDirection st userId agentConnId) >>= \case -- TODO handle errors Left e -> liftIO $ print e @@ -128,7 +130,8 @@ agentSubscriber = do chatSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () chatSubscriber = do cQ <- asks chatQ - forever $ + forever $ do + User {userId, profile} <- asks currentUser atomically (readTBQueue cQ) >>= \case ChatTransmission { agentMsgMeta = meta, @@ -145,12 +148,19 @@ chatSubscriber = do setActive $ ActiveC c _ -> pure () _ -> pure () - AgentTransmission {agentConnId, chatDirection = ReceivedDirectMessage Contact {localContactRef = c}, agentMessage} -> + AgentTransmission {agentConnId, chatDirection = ReceivedDirectMessage NewContact {activeConn}, agentMessage} -> + void . runExceptT $ case agentMessage of + CONF confId connInfo -> do + -- TODO update connection status + saveContact userId activeConn connInfo + withAgent $ \a -> allowConnection a agentConnId confId $ LB.toStrict (J.encode profile) + INFO connInfo -> + saveContact userId activeConn connInfo + _ -> pure () + AgentTransmission {chatDirection = ReceivedDirectMessage Contact {localContactRef = c}, agentMessage} -> case agentMessage of - CONF confId _ -> - -- TODO save profile? Show confirmation? - void . runExceptT . withAgent c $ \a -> allowConnection a agentConnId confId "user profile here" CON -> do + -- TODO update connection status showContactConnected c showToast ("@" <> c) "connected" setActive $ ActiveC c @@ -160,6 +170,68 @@ chatSubscriber = do unsetActive $ ActiveC c _ -> pure () _ -> pure () + where + saveContact userId activeConn connInfo = do + p <- liftEither . first (ChatErrorContact . CEProfile) $ J.eitherDecodeStrict' connInfo + withStore $ \st -> createDirectContact st userId activeConn p + +getCreateActiveUser :: SQLiteStore -> IO User +getCreateActiveUser st = do + user <- + getUsers st >>= \case + [] -> newUser + users -> maybe (selectUser users) pure (find activeUser users) + putStrLn $ "Current user: " <> userStr user + pure user + where + newUser :: IO User + newUser = do + putStrLn + "No user profiles found, it will be created now.\n\ + \Please choose your alias and your profile name.\n\ + \They will be sent to your contacts when you connect.\n\ + \They are only stored on your device and you can change them later." + loop + where + loop = do + contactRef <- getContactRef + displayName <- T.pack <$> getWithPrompt "profile name (optional)" + liftIO (runExceptT $ createUser st Profile {contactRef, displayName} True) >>= \case + Left SEDuplicateContactRef -> do + putStrLn "chosen alias already used by another profile on this device, choose another one" + loop + Left e -> putStrLn ("database error " <> show e) >> exitFailure + Right user -> pure user + selectUser :: [User] -> IO User + selectUser [user] = do + liftIO $ setActiveUser st (userId user) + pure user + selectUser users = do + putStrLn "Select user profile: " + forM_ (zip [1 ..] users) $ \(n :: Int, user) -> putStrLn $ show n <> " - " <> userStr user + loop + where + loop = do + nStr <- getWithPrompt $ "user profile number (1 .. " <> show (length users) <> ")" + case readMaybe nStr :: Maybe Int of + Nothing -> putStrLn "invalid user number" >> loop + Just n + | n <= 0 || n > length users -> putStrLn "invalid user number" >> loop + | otherwise -> do + let user = users !! (n - 1) + liftIO $ setActiveUser st (userId user) + pure user + userStr :: User -> String + userStr User {localContactRef, profile = Profile {displayName}} = + T.unpack $ localContactRef <> if T.null displayName then "" else " (" <> displayName <> ")" + getContactRef :: IO ContactRef + getContactRef = do + contactRef <- getWithPrompt "alias (no spaces)" + if null contactRef || isJust (find (== ' ') contactRef) + then putStrLn "alias has space(s), choose another one" >> getContactRef + else pure $ T.pack contactRef + getWithPrompt :: String -> IO String + getWithPrompt s = putStr (s <> ": ") >> hFlush stdout >> getLine showToast :: (MonadUnliftIO m, MonadReader ChatController m) => Text -> Text -> m () showToast title text = atomically . (`writeTBQueue` Notification {title, text}) =<< asks notifyQ @@ -169,11 +241,11 @@ notificationSubscriber = do ChatController {notifyQ, sendNotification} <- ask forever $ atomically (readTBQueue notifyQ) >>= liftIO . sendNotification -withAgent :: ChatMonad m => ContactRef -> (AgentClient -> ExceptT AgentErrorType m a) -> m a -withAgent c action = +withAgent :: ChatMonad m => (AgentClient -> ExceptT AgentErrorType m a) -> m a +withAgent action = asks smpAgent >>= runExceptT . action - >>= liftEither . first (ChatErrorAgent c) + >>= liftEither . first ChatErrorAgent withStore :: ChatMonad m => @@ -197,11 +269,10 @@ withStore action = do chatCommandP :: Parser ChatCommand chatCommandP = ("/help" <|> "/h") $> ChatHelp - <|> ("/add" <|> "/a") *> (AddContact <$> optional (A.space *> contactRef)) - <|> ("/connect" <|> "/c") *> ((Connect <$> optional (A.space *> contactRef) <*> qInfo) <|> (Connect Nothing <$> qInfo)) + <|> ("/add" <|> "/a") $> AddContact + <|> ("/connect " <|> "/c ") *> (Connect <$> smpQueueInfoP) <|> ("/delete " <|> "/d ") *> (DeleteContact <$> contactRef) <|> A.char '@' *> (SendMessage <$> contactRef <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp where contactRef = safeDecodeUtf8 <$> A.takeTill (== ' ') - qInfo = A.space *> smpQueueInfoP diff --git a/apps/simplex-chat/Simplex/Chat/Controller.hs b/apps/simplex-chat/Simplex/Chat/Controller.hs index 43d9e5e780..ddd0850bc3 100644 --- a/apps/simplex-chat/Simplex/Chat/Controller.hs +++ b/apps/simplex-chat/Simplex/Chat/Controller.hs @@ -1,7 +1,9 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} module Simplex.Chat.Controller where @@ -21,7 +23,7 @@ import Simplex.Terminal import UnliftIO.STM data ChatController = ChatController - { currentUserId :: UserId, + { currentUser :: User, smpAgent :: AgentClient, chatTerminal :: ChatTerminal, chatStore :: SQLiteStore, @@ -35,21 +37,21 @@ data InputEvent = InputCommand String | InputControl Char data ChatError = ChatErrorContact ContactError - | ChatErrorAgent ContactRef AgentErrorType + | ChatErrorAgent AgentErrorType | ChatErrorStore StoreError deriving (Show, Exception) -newtype ContactError = CENotFound ContactRef +data ContactError = CENotFound ContactRef | CEProfile String deriving (Show, Exception) type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) -newChatController :: AgentClient -> ChatTerminal -> SQLiteStore -> (Notification -> IO ()) -> Natural -> STM ChatController -newChatController smpAgent chatTerminal chatStore sendNotification qSize = do +newChatController :: AgentClient -> ChatTerminal -> SQLiteStore -> User -> (Notification -> IO ()) -> Natural -> STM ChatController +newChatController smpAgent chatTerminal chatStore currentUser sendNotification qSize = do inputQ <- newTBQueue qSize notifyQ <- newTBQueue qSize chatQ <- newTBQueue qSize - pure ChatController {currentUserId = 1, smpAgent, chatTerminal, chatStore, chatQ, inputQ, notifyQ, sendNotification} + pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, chatQ, inputQ, notifyQ, sendNotification} setActive :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () setActive to = asks (activeTo . chatTerminal) >>= atomically . (`writeTVar` to) diff --git a/apps/simplex-chat/Simplex/Store.hs b/apps/simplex-chat/Simplex/Store.hs index 37a54c85ae..16ee8a874e 100644 --- a/apps/simplex-chat/Simplex/Store.hs +++ b/apps/simplex-chat/Simplex/Store.hs @@ -13,6 +13,10 @@ module Simplex.Store ( SQLiteStore, StoreError (..), createStore, + createUser, + getUsers, + setActiveUser, + createDirectConnection, createDirectContact, deleteContact, getContactConnection, @@ -21,7 +25,8 @@ module Simplex.Store ) where -import Control.Exception +import Control.Exception (Exception) +import qualified Control.Exception as E import Control.Monad.Except import Control.Monad.IO.Unlift import Data.ByteString.Char8 (ByteString) @@ -29,10 +34,12 @@ import Data.FileEmbed (embedDir, makeRelativeToProject) import Data.Function (on) import Data.Int (Int64) import Data.List (sortBy) -import Data.Maybe (fromMaybe) +import Data.Maybe (listToMaybe) import Data.Text (Text) +import qualified Data.Text as T import Data.Text.Encoding (decodeUtf8) -import Database.SQLite.Simple (NamedParam (..), Only (..)) +import Data.Time.Clock (UTCTime) +import Database.SQLite.Simple (NamedParam (..), Only (..), SQLError) import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Protocol @@ -40,7 +47,7 @@ import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol (AParty (..), ConnId) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore, withTransaction) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) -import Simplex.Messaging.Util (liftIOEither) +import Simplex.Messaging.Util (bshow, liftIOEither, (<$$>)) import System.FilePath (takeBaseName, takeExtension) -- | The list of migrations in ascending order by date @@ -55,33 +62,122 @@ migrations = createStore :: FilePath -> Int -> IO SQLiteStore createStore dbFilePath poolSize = createSQLiteStore dbFilePath poolSize migrations +checkConstraint :: StoreError -> IO (Either StoreError a) -> IO (Either StoreError a) +checkConstraint err action = action `E.catch` (pure . Left . handleSQLError err) + +handleSQLError :: StoreError -> SQLError -> StoreError +handleSQLError err e + | DB.sqlError e == DB.ErrorConstraint = err + | otherwise = SEInternal $ bshow e + insertedRowId :: DB.Connection -> IO Int64 insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid();" -createDirectContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> Maybe Text -> m Contact -createDirectContact st userId agentConnId contactRef = - liftIO . withTransaction st $ \db -> do - DB.execute db "INSERT INTO connections (user_id, agent_conn_id, conn_status) VALUES (?,?,?);" (userId, agentConnId, ConnNew) - connId <- insertedRowId db - let activeConn = Connection {connId, agentConnId, connLevel = 0, viaContact = Nothing, connStatus = ConnNew} - -- TODO support undefined localContactRef (Nothing) - currently it would fail - let localContactRef = fromMaybe "" contactRef - DB.execute db "INSERT INTO contacts (user_id, local_contact_ref) VALUES (?,?);" (userId, localContactRef) +createUser :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> Profile -> Bool -> m User +createUser st Profile {contactRef, displayName} activeUser = + liftIOEither . checkConstraint SEDuplicateContactRef . withTransaction st $ \db -> do + DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?);" (contactRef, displayName) + profileId <- insertedRowId db + DB.execute db "INSERT INTO users (contact_id, active_user) VALUES (0, ?);" (Only activeUser) + userId <- insertedRowId db + DB.execute + db + "INSERT INTO contacts (contact_profile_id, local_contact_ref, lcr_base, user_id, user) VALUES (?, ?, ?, ?, 1);" + (profileId, contactRef, contactRef, userId) contactId <- insertedRowId db - DB.execute db "INSERT INTO contact_connections (connection_id, contact_id, active) VALUES (?,?,1);" (connId, contactId) - pure Contact {contactId, localContactRef, profile = Nothing, activeConn} + DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?;" (contactId, userId) + pure . Right $ toUser (userId, activeUser, contactRef, displayName) + +getUsers :: SQLiteStore -> IO [User] +getUsers st = + withTransaction st $ \db -> + map toUser + <$> DB.query_ + db + [sql| + SELECT u.user_id, u.active_user, c.local_contact_ref, p.display_name + FROM users u + JOIN contacts c ON u.contact_id = c.contact_id + JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id + |] + +toUser :: (UserId, Bool, ContactRef, Text) -> User +toUser (userId, activeUser, contactRef, displayName) = + let profile = Profile {contactRef, displayName} + in User {userId, localContactRef = contactRef, profile, activeUser} + +setActiveUser :: MonadUnliftIO m => SQLiteStore -> UserId -> m () +setActiveUser st userId = do + liftIO . withTransaction st $ \db -> do + DB.execute_ db "UPDATE users SET active_user = 0;" + DB.execute db "UPDATE users SET active_user = 1 WHERE user_id = ?;" (Only userId) + +createDirectConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> m () +createDirectConnection st userId agentConnId = + liftIO . withTransaction st $ \db -> + DB.execute + db + [sql| + INSERT INTO connections + (user_id, agent_conn_id, conn_status, conn_type) VALUES (?,?,?,?); + |] + (userId, agentConnId, ConnNew, ConnContact) + +createDirectContact :: + (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> Connection -> Profile -> m () +createDirectContact st userId Connection {connId} Profile {contactRef, displayName} = + liftIOEither . withTransaction st $ \db -> do + DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?);" (contactRef, displayName) + profileId <- insertedRowId db + lcrSuffix <- getLcrSuffix db + create db profileId lcrSuffix 20 + where + getLcrSuffix :: DB.Connection -> IO Int + getLcrSuffix db = + maybe 0 ((+ 1) . fromOnly) . listToMaybe + <$> DB.queryNamed + db + [sql| + SELECT lcr_suffix FROM contacts + WHERE user_id = :user_id AND lcr_base = :contact_ref + ORDER BY lcr_suffix DESC + LIMIT 1; + |] + [":user_id" := userId, ":contact_ref" := contactRef] + create :: DB.Connection -> Int64 -> Int -> Int -> IO (Either StoreError ()) + create _ _ _ 0 = pure $ Left SEDuplicateContactRef + create db profileId lcrSuffix attempts = do + let lcr = localContactRef' lcrSuffix + E.try (insertUser lcr) >>= \case + Right () -> do + contactId <- insertedRowId db + DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) + pure $ Right () + Left e + | DB.sqlError e == DB.ErrorConstraint -> create db profileId (lcrSuffix + 1) (attempts - 1) + | otherwise -> E.throwIO e + where + localContactRef' 0 = contactRef + localContactRef' n = contactRef <> T.pack ('_' : show n) + insertUser lcr = + DB.execute + db + [sql| + INSERT INTO contacts + (contact_profile_id, local_contact_ref, lcr_base, lcr_suffix, user_id) VALUES (?, ?, ?, ?, ?) + |] + (profileId, lcr, contactRef, lcrSuffix, userId) deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactRef -> m () deleteContact st userId contactRef = liftIO . withTransaction st $ \db -> forM_ [ [sql| - DELETE FROM connections - WHERE user_id = :user_id AND connection_id IN ( - SELECT cc.connection_id - FROM contact_connections AS cc - JOIN contacts AS cs ON cs.contact_id = cc.contact_id - WHERE local_contact_ref = :contact_ref + DELETE FROM connections WHERE connection_id IN ( + SELECT connection_id + FROM connections c + JOIN contacts cs ON c.contact_id = cs.contact_id + WHERE cs.user_id = :user_id AND cs.local_contact_ref = :contact_ref ); |], [sql| @@ -91,21 +187,25 @@ deleteContact st userId contactRef = ] $ \q -> DB.executeNamed db q [":user_id" := userId, ":contact_ref" := contactRef] -getContactConnection :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m Connection +-- TODO return the last connection that is ready, not any last connection +-- requires updating connection status +getContactConnection :: + (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m Connection getContactConnection st userId contactRef = liftIOEither . withTransaction st $ \db -> connection <$> DB.queryNamed db [sql| - SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status - FROM connections AS c - JOIN contact_connections AS cc ON cc.connection_id == c.connection_id - JOIN contacts AS cs ON cc.contact_id == cs.contact_id + SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM connections c + JOIN contacts cs ON c.contact_id == cs.contact_id WHERE c.user_id = :user_id AND cs.user_id = :user_id AND cs.local_contact_ref == :contact_ref - AND cc.active == 1; + ORDER BY c.connection_id DESC + LIMIT 1; |] [":user_id" := userId, ":contact_ref" := contactRef] where @@ -119,61 +219,71 @@ getContactConnections st userId contactRef = <$> DB.queryNamed db [sql| - SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status - FROM connections AS c - JOIN contact_connections AS cc ON cc.connection_id == c.connection_id - JOIN contacts AS cs ON cc.contact_id == cs.contact_id + SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM connections c + JOIN contacts cs ON c.contact_id == cs.contact_id WHERE c.user_id = :user_id AND cs.user_id = :user_id - AND cs.local_contact_ref == :contact_ref - AND cc.active == 1; + AND cs.local_contact_ref == :contact_ref; |] [":user_id" := userId, ":contact_ref" := contactRef] -toConnection :: (Int64, ConnId, Int, Maybe Int64, ConnStatus) -> Connection -toConnection (connId, agentConnId, connLevel, viaContact, connStatus) = - Connection {connId, agentConnId, connLevel, viaContact, connStatus} +toConnection :: + (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, UTCTime) -> Connection +toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) = + let entityId = entityId_ connType + in Connection {connId, agentConnId, connLevel, viaContact, connStatus, connType, entityId, createdAt} + where + entityId_ :: ConnType -> Maybe Int64 + entityId_ ConnContact = contactId + entityId_ ConnMember = groupMemberId getConnectionChatDirection :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ConnId -> m (ChatDirection 'Agent) getConnectionChatDirection st userId agentConnId = - liftIOEither . withTransaction st $ \db -> - chatDirection - <$> DB.queryNamed - db - [sql| - SELECT - cs.contact_id, cs.local_contact_ref, - a.connection_id, a.conn_level, a.via_contact, a.conn_status, - p.contact_profile_id, p.contact_ref, p.display_name - FROM contacts AS cs - JOIN contact_connections AS cc ON cs.contact_id = cc.contact_id - JOIN contact_connections AS ac ON cs.contact_id = ac.contact_id - JOIN connections AS c ON c.connection_id = cc.connection_id - JOIN connections AS a ON a.connection_id = ac.connection_id - LEFT JOIN contact_profiles AS p ON p.contact_profile_id = cs.contact_profile_id - WHERE cs.user_id = :user_id - AND c.agent_conn_id = :agent_conn_id - AND ac.active = 1 - |] - [":user_id" := userId, ":agent_conn_id" := agentConnId] + liftIOEither . withTransaction st $ \db -> do + getConnection db >>= \case + Left e -> pure $ Left e + Right c@Connection {connType, entityId} -> case connType of + ConnMember -> pure . Left $ SEInternal "group members not supported yet" + ConnContact -> + ReceivedDirectMessage <$$> case entityId of + Nothing -> pure $ Right NewContact {activeConn = c} + Just cId -> getContact db cId c where - chatDirection :: [ChatDirRow] -> Either StoreError (ChatDirection 'Agent) - chatDirection [d] = Right $ toChatDirection agentConnId d - chatDirection _ = Left SEConnectionNotFound - -type ChatDirRow = (Int64, Text, Int64, Int, Maybe Int64, ConnStatus, Maybe Int64, Maybe ContactRef, Maybe Text) - -toChatDirection :: ConnId -> ChatDirRow -> ChatDirection 'Agent -toChatDirection - agentConnId - (contactId, localContactRef, connId, connLevel, viaContact, connStatus, profileId, contactRef, displayName) = - let profile = Profile <$> profileId <*> contactRef <*> displayName - activeConn = Connection {connId, agentConnId, connLevel, viaContact, connStatus} - in ReceivedDirectMessage $ Contact {contactId, localContactRef, profile, activeConn} + getConnection db = + connection + <$> DB.query + db + [sql| + SELECT connection_id, agent_conn_id, conn_level, via_contact, + conn_status, conn_type, contact_id, group_member_id, created_at + FROM connections + WHERE user_id = ? AND agent_conn_id = ?; + |] + (userId, agentConnId) + connection (connRow : _) = Right $ toConnection connRow + connection _ = Left $ SEConnectionNotFound agentConnId + getContact db contactId c = + toContact contactId c + <$> DB.query + db + [sql| + SELECT c.local_contact_ref, p.contact_ref, p.display_name + FROM contacts c + JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id + WHERE c.user_id = ? AND c.contact_id = ? + |] + (userId, contactId) + toContact contactId c [(localContactRef, contactRef, displayName)] = + let profile = Profile {contactRef, displayName} + in Right Contact {contactId, localContactRef, profile, activeConn = c} + toContact _ _ _ = Left $ SEInternal "referenced contact not found" data StoreError - = SEContactNotFound ContactRef - | SEConnectionNotFound + = SEDuplicateContactRef + | SEContactNotFound ContactRef + | SEConnectionNotFound ConnId | SEInternal ByteString deriving (Show, Exception) diff --git a/apps/simplex-chat/Simplex/View.hs b/apps/simplex-chat/Simplex/View.hs index ab3c980de1..26579c7be7 100644 --- a/apps/simplex-chat/Simplex/View.hs +++ b/apps/simplex-chat/Simplex/View.hs @@ -19,7 +19,6 @@ where import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.ByteString.Char8 (ByteString) -import Data.Composition ((.:)) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (DiffTime, UTCTime) @@ -36,8 +35,8 @@ import System.Console.ANSI.Types type ChatReader m = (MonadUnliftIO m, MonadReader ChatController m) -showInvitation :: ChatReader m => ContactRef -> SMPQueueInfo -> m () -showInvitation = printToView .: invitation +showInvitation :: ChatReader m => SMPQueueInfo -> m () +showInvitation = printToView . invitation showChatError :: ChatReader m => ChatError -> m () showChatError = printToView . chatError @@ -57,9 +56,9 @@ showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage showSentMessage :: ChatReader m => ContactRef -> ByteString -> m () showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) -invitation :: ContactRef -> SMPQueueInfo -> [StyledString] -invitation c qInfo = - [ "pass this invitation to your contact " <> ttyContact c <> " (via any channel): ", +invitation :: SMPQueueInfo -> [StyledString] +invitation qInfo = + [ "pass this invitation to your contact (via another channel): ", "", (bPlain . serializeSmpQueueInfo) qInfo, "", @@ -117,12 +116,13 @@ chatError :: ChatError -> [StyledString] chatError = \case ChatErrorContact e -> case e of CENotFound c -> ["no contact " <> ttyContact c] - ChatErrorAgent c err -> case err of - CONN e -> case e of - -- TODO replace with ChatErrorContact errors, these errors should never happen - NOT_FOUND -> ["no contact " <> ttyContact c] - DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] - SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] + CEProfile s -> ["invalid profile: " <> plain s] + ChatErrorAgent err -> case err of + -- CONN e -> case e of + -- -- TODO replace with ChatErrorContact errors, these errors should never happen + -- NOT_FOUND -> ["no contact " <> ttyContact c] + -- DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] + -- SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] e -> ["smp agent error: " <> plain (show e)] e -> ["chat error: " <> plain (show e)] diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index 22fcae5b22..aa35c77b01 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -1,21 +1,29 @@ CREATE TABLE contact_profiles ( -- remote user profile contact_profile_id INTEGER PRIMARY KEY, contact_ref TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces - display_name TEXT NOT NULL DEFAULT '', + display_name TEXT NOT NULL, properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties ); --- the first record (id = 1) is reserved for the first local user -INSERT INTO contact_profiles (contact_profile_id, contact_ref) VALUES (1, ''); - - CREATE TABLE users ( user_id INTEGER PRIMARY KEY, - contact_profile_id INTEGER NOT NULL UNIQUE REFERENCES contact_profiles -- user's profile + contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE + DEFERRABLE INITIALLY DEFERRED, + active_user INTEGER -- 1 for active user ); --- the first record (id = 1) is reserved for the first local user -INSERT INTO users (user_id, contact_profile_id) VALUES (1, 1); +CREATE TABLE contacts ( + contact_id INTEGER PRIMARY KEY, + contact_profile_id INTEGER UNIQUE REFERENCES contact_profiles, -- NULL if it's an incognito profile + local_contact_ref TEXT NOT NULL, + lcr_base TEXT NOT NULL, + lcr_suffix INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL REFERENCES users, + user INTEGER, -- 1 if this contact is a user + created_at TEXT NOT NULL DEFAULT (datetime('now')), + UNIQUE (user_id, local_contact_ref) ON CONFLICT FAIL, + UNIQUE (user_id, lcr_base, lcr_suffix) ON CONFLICT FAIL +); CREATE TABLE known_servers( server_id INTEGER PRIMARY KEY, @@ -26,36 +34,13 @@ CREATE TABLE known_servers( UNIQUE (user_id, host, port) ) WITHOUT ROWID; -CREATE TABLE contacts ( - contact_id INTEGER PRIMARY KEY, - local_contact_ref TEXT NOT NULL UNIQUE, -- contact name set by local user - must be unique - local_properties TEXT NOT NULL DEFAULT '{}', -- JSON set by local user - contact_profile_id INTEGER UNIQUE REFERENCES contact_profiles, -- profile sent by remote contact, NULL for incognito contacts - user_id INTEGER NOT NULL REFERENCES users -); - -CREATE TABLE connections ( -- all SMP agent connections - connection_id INTEGER PRIMARY KEY, - agent_conn_id BLOB NOT NULL UNIQUE, - conn_level INTEGER NOT NULL DEFAULT 0, - via_contact INTEGER REFERENCES contacts (contact_id), - conn_status TEXT NOT NULL, - user_id INTEGER NOT NULL REFERENCES users -); - -CREATE TABLE contact_connections ( -- connections only for direct messages, many per contact - connection_id INTEGER NOT NULL UNIQUE REFERENCES connections ON DELETE CASCADE, - contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, -- connection must be removed first via the agent - active INTEGER NOT NULL DEFAULT 0 -); - -CREATE TABLE contact_invitations ( - invitation_id INTEGER PRIMARY KEY, - agent_inv_id BLOB UNIQUE, - invitation TEXT, - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, - invitation_status TEXT NOT NULL DEFAULT '' -); +-- CREATE TABLE contact_invitations ( +-- invitation_id INTEGER PRIMARY KEY, +-- agent_inv_id BLOB UNIQUE, +-- invitation TEXT, +-- contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, +-- invitation_status TEXT NOT NULL DEFAULT '' +-- ); CREATE TABLE group_profiles ( -- shared group profiles group_profile_id INTEGER PRIMARY KEY, @@ -71,8 +56,6 @@ CREATE TABLE groups ( local_group_ref TEXT NOT NULL UNIQUE, -- local group name without spaces local_properties TEXT NOT NULL, -- local JSON group properties group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile - user_group_member_details_id INTEGER NOT NULL - REFERENCES group_member_details (group_member_details_id) ON DELETE RESTRICT, user_id INTEGER NOT NULL REFERENCES users, UNIQUE (invited_by, external_group_id) ); @@ -80,21 +63,27 @@ CREATE TABLE groups ( CREATE TABLE group_members ( -- group members, excluding the local user group_member_id INTEGER PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, - group_member_details_id INTEGER NOT NULL REFERENCES group_member_details ON DELETE RESTRICT, - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, - connection_id INTEGER UNIQUE REFERENCES connections -); - -CREATE TABLE group_member_details ( - group_member_details_id INTEGER PRIMARY KEY, - group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, member_id BLOB NOT NULL, -- shared member ID, unique per group member_role TEXT NOT NULL DEFAULT '', -- owner, admin, moderator, '' member_status TEXT NOT NULL DEFAULT '', -- inv | con | full | off - invited_by INTEGER REFERENCES contacts ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator + invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, UNIQUE (group_id, member_id) ); +CREATE TABLE connections ( -- all SMP agent connections + connection_id INTEGER PRIMARY KEY, + agent_conn_id BLOB NOT NULL UNIQUE, + conn_level INTEGER NOT NULL DEFAULT 0, + via_contact INTEGER REFERENCES contacts (contact_id), + conn_status TEXT NOT NULL, + conn_type TEXT NOT NULL, -- contact, member + contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, + group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + user_id INTEGER NOT NULL REFERENCES users +); + CREATE TABLE events ( -- messages received by the agent, append only event_id INTEGER PRIMARY KEY, agent_msg_id INTEGER NOT NULL, -- internal message ID diff --git a/package.yaml b/package.yaml index e5991df418..2f42705dbf 100644 --- a/package.yaml +++ b/package.yaml @@ -12,6 +12,7 @@ extra-source-files: - README.md dependencies: + - aeson == 1.5.* - ansi-terminal == 0.10.* - attoparsec == 0.13.* - base >= 4.7 && < 5 @@ -21,6 +22,7 @@ dependencies: - simplexmq == 0.3.* - sqlite-simple == 0.4.* - text == 1.2.* + - time == 1.9.* library: source-dirs: src diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 6cf6a44c3f..6fa3d3823d 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -1,30 +1,39 @@ +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module Simplex.Chat.Types where +import Data.Aeson (FromJSON, ToJSON) +import qualified Data.Aeson as J import Data.ByteString.Char8 (ByteString) import Data.Int (Int64) import Data.Text (Text) +import Data.Time.Clock (UTCTime) import Database.SQLite.Simple.FromField (FromField (..)) import Database.SQLite.Simple.ToField (ToField (..)) +import GHC.Generics import Simplex.Messaging.Agent.Protocol (ConnId) import Simplex.Messaging.Agent.Store.SQLite (fromTextField_) data User = User { userId :: UserId, - profile :: Profile + localContactRef :: ContactRef, + profile :: Profile, + activeUser :: Bool } type UserId = Int64 -data Contact = Contact - { contactId :: Int64, - localContactRef :: ContactRef, - profile :: Maybe Profile, - activeConn :: Connection - } +data Contact + = Contact + { contactId :: Int64, + localContactRef :: ContactRef, + profile :: Profile, + activeConn :: Connection + } + | NewContact {activeConn :: Connection} deriving (Eq, Show) type ContactRef = Text @@ -36,18 +45,24 @@ data Group = Group deriving (Eq, Show) data Profile = Profile - { profileId :: Int64, - contactRef :: ContactRef, + { contactRef :: ContactRef, displayName :: Text } - deriving (Eq, Show) + deriving (Generic, Eq, Show) + +instance ToJSON Profile where toEncoding = J.genericToEncoding J.defaultOptions + +instance FromJSON Profile data Connection = Connection { connId :: Int64, agentConnId :: ConnId, connLevel :: Int, viaContact :: Maybe Int64, - connStatus :: ConnStatus + connType :: ConnType, + connStatus :: ConnStatus, + entityId :: Maybe Int64, -- contact or group member ID + createdAt :: UTCTime } deriving (Eq, Show) @@ -60,18 +75,36 @@ instance ToField ConnStatus where toField = toField . serializeConnStatus connStatusT :: Text -> Maybe ConnStatus connStatusT = \case - "NEW" -> Just ConnNew - "CONF" -> Just ConnConfirmed - "ACPT" -> Just ConnAccepted - "READY" -> Just ConnReady + "new" -> Just ConnNew + "confirmed" -> Just ConnConfirmed + "accepted" -> Just ConnAccepted + "ready" -> Just ConnReady _ -> Nothing serializeConnStatus :: ConnStatus -> Text serializeConnStatus = \case - ConnNew -> "NEW" - ConnConfirmed -> "CONF" - ConnAccepted -> "ACPT" - ConnReady -> "READY" + ConnNew -> "new" + ConnConfirmed -> "confirmed" + ConnAccepted -> "accepted" + ConnReady -> "ready" + +data ConnType = ConnContact | ConnMember + deriving (Eq, Show) + +instance FromField ConnType where fromField = fromTextField_ connTypeT + +instance ToField ConnType where toField = toField . serializeConnType + +connTypeT :: Text -> Maybe ConnType +connTypeT = \case + "contact" -> Just ConnContact + "member" -> Just ConnMember + _ -> Nothing + +serializeConnType :: ConnType -> Text +serializeConnType = \case + ConnContact -> "contact" + ConnMember -> "member" data NewConnection = NewConnection { agentConnId :: ByteString, From 85727bfbf1ca2e76257dffa200993062f481482d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:05:07 +0100 Subject: [PATCH 05/29] move files to src folder (to allow testing) (#70) --- apps/simplex-chat/{Simplex => }/Demo.hs | 2 +- apps/simplex-chat/Main.hs | 12 +++++----- package.yaml | 22 +++++++------------ {apps/simplex-chat => src}/Simplex/Chat.hs | 12 +++++----- .../Simplex/Chat/Controller.hs | 6 ++--- .../Simplex => src/Simplex/Chat}/Help.hs | 2 +- .../Simplex => src/Simplex/Chat}/Input.hs | 4 ++-- .../Simplex/Chat}/Notification.hs | 2 +- .../Simplex/Chat/Options.hs | 2 +- .../Simplex => src/Simplex/Chat}/Store.hs | 2 +- .../Simplex => src/Simplex/Chat}/Terminal.hs | 2 +- .../Simplex => src/Simplex/Chat}/Util.hs | 2 +- .../Simplex => src/Simplex/Chat}/View.hs | 6 ++--- 13 files changed, 35 insertions(+), 41 deletions(-) rename apps/simplex-chat/{Simplex => }/Demo.hs (99%) rename {apps/simplex-chat => src}/Simplex/Chat.hs (98%) rename {apps/simplex-chat => src}/Simplex/Chat/Controller.hs (95%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/Help.hs (98%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/Input.hs (98%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/Notification.hs (97%) rename apps/simplex-chat/ChatOptions.hs => src/Simplex/Chat/Options.hs (96%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/Store.hs (99%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/Terminal.hs (99%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/Util.hs (87%) rename {apps/simplex-chat/Simplex => src/Simplex/Chat}/View.hs (98%) diff --git a/apps/simplex-chat/Simplex/Demo.hs b/apps/simplex-chat/Demo.hs similarity index 99% rename from apps/simplex-chat/Simplex/Demo.hs rename to apps/simplex-chat/Demo.hs index 4bc5cb9e69..2fff0b0a32 100644 --- a/apps/simplex-chat/Simplex/Demo.hs +++ b/apps/simplex-chat/Demo.hs @@ -1,7 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -module Simplex.Demo where +module Demo where import Simplex.Chat.Styled import System.Console.ANSI.Types diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index 8d24c0d4d2..fd788bf5d4 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -5,20 +5,20 @@ module Main where -import ChatOptions -import Control.Concurrent.STM +import Control.Concurrent.STM (atomically) import Control.Logger.Simple import Control.Monad.IO.Unlift import Control.Monad.Reader import Simplex.Chat import Simplex.Chat.Controller -import Simplex.Input +import Simplex.Chat.Input +import Simplex.Chat.Notification +import Simplex.Chat.Options +import Simplex.Chat.Store (createStore) +import Simplex.Chat.Terminal import Simplex.Messaging.Agent (getSMPAgentClient) import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Client (smpDefaultConfig) -import Simplex.Notification -import Simplex.Store (createStore) -import Simplex.Terminal import System.Directory (getAppUserDataDirectory) import UnliftIO.Async (race_) diff --git a/package.yaml b/package.yaml index 2f42705dbf..1dc6b766d7 100644 --- a/package.yaml +++ b/package.yaml @@ -18,11 +18,19 @@ dependencies: - base >= 4.7 && < 5 - bytestring == 0.10.* - containers == 0.6.* + - directory == 1.3.* + - file-embed == 0.0.14.* + - filepath == 1.4.* - mtl == 2.2.* + - optparse-applicative == 0.15.* + - process == 1.6.* - simplexmq == 0.3.* - sqlite-simple == 0.4.* + - terminal == 0.2.* - text == 1.2.* - time == 1.9.* + - unliftio == 0.2.* + - unliftio-core == 0.2.* library: source-dirs: src @@ -33,24 +41,10 @@ executables: main: Main.hs dependencies: - simplex-chat - - aeson == 1.5.* - async == 2.2.* - - bytestring == 0.10.* - - composition == 1.0.* - - directory == 1.3.* - - file-embed == 0.0.14.* - - filepath == 1.4.* - - mtl == 2.2.* - - optparse-applicative == 0.15.* - - process == 1.6.* - simple-logger == 0.1.* - simplexmq == 0.3.* - - sqlite-simple == 0.4.* - stm == 2.5.* - - terminal == 0.2.* - - time == 1.9.* - - unliftio == 0.2.* - - unliftio-core == 0.2.* ghc-options: - -threaded diff --git a/apps/simplex-chat/Simplex/Chat.hs b/src/Simplex/Chat.hs similarity index 98% rename from apps/simplex-chat/Simplex/Chat.hs rename to src/Simplex/Chat.hs index 25a3b340d8..a025e46caf 100644 --- a/apps/simplex-chat/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -27,18 +27,18 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Controller +import Simplex.Chat.Help +import Simplex.Chat.Notification import Simplex.Chat.Protocol +import Simplex.Chat.Store import Simplex.Chat.Styled (plain) +import Simplex.Chat.Terminal import Simplex.Chat.Types -import Simplex.Help +import Simplex.Chat.View import Simplex.Messaging.Agent import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow, raceAny_) -import Simplex.Notification -import Simplex.Store -import Simplex.Terminal -import Simplex.View import System.Exit (exitFailure) import System.IO (hFlush, stdout) import Text.Read (readMaybe) @@ -207,7 +207,7 @@ getCreateActiveUser st = do liftIO $ setActiveUser st (userId user) pure user selectUser users = do - putStrLn "Select user profile: " + putStrLn "Select user profile:" forM_ (zip [1 ..] users) $ \(n :: Int, user) -> putStrLn $ show n <> " - " <> userStr user loop where diff --git a/apps/simplex-chat/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs similarity index 95% rename from apps/simplex-chat/Simplex/Chat/Controller.hs rename to src/Simplex/Chat/Controller.hs index ddd0850bc3..4aba3cdb9e 100644 --- a/apps/simplex-chat/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -12,14 +12,14 @@ import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader import Numeric.Natural +import Simplex.Chat.Notification import Simplex.Chat.Protocol +import Simplex.Chat.Store (StoreError) +import Simplex.Chat.Terminal import Simplex.Chat.Types import Simplex.Messaging.Agent (AgentClient) import Simplex.Messaging.Agent.Protocol (AgentErrorType) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) -import Simplex.Notification -import Simplex.Store (StoreError) -import Simplex.Terminal import UnliftIO.STM data ChatController = ChatController diff --git a/apps/simplex-chat/Simplex/Help.hs b/src/Simplex/Chat/Help.hs similarity index 98% rename from apps/simplex-chat/Simplex/Help.hs rename to src/Simplex/Chat/Help.hs index a508b1a76f..d3dbd9606b 100644 --- a/apps/simplex-chat/Simplex/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -module Simplex.Help where +module Simplex.Chat.Help where import Data.List (intersperse) import Simplex.Chat.Markdown diff --git a/apps/simplex-chat/Simplex/Input.hs b/src/Simplex/Chat/Input.hs similarity index 98% rename from apps/simplex-chat/Simplex/Input.hs rename to src/Simplex/Chat/Input.hs index 9984597179..8da7c679f8 100644 --- a/apps/simplex-chat/Simplex/Input.hs +++ b/src/Simplex/Chat/Input.hs @@ -2,14 +2,14 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} -module Simplex.Input where +module Simplex.Chat.Input where import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.List (dropWhileEnd) import qualified Data.Text as T import Simplex.Chat.Controller -import Simplex.Terminal +import Simplex.Chat.Terminal import System.Exit (exitSuccess) import System.Terminal hiding (insertChars) import UnliftIO.STM diff --git a/apps/simplex-chat/Simplex/Notification.hs b/src/Simplex/Chat/Notification.hs similarity index 97% rename from apps/simplex-chat/Simplex/Notification.hs rename to src/Simplex/Chat/Notification.hs index 6d96eb0d89..718d3c18f5 100644 --- a/apps/simplex-chat/Simplex/Notification.hs +++ b/src/Simplex/Chat/Notification.hs @@ -2,7 +2,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} -module Simplex.Notification (Notification (..), initializeNotifications) where +module Simplex.Chat.Notification (Notification (..), initializeNotifications) where import Control.Monad (void) import Data.Char (toLower) diff --git a/apps/simplex-chat/ChatOptions.hs b/src/Simplex/Chat/Options.hs similarity index 96% rename from apps/simplex-chat/ChatOptions.hs rename to src/Simplex/Chat/Options.hs index b646d66d02..73538aa113 100644 --- a/apps/simplex-chat/ChatOptions.hs +++ b/src/Simplex/Chat/Options.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -module ChatOptions (getChatOpts, ChatOpts (..)) where +module Simplex.Chat.Options (getChatOpts, ChatOpts (..)) where import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Char8 as B diff --git a/apps/simplex-chat/Simplex/Store.hs b/src/Simplex/Chat/Store.hs similarity index 99% rename from apps/simplex-chat/Simplex/Store.hs rename to src/Simplex/Chat/Store.hs index 16ee8a874e..0c80bd4a5f 100644 --- a/apps/simplex-chat/Simplex/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -9,7 +9,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} -module Simplex.Store +module Simplex.Chat.Store ( SQLiteStore, StoreError (..), createStore, diff --git a/apps/simplex-chat/Simplex/Terminal.hs b/src/Simplex/Chat/Terminal.hs similarity index 99% rename from apps/simplex-chat/Simplex/Terminal.hs rename to src/Simplex/Chat/Terminal.hs index b8e77ea9c7..5451846665 100644 --- a/apps/simplex-chat/Simplex/Terminal.hs +++ b/src/Simplex/Chat/Terminal.hs @@ -2,7 +2,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} -module Simplex.Terminal where +module Simplex.Chat.Terminal where import Simplex.Chat.Styled import Simplex.Chat.Types diff --git a/apps/simplex-chat/Simplex/Util.hs b/src/Simplex/Chat/Util.hs similarity index 87% rename from apps/simplex-chat/Simplex/Util.hs rename to src/Simplex/Chat/Util.hs index 29c7a1568c..d2fe0c3d40 100644 --- a/apps/simplex-chat/Simplex/Util.hs +++ b/src/Simplex/Chat/Util.hs @@ -1,4 +1,4 @@ -module Simplex.Util where +module Simplex.Chat.Util where import Data.ByteString.Char8 (ByteString) import Data.Text (Text) diff --git a/apps/simplex-chat/Simplex/View.hs b/src/Simplex/Chat/View.hs similarity index 98% rename from apps/simplex-chat/Simplex/View.hs rename to src/Simplex/Chat/View.hs index 26579c7be7..e305e09902 100644 --- a/apps/simplex-chat/Simplex/View.hs +++ b/src/Simplex/Chat/View.hs @@ -3,7 +3,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -module Simplex.View +module Simplex.Chat.View ( printToView, showInvitation, showChatError, @@ -27,10 +27,10 @@ import Data.Time.LocalTime (TimeZone, ZonedTime, getCurrentTimeZone, getZonedTim import Simplex.Chat.Controller import Simplex.Chat.Markdown import Simplex.Chat.Styled +import Simplex.Chat.Terminal (printToTerminal) import Simplex.Chat.Types +import Simplex.Chat.Util (safeDecodeUtf8) import Simplex.Messaging.Agent.Protocol -import Simplex.Terminal (printToTerminal) -import Simplex.Util (safeDecodeUtf8) import System.Console.ANSI.Types type ChatReader m = (MonadUnliftIO m, MonadReader ChatController m) From 25ac250d3759b7160d2366b742b4eae2fa431b77 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 6 Jul 2021 19:07:03 +0100 Subject: [PATCH 06/29] use chat message format to pass profile information, refactor (#71) --- src/Simplex/Chat.hs | 117 +++++++++++++++++---------------- src/Simplex/Chat/Controller.hs | 7 +- src/Simplex/Chat/Protocol.hs | 55 +++++++--------- src/Simplex/Chat/Store.hs | 35 +++++----- 4 files changed, 103 insertions(+), 111 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index a025e46caf..981c2e608d 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} @@ -18,7 +19,6 @@ import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (first) import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as LB import Data.Functor (($>)) import Data.List (find) @@ -59,7 +59,6 @@ runChatController = raceAny_ [ inputSubscriber, agentSubscriber, - chatSubscriber, notificationSubscriber ] @@ -77,9 +76,7 @@ inputSubscriber = do SendMessage c msg -> showSentMessage c msg _ -> printToView [plain s] user <- asks currentUser - runExceptT (processChatCommand user cmd) >>= \case - Left e -> showChatError e - _ -> pure () + void . runExceptT $ processChatCommand user cmd `catchError` showChatError processChatCommand :: ChatMonad m => User -> ChatCommand -> m () processChatCommand User {userId, profile} = \case @@ -90,7 +87,7 @@ processChatCommand User {userId, profile} = \case withStore $ \st -> createDirectConnection st userId connId showInvitation qInfo Connect qInfo -> do - connId <- withAgent $ \agent -> joinConnection agent qInfo $ LB.toStrict (J.encode profile) + connId <- withAgent $ \agent -> joinConnection agent qInfo $ encodeProfile profile withStore $ \st -> createDirectConnection st userId connId DeleteContact cRef -> do conns <- withStore $ \st -> getContactConnections st userId cRef @@ -109,35 +106,22 @@ processChatCommand User {userId, profile} = \case agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do - aQ <- asks $ subQ . smpAgent - cQ <- asks chatQ + q <- asks $ subQ . smpAgent + -- cQ <- asks chatQ forever $ do - (_, agentConnId, resp) <- atomically (readTBQueue aQ) - User {userId} <- asks currentUser - runExceptT (withStore $ \st -> getConnectionChatDirection st userId agentConnId) >>= \case - -- TODO handle errors - Left e -> liftIO $ print e - Right chatDirection -> do - case resp of - MSG agentMsgMeta msgBody -> do - atomically . writeTBQueue cQ $ - case first B.pack (parseAll rawChatMessageP msgBody) >>= toChatMessage of - Right chatMessage -> ChatTransmission {agentMsgMeta, chatDirection, chatMessage} - Left msgError -> ChatTransmissionError {agentMsgMeta, chatDirection, msgBody, msgError} - agentMessage -> - atomically $ writeTBQueue cQ AgentTransmission {agentConnId, chatDirection, agentMessage} + (_, connId, msg) <- atomically $ readTBQueue q + user <- asks currentUser + -- TODO handle errors properly + void . runExceptT $ processAgentMessage user connId msg `catchError` (liftIO . print) -chatSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () -chatSubscriber = do - cQ <- asks chatQ - forever $ do - User {userId, profile} <- asks currentUser - atomically (readTBQueue cQ) >>= \case - ChatTransmission - { agentMsgMeta = meta, - chatDirection = ReceivedDirectMessage Contact {localContactRef = c}, - chatMessage = ChatMessage {chatMsgEvent, chatMsgBody} - } -> +processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () +processAgentMessage User {userId, profile} agentConnId agentMessage = do + chatDirection <- withStore $ \st -> getConnectionChatDirection st userId agentConnId + case chatDirection of + ReceivedDirectMessage Contact {localContactRef = c} -> + case agentMessage of + MSG meta msgBody -> do + ChatMessage {chatMsgEvent, chatMsgBody} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of XMsgNew MTText -> do case find (isSimplexContentType XCText) chatMsgBody of @@ -147,33 +131,50 @@ chatSubscriber = do showToast ("@" <> c) text setActive $ ActiveC c _ -> pure () + XInfo -> pure () -- TODO profile update _ -> pure () - AgentTransmission {agentConnId, chatDirection = ReceivedDirectMessage NewContact {activeConn}, agentMessage} -> - void . runExceptT $ case agentMessage of - CONF confId connInfo -> do - -- TODO update connection status - saveContact userId activeConn connInfo - withAgent $ \a -> allowConnection a agentConnId confId $ LB.toStrict (J.encode profile) - INFO connInfo -> - saveContact userId activeConn connInfo - _ -> pure () - AgentTransmission {chatDirection = ReceivedDirectMessage Contact {localContactRef = c}, agentMessage} -> - case agentMessage of - CON -> do - -- TODO update connection status - showContactConnected c - showToast ("@" <> c) "connected" - setActive $ ActiveC c - END -> do - showContactDisconnected c - showToast ("@" <> c) "disconnected" - unsetActive $ ActiveC c - _ -> pure () - _ -> pure () + CON -> do + -- TODO update connection status + showContactConnected c + showToast ("@" <> c) "connected" + setActive $ ActiveC c + END -> do + showContactDisconnected c + showToast ("@" <> c) "disconnected" + unsetActive $ ActiveC c + _ -> pure () + ReceivedDirectMessage NewContact {activeConn} -> + case agentMessage of + CONF confId connInfo -> do + -- TODO update connection status + saveConnInfo activeConn connInfo + withAgent $ \a -> allowConnection a agentConnId confId $ encodeProfile profile + INFO connInfo -> + saveConnInfo activeConn connInfo + _ -> pure () + _ -> pure () where - saveContact userId activeConn connInfo = do - p <- liftEither . first (ChatErrorContact . CEProfile) $ J.eitherDecodeStrict' connInfo - withStore $ \st -> createDirectContact st userId activeConn p + parseChatMessage :: ByteString -> Either ChatError ChatMessage + parseChatMessage msgBody = first ChatErrorMessage (parseAll rawChatMessageP msgBody >>= toChatMessage) + + saveConnInfo :: Connection -> ConnInfo -> m () + saveConnInfo activeConn connInfo = do + ChatMessage {chatMsgEvent, chatMsgBody} <- liftEither $ parseChatMessage connInfo + case chatMsgEvent of + XInfo -> + case find (isSimplexContentType XCJson) chatMsgBody of + Just MsgBodyContent {contentData = MBFull (MsgData bs)} -> do + p <- liftEither . first (ChatErrorContact . CEProfile) $ J.eitherDecodeStrict' bs + withStore $ \st -> createDirectContact st userId activeConn p + _ -> pure () -- TODO show/log error? + _ -> pure () -- TODO show/log error, other events in SMP confirmation + +encodeProfile :: Profile -> ByteString +encodeProfile profile = + let json = LB.toStrict $ J.encode profile + body = MsgBodyContent {contentType = SimplexContentType XCJson, contentHash = Nothing, contentData = MBFull $ MsgData json} + chatMsg = ChatMessage {chatMsgId = Nothing, chatMsgEvent = XInfo, chatMsgBody = [body], chatDAGIdx = Nothing} + in serializeRawChatMessage $ rawChatMessage chatMsg getCreateActiveUser :: SQLiteStore -> IO User getCreateActiveUser st = do diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 4aba3cdb9e..fb08a64f60 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -13,7 +13,6 @@ import Control.Monad.IO.Unlift import Control.Monad.Reader import Numeric.Natural import Simplex.Chat.Notification -import Simplex.Chat.Protocol import Simplex.Chat.Store (StoreError) import Simplex.Chat.Terminal import Simplex.Chat.Types @@ -27,7 +26,6 @@ data ChatController = ChatController smpAgent :: AgentClient, chatTerminal :: ChatTerminal, chatStore :: SQLiteStore, - chatQ :: TBQueue ChatTransmission, inputQ :: TBQueue InputEvent, notifyQ :: TBQueue Notification, sendNotification :: Notification -> IO () @@ -37,6 +35,7 @@ data InputEvent = InputCommand String | InputControl Char data ChatError = ChatErrorContact ContactError + | ChatErrorMessage String | ChatErrorAgent AgentErrorType | ChatErrorStore StoreError deriving (Show, Exception) @@ -50,8 +49,8 @@ newChatController :: AgentClient -> ChatTerminal -> SQLiteStore -> User -> (Noti newChatController smpAgent chatTerminal chatStore currentUser sendNotification qSize = do inputQ <- newTBQueue qSize notifyQ <- newTBQueue qSize - chatQ <- newTBQueue qSize - pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, chatQ, inputQ, notifyQ, sendNotification} + -- chatQ <- newTBQueue qSize + pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, inputQ, notifyQ, sendNotification} setActive :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () setActive to = asks (activeTo . chatTerminal) >>= atomically . (`writeTVar` to) diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 5f1c69ebbc..6474b3cf8d 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -21,28 +21,8 @@ import Data.Text (Text) import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Parsers (base64P) -import Simplex.Messaging.Protocol (MsgBody) import Simplex.Messaging.Util (bshow) -data ChatTransmission - = ChatTransmission - { agentMsgMeta :: MsgMeta, - chatDirection :: ChatDirection 'Agent, - chatMessage :: ChatMessage - } - | ChatTransmissionError - { agentMsgMeta :: MsgMeta, - chatDirection :: ChatDirection 'Agent, - msgBody :: MsgBody, - msgError :: ByteString - } - | AgentTransmission - { agentConnId :: ConnId, - chatDirection :: ChatDirection 'Agent, - agentMessage :: ACommand 'Agent - } - deriving (Eq, Show) - data ChatDirection (p :: AParty) where ReceivedDirectMessage :: Contact -> ChatDirection 'Agent SentDirectMessage :: Contact -> ChatDirection 'Client @@ -53,16 +33,18 @@ deriving instance Eq (ChatDirection p) deriving instance Show (ChatDirection p) -newtype ChatMsgEvent = XMsgNew MessageType +data ChatMsgEvent + = XMsgNew MessageType + | XInfo deriving (Eq, Show) data MessageType = MTText | MTImage deriving (Eq, Show) -toMsgType :: ByteString -> Either ByteString MessageType +toMsgType :: ByteString -> Either String MessageType toMsgType = \case "c.text" -> Right MTText "c.image" -> Right MTImage - t -> Left $ "invalid message type " <> t + t -> Left $ "invalid message type " <> B.unpack t rawMsgType :: MessageType -> ByteString rawMsgType = \case @@ -77,16 +59,20 @@ data ChatMessage = ChatMessage } deriving (Eq, Show) -toChatMessage :: RawChatMessage -> Either ByteString ChatMessage +toChatMessage :: RawChatMessage -> Either String ChatMessage toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} = do body <- mapM toMsgBodyContent chatMsgBody + let chatDAGIdx = findDAG body case chatMsgEvent of "x.msg.new" -> case chatMsgParams of [mt] -> do t <- toMsgType mt - pure ChatMessage {chatMsgId, chatMsgEvent = XMsgNew t, chatMsgBody = body, chatDAGIdx = findDAG body} + pure ChatMessage {chatMsgId, chatMsgEvent = XMsgNew t, chatMsgBody = body, chatDAGIdx} _ -> throwError "x.msg.new expects one parameter" - _ -> throwError $ "unsupported event " <> chatMsgEvent + "x.info" -> case chatMsgParams of + [] -> pure ChatMessage {chatMsgId, chatMsgEvent = XInfo, chatMsgBody = body, chatDAGIdx} + _ -> throwError "x.info expects no parameters" + _ -> throwError $ "unsupported event " <> B.unpack chatMsgEvent toChatMessage _ = Left "message continuation" findDAG :: [MsgBodyContent] -> Maybe Int @@ -101,11 +87,12 @@ isSimplexContentType = isContentType . SimplexContentType rawChatMessage :: ChatMessage -> RawChatMessage rawChatMessage ChatMessage {chatMsgId, chatMsgEvent = event, chatMsgBody = body} = case event of - XMsgNew t -> - let chatMsgBody = map rawMsgBodyContent body - in RawChatMessage {chatMsgId, chatMsgEvent = "x.msg.new", chatMsgParams = [rawMsgType t], chatMsgBody} + XMsgNew t -> RawChatMessage {chatMsgId, chatMsgEvent = "x.msg.new", chatMsgParams = [rawMsgType t], chatMsgBody} + XInfo -> RawChatMessage {chatMsgId, chatMsgEvent = "x.info", chatMsgParams = [], chatMsgBody} + where + chatMsgBody = map rawMsgBodyContent body -toMsgBodyContent :: RawMsgBodyContent -> Either ByteString MsgBodyContent +toMsgBodyContent :: RawMsgBodyContent -> Either String MsgBodyContent toMsgBodyContent RawMsgBodyContent {contentType, contentHash, contentData} = do cType <- toContentType contentType pure MsgBodyContent {contentType = cType, contentHash, contentData} @@ -127,15 +114,16 @@ data ContentType | SimplexDAG deriving (Eq, Show) -data XContentType = XCText | XCImage deriving (Eq, Show) +data XContentType = XCText | XCImage | XCJson deriving (Eq, Show) data MContentType = MCImageJPG | MCImagePNG deriving (Eq, Show) -toContentType :: RawContentType -> Either ByteString ContentType +toContentType :: RawContentType -> Either String ContentType toContentType (RawContentType ns cType) = case ns of "x" -> case cType of "text" -> Right $ SimplexContentType XCText "image" -> Right $ SimplexContentType XCImage + "json" -> Right $ SimplexContentType XCJson "dag" -> Right SimplexDAG _ -> err "m" -> case cType of @@ -144,13 +132,14 @@ toContentType (RawContentType ns cType) = case ns of _ -> err _ -> err where - err = Left $ "invalid content type " <> ns <> "." <> cType + err = Left . B.unpack $ "invalid content type " <> ns <> "." <> cType rawContentType :: ContentType -> RawContentType rawContentType t = case t of SimplexContentType t' -> RawContentType "x" $ case t' of XCText -> "text" XCImage -> "image" + XCJson -> "json" MimeContentType t' -> RawContentType "m" $ case t' of MCImageJPG -> "image/jpg" MCImagePNG -> "image/png" diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 0c80bd4a5f..86a2cb9200 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -170,22 +170,25 @@ createDirectContact st userId Connection {connId} Profile {contactRef, displayNa deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactRef -> m () deleteContact st userId contactRef = - liftIO . withTransaction st $ \db -> - forM_ - [ [sql| - DELETE FROM connections WHERE connection_id IN ( - SELECT connection_id - FROM connections c - JOIN contacts cs ON c.contact_id = cs.contact_id - WHERE cs.user_id = :user_id AND cs.local_contact_ref = :contact_ref - ); - |], - [sql| - DELETE FROM contacts - WHERE user_id = :user_id AND local_contact_ref = :contact_ref; - |] - ] - $ \q -> DB.executeNamed db q [":user_id" := userId, ":contact_ref" := contactRef] + liftIO . withTransaction st $ \db -> do + DB.executeNamed + db + [sql| + DELETE FROM connections WHERE connection_id IN ( + SELECT connection_id + FROM connections c + JOIN contacts cs ON c.contact_id = cs.contact_id + WHERE cs.user_id = :user_id AND cs.local_contact_ref = :contact_ref + ); + |] + [":user_id" := userId, ":contact_ref" := contactRef] + DB.executeNamed + db + [sql| + DELETE FROM contacts + WHERE user_id = :user_id AND local_contact_ref = :contact_ref; + |] + [":user_id" := userId, ":contact_ref" := contactRef] -- TODO return the last connection that is ready, not any last connection -- requires updating connection status From d21abbdec1f60bfa57847dbc30fba2607956d007 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 7 Jul 2021 22:46:38 +0100 Subject: [PATCH 07/29] chat test with VirtualTerminal (#72) * chat test with VirtualTerminal * disable chat test * fix intermittently failing test * simplify test --- apps/simplex-chat/Main.hs | 46 ++------------------- package.yaml | 5 ++- src/Simplex/Chat.hs | 62 +++++++++++++++++++++++----- src/Simplex/Chat/Controller.hs | 9 ----- src/Simplex/Chat/Input.hs | 7 ++-- src/Simplex/Chat/Terminal.hs | 36 +++++++++++++---- src/Simplex/Chat/View.hs | 2 +- tests/ChatClient.hs | 56 +++++++++++++++++++++++++ tests/ChatTests.hs | 74 ++++++++++++++++++++++++++++++++++ tests/Test.hs | 5 +++ 10 files changed, 230 insertions(+), 72 deletions(-) create mode 100644 tests/ChatClient.hs create mode 100644 tests/ChatTests.hs diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index fd788bf5d4..e618a0c3de 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -5,51 +5,16 @@ module Main where -import Control.Concurrent.STM (atomically) -import Control.Logger.Simple -import Control.Monad.IO.Unlift -import Control.Monad.Reader import Simplex.Chat -import Simplex.Chat.Controller -import Simplex.Chat.Input -import Simplex.Chat.Notification import Simplex.Chat.Options -import Simplex.Chat.Store (createStore) -import Simplex.Chat.Terminal -import Simplex.Messaging.Agent (getSMPAgentClient) -import Simplex.Messaging.Agent.Env.SQLite -import Simplex.Messaging.Client (smpDefaultConfig) import System.Directory (getAppUserDataDirectory) -import UnliftIO.Async (race_) - -cfg :: AgentConfig -cfg = - AgentConfig - { tcpPort = undefined, -- agent does not listen to TCP - smpServers = undefined, -- filled in from options - rsaKeySize = 2048 `div` 8, - connIdBytes = 12, - tbqSize = 16, - dbFile = "smp-chat.db", - dbPoolSize = 4, - smpCfg = smpDefaultConfig - } - -logCfg :: LogConfig -logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} +import System.Terminal (withTerminal) main :: IO () main = do - ChatOpts {dbFile, smpServers} <- welcomeGetOpts - st <- createStore (dbFile <> ".chat.db") 4 - user <- getCreateActiveUser st - ct <- newChatTerminal - a <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} - notify <- initializeNotifications - cc <- atomically $ newChatController a ct st user notify $ tbqSize cfg - -- setLogLevel LogInfo -- LogError - -- withGlobalLogging logCfg $ do - runReaderT simplexChat cc + opts <- welcomeGetOpts + t <- withTerminal pure + simplexChat opts t welcomeGetOpts :: IO ChatOpts welcomeGetOpts = do @@ -60,9 +25,6 @@ welcomeGetOpts = do putStrLn "type \"/help\" or \"/h\" for usage info" pure opts -simplexChat :: (MonadUnliftIO m, MonadReader ChatController m) => m () -simplexChat = race_ runTerminalInput runChatController - -- defaultSettings :: C.Size -> C.VirtualTerminalSettings -- defaultSettings size = -- C.VirtualTerminalSettings diff --git a/package.yaml b/package.yaml index 1dc6b766d7..a838be3c2f 100644 --- a/package.yaml +++ b/package.yaml @@ -19,11 +19,13 @@ dependencies: - bytestring == 0.10.* - containers == 0.6.* - directory == 1.3.* + - exceptions == 0.10.* - file-embed == 0.0.14.* - filepath == 1.4.* - mtl == 2.2.* - optparse-applicative == 0.15.* - process == 1.6.* + - simple-logger == 0.1.* - simplexmq == 0.3.* - sqlite-simple == 0.4.* - terminal == 0.2.* @@ -42,7 +44,6 @@ executables: dependencies: - simplex-chat - async == 2.2.* - - simple-logger == 0.1.* - simplexmq == 0.3.* - stm == 2.5.* ghc-options: @@ -54,7 +55,9 @@ tests: main: Test.hs dependencies: - simplex-chat + - async == 2.2.* - hspec == 2.7.* + - stm == 2.5.* ghc-options: # - -haddock diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 981c2e608d..51fbdbb11d 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -11,6 +11,7 @@ module Simplex.Chat where import Control.Applicative ((<|>)) +import Control.Logger.Simple import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader @@ -28,7 +29,9 @@ import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Controller import Simplex.Chat.Help +import Simplex.Chat.Input import Simplex.Chat.Notification +import Simplex.Chat.Options (ChatOpts (..)) import Simplex.Chat.Protocol import Simplex.Chat.Store import Simplex.Chat.Styled (plain) @@ -36,12 +39,15 @@ import Simplex.Chat.Terminal import Simplex.Chat.Types import Simplex.Chat.View import Simplex.Messaging.Agent +import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..)) import Simplex.Messaging.Agent.Protocol +import Simplex.Messaging.Client (smpDefaultConfig) import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow, raceAny_) import System.Exit (exitFailure) import System.IO (hFlush, stdout) import Text.Read (readMaybe) +import UnliftIO.Async (race_) import qualified UnliftIO.Exception as E import UnliftIO.STM @@ -54,6 +60,43 @@ data ChatCommand | SendMessage ContactRef ByteString deriving (Show) +cfg :: AgentConfig +cfg = + AgentConfig + { tcpPort = undefined, -- agent does not listen to TCP + smpServers = undefined, -- filled in from options + rsaKeySize = 2048 `div` 8, + connIdBytes = 12, + tbqSize = 16, + dbFile = undefined, -- filled in from options + dbPoolSize = 4, + smpCfg = smpDefaultConfig + } + +logCfg :: LogConfig +logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} + +simplexChat :: WithTerminal t => ChatOpts -> t -> IO () +simplexChat opts t = do + -- setLogLevel LogInfo -- LogError + -- withGlobalLogging logCfg $ do + initializeNotifications + >>= newChatController opts t + >>= runSimplexChat + +newChatController :: WithTerminal t => ChatOpts -> t -> (Notification -> IO ()) -> IO ChatController +newChatController ChatOpts {dbFile, smpServers} t sendNotification = do + chatStore <- createStore (dbFile <> ".chat.db") 4 + currentUser <- getCreateActiveUser chatStore + chatTerminal <- newChatTerminal t + smpAgent <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} + inputQ <- newTBQueueIO $ tbqSize cfg + notifyQ <- newTBQueueIO $ tbqSize cfg + pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, inputQ, notifyQ, sendNotification} + +runSimplexChat :: ChatController -> IO () +runSimplexChat = runReaderT (race_ runTerminalInput runChatController) + runChatController :: (MonadUnliftIO m, MonadReader ChatController m) => m () runChatController = raceAny_ @@ -107,7 +150,6 @@ processChatCommand User {userId, profile} = \case agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do q <- asks $ subQ . smpAgent - -- cQ <- asks chatQ forever $ do (_, connId, msg) <- atomically $ readTBQueue q user <- asks currentUser @@ -123,14 +165,7 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do MSG meta msgBody -> do ChatMessage {chatMsgEvent, chatMsgBody} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of - XMsgNew MTText -> do - case find (isSimplexContentType XCText) chatMsgBody of - Just MsgBodyContent {contentData = MBFull (MsgData bs)} -> do - let text = safeDecodeUtf8 bs - showReceivedMessage c (snd $ broker meta) text (integrity meta) - showToast ("@" <> c) text - setActive $ ActiveC c - _ -> pure () + XMsgNew MTText -> newTextMessage c meta $ find (isSimplexContentType XCText) chatMsgBody XInfo -> pure () -- TODO profile update _ -> pure () CON -> do @@ -154,6 +189,15 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do _ -> pure () _ -> pure () where + newTextMessage :: ContactRef -> MsgMeta -> Maybe MsgBodyContent -> m () + newTextMessage c meta = \case + Just MsgBodyContent {contentData = MBFull (MsgData bs)} -> do + let text = safeDecodeUtf8 bs + showReceivedMessage c (snd $ broker meta) text (integrity meta) + showToast ("@" <> c) text + setActive $ ActiveC c + _ -> pure () + parseChatMessage :: ByteString -> Either ChatError ChatMessage parseChatMessage msgBody = first ChatErrorMessage (parseAll rawChatMessageP msgBody >>= toChatMessage) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index fb08a64f60..9d39b28fca 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -2,7 +2,6 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Simplex.Chat.Controller where @@ -11,7 +10,6 @@ import Control.Exception import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader -import Numeric.Natural import Simplex.Chat.Notification import Simplex.Chat.Store (StoreError) import Simplex.Chat.Terminal @@ -45,13 +43,6 @@ data ContactError = CENotFound ContactRef | CEProfile String type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) -newChatController :: AgentClient -> ChatTerminal -> SQLiteStore -> User -> (Notification -> IO ()) -> Natural -> STM ChatController -newChatController smpAgent chatTerminal chatStore currentUser sendNotification qSize = do - inputQ <- newTBQueue qSize - notifyQ <- newTBQueue qSize - -- chatQ <- newTBQueue qSize - pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, inputQ, notifyQ, sendNotification} - setActive :: (MonadUnliftIO m, MonadReader ChatController m) => ActiveTo -> m () setActive to = asks (activeTo . chatTerminal) >>= atomically . (`writeTVar` to) diff --git a/src/Simplex/Chat/Input.hs b/src/Simplex/Chat/Input.hs index 8da7c679f8..7623c40d47 100644 --- a/src/Simplex/Chat/Input.hs +++ b/src/Simplex/Chat/Input.hs @@ -24,9 +24,10 @@ getKey = runTerminalInput :: (MonadUnliftIO m, MonadReader ChatController m) => m () runTerminalInput = do ChatController {inputQ, chatTerminal = ct} <- ask - liftIO . withTerminal . runTerminalT $ do - updateInput ct - receiveFromTTY inputQ ct + liftIO $ + withChatTerm ct $ do + updateInput ct + receiveFromTTY inputQ ct receiveFromTTY :: MonadTerminal m => TBQueue InputEvent -> ChatTerminal -> m () receiveFromTTY inputQ ct@ChatTerminal {activeTo, termSize, termState} = diff --git a/src/Simplex/Chat/Terminal.hs b/src/Simplex/Chat/Terminal.hs index 5451846665..d3d5cec6ef 100644 --- a/src/Simplex/Chat/Terminal.hs +++ b/src/Simplex/Chat/Terminal.hs @@ -1,13 +1,18 @@ +{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} module Simplex.Chat.Terminal where +import Control.Monad.Catch (MonadMask) +import Control.Monad.IO.Class (MonadIO) import Simplex.Chat.Styled import Simplex.Chat.Types import System.Console.ANSI.Types import System.Terminal +import System.Terminal.Internal (LocalTerminal, Terminal, VirtualTerminal) import UnliftIO.STM data ActiveTo = ActiveNone | ActiveC ContactRef @@ -15,6 +20,7 @@ data ActiveTo = ActiveNone | ActiveC ContactRef data ChatTerminal = ChatTerminal { activeTo :: TVar ActiveTo, + termDevice :: TerminalDevice, termState :: TVar TerminalState, termSize :: Size, nextMessageRow :: TVar Int, @@ -28,16 +34,30 @@ data TerminalState = TerminalState previousInput :: String } -newChatTerminal :: IO ChatTerminal -newChatTerminal = do +class Terminal t => WithTerminal t where + withTerm :: (MonadIO m, MonadMask m) => t -> (t -> m a) -> m a + +data TerminalDevice = forall t. WithTerminal t => TerminalDevice t + +instance WithTerminal LocalTerminal where + withTerm _ = withTerminal + +instance WithTerminal VirtualTerminal where + withTerm t = ($ t) + +withChatTerm :: (MonadIO m, MonadMask m) => ChatTerminal -> (forall t. WithTerminal t => TerminalT t m a) -> m a +withChatTerm ChatTerminal {termDevice = TerminalDevice t} action = withTerm t $ runTerminalT action + +newChatTerminal :: WithTerminal t => t -> IO ChatTerminal +newChatTerminal t = do activeTo <- newTVarIO ActiveNone - termSize <- withTerminal . runTerminalT $ getWindowSize + termSize <- withTerm t . runTerminalT $ getWindowSize let lastRow = height termSize - 1 termState <- newTVarIO newTermState termLock <- newTMVarIO () nextMessageRow <- newTVarIO lastRow -- threadDelay 500000 -- this delay is the same as timeout in getTerminalSize - return ChatTerminal {activeTo, termState, termSize, nextMessageRow, termLock} + return ChatTerminal {activeTo, termDevice = TerminalDevice t, termState, termSize, nextMessageRow, termLock} newTermState :: TerminalState newTermState = @@ -55,9 +75,11 @@ withTermLock ChatTerminal {termLock} action = do atomically $ putTMVar termLock () printToTerminal :: ChatTerminal -> [StyledString] -> IO () -printToTerminal ct s = withTerminal . runTerminalT . withTermLock ct $ do - printMessage ct s - updateInput ct +printToTerminal ct s = + withChatTerm ct $ + withTermLock ct $ do + printMessage ct s + updateInput ct updateInput :: forall m. MonadTerminal m => ChatTerminal -> m () updateInput ChatTerminal {termSize = Size {height, width}, termState, nextMessageRow} = do diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index e305e09902..0a34d67c4a 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -133,7 +133,7 @@ ttyContact :: ContactRef -> StyledString ttyContact = styled (Colored Green) ttyToContact :: ContactRef -> StyledString -ttyToContact c = styled (Colored Cyan) $ c <> " " +ttyToContact c = styled (Colored Cyan) $ "@" <> c <> " " ttyFromContact :: ContactRef -> StyledString ttyFromContact c = styled (Colored Yellow) $ c <> "> " diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs new file mode 100644 index 0000000000..4b5907ee77 --- /dev/null +++ b/tests/ChatClient.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +module ChatClient where + +import Control.Concurrent.Async +import Control.Concurrent.STM (retry) +import Control.Monad.Except +import Simplex.Chat +import Simplex.Chat.Controller (ChatController (..)) +import Simplex.Chat.Options +import Simplex.Chat.Store +import Simplex.Chat.Types (Profile) +import qualified System.Terminal as C +import System.Terminal.Internal (VirtualTerminal, VirtualTerminalSettings (..), withVirtualTerminal) + +testDB1 :: FilePath +testDB1 = "tests/tmp/test1" + +testDB2 :: FilePath +testDB2 = "tests/tmp/test2" + +opts :: ChatOpts +opts = + ChatOpts + { dbFile = undefined, + smpServers = ["localhost:5223"] + } + +termSettings :: VirtualTerminalSettings +termSettings = + VirtualTerminalSettings + { virtualType = "xterm", + virtualWindowSize = pure C.Size {height = 24, width = 1000}, + virtualEvent = retry, + virtualInterrupt = retry + } + +data TestCC = TestCC ChatController VirtualTerminal (Async ()) + +virtualSimplexChat :: FilePath -> Profile -> IO TestCC +virtualSimplexChat dbFile profile = do + st <- createStore (dbFile <> ".chat.db") 1 + void . runExceptT $ createUser st profile True + t <- withVirtualTerminal termSettings pure + cc <- newChatController opts {dbFile} t . const $ pure () -- no notifications + a <- async $ runSimplexChat cc + pure (TestCC cc t a) + +testChat2 :: Profile -> Profile -> (TestCC -> TestCC -> IO ()) -> IO () +testChat2 p1 p2 test = do + tc1 <- virtualSimplexChat testDB1 p1 + tc2 <- virtualSimplexChat testDB2 p2 + test tc1 tc2 diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs new file mode 100644 index 0000000000..cfd626a744 --- /dev/null +++ b/tests/ChatTests.hs @@ -0,0 +1,74 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +module ChatTests where + +import ChatClient +import Control.Concurrent.Async (concurrently_) +import Control.Concurrent.STM +import Data.Char (isDigit) +import Data.List (dropWhileEnd, find, isPrefixOf) +import Simplex.Chat.Controller +import Simplex.Chat.Types (Profile (..)) +import System.Terminal.Internal (VirtualTerminal (..)) +import Test.Hspec + +aliceProfile :: Profile +aliceProfile = Profile {contactRef = "alice", displayName = "Alice"} + +bobProfile :: Profile +bobProfile = Profile {contactRef = "bob", displayName = "Bob"} + +testAddContact :: Spec +testAddContact = describe "add chat contact" $ + xit "add contact and send/receive message" $ + testChat2 aliceProfile bobProfile $ \alice bob -> do + alice ##> "/a" + Just inv <- invitation <$> getWindow alice + bob ##> ("/c " <> inv) + concurrently_ + (bob <## "alice is connected") + (alice <## "bob is connected") + alice #> "@bob hello" + bob <# "alice> hello" + bob #> "@alice hi" + alice <# "bob> hi" + +(##>) :: TestCC -> String -> IO () +(##>) cc cmd = do + chatCommand cc cmd + cc <## cmd + +(#>) :: TestCC -> String -> IO () +(#>) cc cmd = do + chatCommand cc cmd + cc <# cmd + +chatCommand :: TestCC -> String -> IO () +chatCommand (TestCC cc _ _) cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd + +(<##) :: TestCC -> String -> Expectation +cc <## line = (lastOutput <$> getWindow cc) `shouldReturn` line + +(<#) :: TestCC -> String -> Expectation +cc <# line = (dropTime . lastOutput <$> getWindow cc) `shouldReturn` line + +dropTime :: String -> String +dropTime msg = case splitAt 6 msg of + ([m, m', ':', s, s', ' '], text) -> + if all isDigit [m, m', s, s'] then text else error "invalid time" + _ -> error "invalid time" + +getWindow :: TestCC -> IO [String] +getWindow (TestCC _ t _) = do + let w = virtualWindow t + win <- readTVarIO w + atomically $ do + win' <- readTVar w + if win' /= win then pure win' else retry + +invitation :: [String] -> Maybe String +invitation win = dropWhileEnd (== ' ') <$> find ("smp::" `isPrefixOf`) win + +lastOutput :: [String] -> String +lastOutput win = dropWhileEnd (== ' ') $ win !! (length win - 2) -- (- 2) to exclude prompt diff --git a/tests/Test.hs b/tests/Test.hs index 2575ec5eb9..f9c0b770e0 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -1,9 +1,14 @@ +import ChatTests import MarkdownTests import ProtocolTests +import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import Test.Hspec main :: IO () main = do + createDirectoryIfMissing False "tests/tmp" hspec $ do describe "SimpleX chat markdown" markdownTests describe "SimpleX chat protocol" protocolTests + describe "SimpleX chat client" testAddContact + removeDirectoryRecursive "tests/tmp" From 24c62584fc296288ed477f228de00433357e7bb1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 11 Jul 2021 12:22:22 +0100 Subject: [PATCH 08/29] simplify chat protocol (#74) * groups protocol and some group commands * simplify chat message format, refactor types to include parsed message body * disable chat test --- migrations/20210612_initial.sql | 11 +- package.yaml | 1 + src/Simplex/Chat.hs | 64 +++++--- src/Simplex/Chat/Protocol.hs | 275 +++++++++++++++++++------------- src/Simplex/Chat/Types.hs | 12 ++ src/Simplex/Chat/protocol.md | 73 ++++++--- tests/ChatTests.hs | 1 - tests/ProtocolTests.hs | 28 ++-- 8 files changed, 280 insertions(+), 185 deletions(-) diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index aa35c77b01..e93e3e686b 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -34,14 +34,6 @@ CREATE TABLE known_servers( UNIQUE (user_id, host, port) ) WITHOUT ROWID; --- CREATE TABLE contact_invitations ( --- invitation_id INTEGER PRIMARY KEY, --- agent_inv_id BLOB UNIQUE, --- invitation TEXT, --- contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, --- invitation_status TEXT NOT NULL DEFAULT '' --- ); - CREATE TABLE group_profiles ( -- shared group profiles group_profile_id INTEGER PRIMARY KEY, group_ref TEXT NOT NULL, -- this name must not contain spaces @@ -54,7 +46,6 @@ CREATE TABLE groups ( invited_by INTEGER REFERENCES contacts ON DELETE RESTRICT, external_group_id BLOB NOT NULL, local_group_ref TEXT NOT NULL UNIQUE, -- local group name without spaces - local_properties TEXT NOT NULL, -- local JSON group properties group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile user_id INTEGER NOT NULL REFERENCES users, UNIQUE (invited_by, external_group_id) @@ -64,7 +55,7 @@ CREATE TABLE group_members ( -- group members, excluding the local user group_member_id INTEGER PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, member_id BLOB NOT NULL, -- shared member ID, unique per group - member_role TEXT NOT NULL DEFAULT '', -- owner, admin, moderator, '' + member_role TEXT NOT NULL DEFAULT '', -- owner, admin, member member_status TEXT NOT NULL DEFAULT '', -- inv | con | full | off invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, diff --git a/package.yaml b/package.yaml index a838be3c2f..cabd3cf74e 100644 --- a/package.yaml +++ b/package.yaml @@ -16,6 +16,7 @@ dependencies: - ansi-terminal == 0.10.* - attoparsec == 0.13.* - base >= 4.7 && < 5 + - base64-bytestring >= 1.0 && < 1.3 - bytestring == 0.10.* - containers == 0.6.* - directory == 1.3.* diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 51fbdbb11d..91c60a47ae 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -15,12 +15,11 @@ import Control.Logger.Simple import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader -import qualified Data.Aeson as J -import Data.Attoparsec.ByteString.Char8 (Parser) +import Data.Attoparsec.ByteString.Char8 (Parser, ()) import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (first) import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Lazy.Char8 as LB +import qualified Data.ByteString.Char8 as B import Data.Functor (($>)) import Data.List (find) import Data.Maybe (isJust) @@ -58,6 +57,14 @@ data ChatCommand | Connect SMPQueueInfo | DeleteContact ContactRef | SendMessage ContactRef ByteString + | NewGroup GroupRef + | AddMember GroupRef ContactRef GroupMemberRole + | RemoveMember GroupRef ContactRef + | MemberRole GroupRef ContactRef GroupMemberRole + | LeaveGroup GroupRef + | DeleteGroup GroupRef + | ListMembers GroupRef + | SendGroupMessage GroupRef ByteString deriving (Show) cfg :: AgentConfig @@ -142,10 +149,18 @@ processChatCommand User {userId, profile} = \case showContactDeleted cRef SendMessage cRef msg -> do Connection {agentConnId} <- withStore $ \st -> getContactConnection st userId cRef - let body = MsgBodyContent {contentType = SimplexContentType XCText, contentHash = Nothing, contentData = MBFull $ MsgData msg} - rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent = XMsgNew MTText, chatMsgBody = [body], chatDAGIdx = Nothing} + let body = MsgBodyContent {contentType = SimplexContentType XCText, contentData = msg} + rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent = XMsgNew MTText [] [body], chatDAG = Nothing} void . withAgent $ \smp -> sendMessage smp agentConnId $ serializeRawChatMessage rawMsg setActive $ ActiveC cRef + NewGroup _gRef -> pure () + AddMember _gRef _cRef _mRole -> pure () + MemberRole _gRef _cRef _mRole -> pure () + RemoveMember _gRef _cRef -> pure () + LeaveGroup _gRef -> pure () + DeleteGroup _gRef -> pure () + ListMembers _gRef -> pure () + SendGroupMessage _gRef _msg -> pure () agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do @@ -163,10 +178,10 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do ReceivedDirectMessage Contact {localContactRef = c} -> case agentMessage of MSG meta msgBody -> do - ChatMessage {chatMsgEvent, chatMsgBody} <- liftEither $ parseChatMessage msgBody + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of - XMsgNew MTText -> newTextMessage c meta $ find (isSimplexContentType XCText) chatMsgBody - XInfo -> pure () -- TODO profile update + XMsgNew MTText [] body -> newTextMessage c meta $ find (isSimplexContentType XCText) body + XInfo _ -> pure () -- TODO profile update _ -> pure () CON -> do -- TODO update connection status @@ -191,7 +206,7 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do where newTextMessage :: ContactRef -> MsgMeta -> Maybe MsgBodyContent -> m () newTextMessage c meta = \case - Just MsgBodyContent {contentData = MBFull (MsgData bs)} -> do + Just MsgBodyContent {contentData = bs} -> do let text = safeDecodeUtf8 bs showReceivedMessage c (snd $ broker meta) text (integrity meta) showToast ("@" <> c) text @@ -203,21 +218,15 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do saveConnInfo :: Connection -> ConnInfo -> m () saveConnInfo activeConn connInfo = do - ChatMessage {chatMsgEvent, chatMsgBody} <- liftEither $ parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of - XInfo -> - case find (isSimplexContentType XCJson) chatMsgBody of - Just MsgBodyContent {contentData = MBFull (MsgData bs)} -> do - p <- liftEither . first (ChatErrorContact . CEProfile) $ J.eitherDecodeStrict' bs - withStore $ \st -> createDirectContact st userId activeConn p - _ -> pure () -- TODO show/log error? + XInfo p -> + withStore $ \st -> createDirectContact st userId activeConn p _ -> pure () -- TODO show/log error, other events in SMP confirmation encodeProfile :: Profile -> ByteString encodeProfile profile = - let json = LB.toStrict $ J.encode profile - body = MsgBodyContent {contentType = SimplexContentType XCJson, contentHash = Nothing, contentData = MBFull $ MsgData json} - chatMsg = ChatMessage {chatMsgId = Nothing, chatMsgEvent = XInfo, chatMsgBody = [body], chatDAGIdx = Nothing} + let chatMsg = ChatMessage {chatMsgId = Nothing, chatMsgEvent = XInfo profile, chatDAG = Nothing} in serializeRawChatMessage $ rawChatMessage chatMsg getCreateActiveUser :: SQLiteStore -> IO User @@ -314,10 +323,23 @@ withStore action = do chatCommandP :: Parser ChatCommand chatCommandP = ("/help" <|> "/h") $> ChatHelp + <|> ("/group #" <|> "/g #") *> (NewGroup <$> groupRef) + <|> ("/add #" <|> "/a #") *> (AddMember <$> groupRef <* A.space <*> contactRef <* A.space <*> memberRole) + <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> groupRef <* A.space <*> contactRef) + <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> groupRef) + <|> ("/members #" <|> "/ms #") *> (ListMembers <$> groupRef) + <|> A.char '#' *> (SendGroupMessage <$> groupRef <* A.space <*> A.takeByteString) <|> ("/add" <|> "/a") $> AddContact <|> ("/connect " <|> "/c ") *> (Connect <$> smpQueueInfoP) - <|> ("/delete " <|> "/d ") *> (DeleteContact <$> contactRef) + <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> contactRef) <|> A.char '@' *> (SendMessage <$> contactRef <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp where - contactRef = safeDecodeUtf8 <$> A.takeTill (== ' ') + contactRef = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) + refChar c = c > ' ' && c /= '#' && c /= '@' + groupRef = contactRef + memberRole = + ("owner" $> GROwner) + <|> ("admin" $> GRAdmin) + <|> ("normal" $> GRMember) + "memberRole" diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 6474b3cf8d..1449ca9690 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -6,21 +6,27 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TupleSections #-} module Simplex.Chat.Protocol where -import Control.Applicative (optional, (<|>)) +import Control.Applicative (optional) +import Control.Monad ((<=<)) import Control.Monad.Except (throwError) +import Data.Aeson (FromJSON, ToJSON) +import qualified Data.Aeson as J import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as A +import qualified Data.ByteString.Base64 as B64 import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.Lazy.Char8 as LB import Data.Int (Int64) -import Data.List (findIndex) +import Data.List (find) import Data.Text (Text) import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Parsers (base64P) +import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow) data ChatDirection (p :: AParty) where @@ -34,12 +40,34 @@ deriving instance Eq (ChatDirection p) deriving instance Show (ChatDirection p) data ChatMsgEvent - = XMsgNew MessageType - | XInfo + = XMsgNew {messageType :: MessageType, files :: [(ContentType, Int)], content :: [MsgBodyContent]} + | XInfo Profile + | XGrpInv InvitationId MemberId GroupMemberRole GroupProfile + | XGrpAcpt InvitationId SMPQueueInfo + | XGrpMemNew MemberId GroupMemberRole Profile + | XGrpMemIntro MemberId GroupMemberRole Profile deriving (Eq, Show) +type MemberId = ByteString + data MessageType = MTText | MTImage deriving (Eq, Show) +data GroupMemberRole = GROwner | GRAdmin | GRMember + deriving (Eq, Show) + +toMemberRole :: ByteString -> Either String GroupMemberRole +toMemberRole = \case + "owner" -> Right GROwner + "admin" -> Right GRAdmin + "member" -> Right GRMember + r -> Left $ "invalid group member role " <> B.unpack r + +serializeMemberRole :: GroupMemberRole -> ByteString +serializeMemberRole = \case + GROwner -> "owner" + GRAdmin -> "admin" + GRMember -> "member" + toMsgType :: ByteString -> Either String MessageType toMsgType = \case "c.text" -> Right MTText @@ -54,29 +82,59 @@ rawMsgType = \case data ChatMessage = ChatMessage { chatMsgId :: Maybe Int64, chatMsgEvent :: ChatMsgEvent, - chatMsgBody :: [MsgBodyContent], - chatDAGIdx :: Maybe Int + chatDAG :: Maybe ByteString } deriving (Eq, Show) toChatMessage :: RawChatMessage -> Either String ChatMessage toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} = do - body <- mapM toMsgBodyContent chatMsgBody - let chatDAGIdx = findDAG body + (chatDAG, body) <- getDAG <$> mapM toMsgBodyContent chatMsgBody case chatMsgEvent of "x.msg.new" -> case chatMsgParams of - [mt] -> do + mt : rawFiles -> do t <- toMsgType mt - pure ChatMessage {chatMsgId, chatMsgEvent = XMsgNew t, chatMsgBody = body, chatDAGIdx} - _ -> throwError "x.msg.new expects one parameter" + files <- mapM (toContentInfo <=< parseAll contentInfoP) rawFiles + let msg = XMsgNew {messageType = t, files, content = body} + pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + [] -> throwError "x.msg.new expects at least one parameter" "x.info" -> case chatMsgParams of - [] -> pure ChatMessage {chatMsgId, chatMsgEvent = XInfo, chatMsgBody = body, chatDAGIdx} + [] -> do + profile <- getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = XInfo profile, chatDAG} _ -> throwError "x.info expects no parameters" + "x.grp.inv" -> case chatMsgParams of + [invId', memId', role'] -> do + invId <- B64.decode invId' + memId <- B64.decode memId' + role <- toMemberRole role' + groupProfile <- getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = XGrpInv invId memId role groupProfile, chatDAG} + _ -> throwError "x.grp.inv expects 3 parameters" + "x.grp.acpt" -> case chatMsgParams of + [invId, qInfo] -> do + msg <- XGrpAcpt <$> B64.decode invId <*> parseAll smpQueueInfoP qInfo + pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + _ -> throwError "x.grp.acpt expects 2 parameters" + "x.grp.mem.new" -> case chatMsgParams of + [memId, role] -> do + msg <- XGrpMemNew <$> B64.decode memId <*> toMemberRole role <*> getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + _ -> throwError "x.grp.acpt expects 2 parameters" + "x.grp.mem.intro" -> case chatMsgParams of + [memId, role] -> do + msg <- XGrpMemIntro <$> B64.decode memId <*> toMemberRole role <*> getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + _ -> throwError "x.grp.acpt expects 2 parameters" _ -> throwError $ "unsupported event " <> B.unpack chatMsgEvent -toChatMessage _ = Left "message continuation" - -findDAG :: [MsgBodyContent] -> Maybe Int -findDAG = findIndex $ isContentType SimplexDAG + where + getDAG :: [MsgBodyContent] -> (Maybe ByteString, [MsgBodyContent]) + getDAG body = case break (isContentType SimplexDAG) body of + (b, MsgBodyContent SimplexDAG dag : a) -> (Just dag, b <> a) + _ -> (Nothing, body) + toContentInfo :: (RawContentType, Int) -> Either String (ContentType, Int) + toContentInfo (rawType, size) = (,size) <$> toContentType rawType + getJSON :: FromJSON a => [MsgBodyContent] -> Either String a + getJSON = J.eitherDecodeStrict' <=< getSimplexContentType XCJson isContentType :: ContentType -> MsgBodyContent -> Bool isContentType t MsgBodyContent {contentType = t'} = t == t' @@ -84,27 +142,64 @@ isContentType t MsgBodyContent {contentType = t'} = t == t' isSimplexContentType :: XContentType -> MsgBodyContent -> Bool isSimplexContentType = isContentType . SimplexContentType +getContentType :: ContentType -> [MsgBodyContent] -> Either String ByteString +getContentType t body = case find (isContentType t) body of + Just MsgBodyContent {contentData} -> Right contentData + Nothing -> Left "no required content type" + +getSimplexContentType :: XContentType -> [MsgBodyContent] -> Either String ByteString +getSimplexContentType = getContentType . SimplexContentType + rawChatMessage :: ChatMessage -> RawChatMessage -rawChatMessage ChatMessage {chatMsgId, chatMsgEvent = event, chatMsgBody = body} = - case event of - XMsgNew t -> RawChatMessage {chatMsgId, chatMsgEvent = "x.msg.new", chatMsgParams = [rawMsgType t], chatMsgBody} - XInfo -> RawChatMessage {chatMsgId, chatMsgEvent = "x.info", chatMsgParams = [], chatMsgBody} +rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = + case chatMsgEvent of + XMsgNew {messageType = t, files, content} -> + let rawFiles = map (serializeContentInfo . rawContentInfo) files + chatMsgParams = rawMsgType t : rawFiles + chatMsgBody = rawWithDAG content + in RawChatMessage {chatMsgId, chatMsgEvent = "x.msg.new", chatMsgParams, chatMsgBody} + XInfo profile -> + let chatMsgBody = rawWithDAG [jsonBody profile] + in RawChatMessage {chatMsgId, chatMsgEvent = "x.info", chatMsgParams = [], chatMsgBody} + XGrpInv invId memId role groupProfile -> + let chatMsgParams = [B64.encode invId, B64.encode memId, serializeMemberRole role] + chatMsgBody = rawWithDAG [jsonBody groupProfile] + in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.inv", chatMsgParams, chatMsgBody} + XGrpAcpt invId qInfo -> + let chatMsgParams = [B64.encode invId, serializeSmpQueueInfo qInfo] + in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.acpt", chatMsgParams, chatMsgBody = []} + XGrpMemNew memId role profile -> + let chatMsgParams = [B64.encode memId, serializeMemberRole role] + chatMsgBody = rawWithDAG [jsonBody profile] + in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.mem.new", chatMsgParams, chatMsgBody} + XGrpMemIntro memId role profile -> + let chatMsgParams = [B64.encode memId, serializeMemberRole role] + chatMsgBody = rawWithDAG [jsonBody profile] + in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.mem.intro", chatMsgParams, chatMsgBody} where - chatMsgBody = map rawMsgBodyContent body + rawContentInfo :: (ContentType, Int) -> (RawContentType, Int) + rawContentInfo (t, size) = (rawContentType t, size) + jsonBody :: ToJSON a => a -> MsgBodyContent + jsonBody x = + let json = LB.toStrict $ J.encode x + in MsgBodyContent {contentType = SimplexContentType XCJson, contentData = json} + rawWithDAG :: [MsgBodyContent] -> [RawMsgBodyContent] + rawWithDAG body = map rawMsgBodyContent $ case chatDAG of + Nothing -> body + Just dag -> MsgBodyContent {contentType = SimplexDAG, contentData = dag} : body toMsgBodyContent :: RawMsgBodyContent -> Either String MsgBodyContent -toMsgBodyContent RawMsgBodyContent {contentType, contentHash, contentData} = do +toMsgBodyContent RawMsgBodyContent {contentType, contentData} = do cType <- toContentType contentType - pure MsgBodyContent {contentType = cType, contentHash, contentData} + pure MsgBodyContent {contentType = cType, contentData} rawMsgBodyContent :: MsgBodyContent -> RawMsgBodyContent -rawMsgBodyContent MsgBodyContent {contentType = t, contentHash, contentData} = - RawMsgBodyContent {contentType = rawContentType t, contentHash, contentData} +rawMsgBodyContent MsgBodyContent {contentType = t, contentData} = + RawMsgBodyContent {contentType = rawContentType t, contentData} data MsgBodyContent = MsgBodyContent { contentType :: ContentType, - contentHash :: Maybe ByteString, - contentData :: MsgBodyPartData + contentData :: ByteString } deriving (Eq, Show) @@ -149,24 +244,17 @@ newtype ContentMsg = NewContentMsg ContentData newtype ContentData = ContentText Text -data RawChatMessage - = RawChatMessage - { chatMsgId :: Maybe Int64, - chatMsgEvent :: ByteString, - chatMsgParams :: [ByteString], - chatMsgBody :: [RawMsgBodyContent] - } - | RawChatMsgContinuation - { prevChatMsgId :: Int64, - continuationId :: Int, - continuationData :: ByteString - } +data RawChatMessage = RawChatMessage + { chatMsgId :: Maybe Int64, + chatMsgEvent :: ByteString, + chatMsgParams :: [ByteString], + chatMsgBody :: [RawMsgBodyContent] + } deriving (Eq, Show) data RawMsgBodyContent = RawMsgBodyContent { contentType :: RawContentType, - contentHash :: Maybe ByteString, - contentData :: MsgBodyPartData + contentData :: ByteString } deriving (Eq, Show) @@ -175,82 +263,51 @@ data RawContentType = RawContentType NameSpace ByteString type NameSpace = ByteString -data MsgBodyPartData - = -- | fully loaded - MBFull MsgData - | -- | partially loaded - MBPartial Int MsgData - | -- | not loaded yet - MBEmpty Int - deriving (Eq, Show) - -data MsgData - = MsgData ByteString - | MsgDataRec {dataId :: Int64, dataSize :: Int} +newtype MsgData = MsgData ByteString deriving (Eq, Show) class DataLength a where dataLength :: a -> Int -instance DataLength MsgBodyPartData where - dataLength (MBFull d) = dataLength d - dataLength (MBPartial l _) = l - dataLength (MBEmpty l) = l - -instance DataLength MsgData where - dataLength (MsgData s) = B.length s - dataLength MsgDataRec {dataSize} = dataSize - rawChatMessageP :: Parser RawChatMessage -rawChatMessageP = A.char '#' *> chatMsgContP <|> chatMsgP +rawChatMessageP = do + chatMsgId <- optional A.decimal <* A.space + chatMsgEvent <- B.intercalate "." <$> identifierP `A.sepBy1'` A.char '.' <* A.space + chatMsgParams <- A.takeWhile1 (not . A.inClass ", ") `A.sepBy'` A.char ',' <* A.space + chatMsgBody <- msgBodyContent =<< contentInfoP `A.sepBy'` A.char ',' <* A.space + pure RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} where - chatMsgContP :: Parser RawChatMessage - chatMsgContP = do - prevChatMsgId <- A.decimal <* A.char '.' - continuationId <- A.decimal <* A.space - continuationData <- A.takeByteString - pure RawChatMsgContinuation {prevChatMsgId, continuationId, continuationData} - chatMsgP :: Parser RawChatMessage - chatMsgP = do - chatMsgId <- optional A.decimal <* A.space - chatMsgEvent <- B.intercalate "." <$> identifier `A.sepBy1'` A.char '.' <* A.space - chatMsgParams <- A.takeWhile1 (not . A.inClass ", ") `A.sepBy'` A.char ',' <* A.space - chatMsgBody <- msgBodyContent =<< contentInfo `A.sepBy'` A.char ',' <* A.space - pure RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} - identifier :: Parser ByteString - identifier = B.cons <$> A.letter_ascii <*> A.takeWhile (\c -> A.isAlpha_ascii c || A.isDigit c) - contentInfo :: Parser RawMsgBodyContent - contentInfo = do - contentType <- RawContentType <$> identifier <* A.char '.' <*> A.takeTill (A.inClass ":, ") - contentSize <- A.char ':' *> A.decimal - contentHash <- optional (A.char ':' *> base64P) - pure RawMsgBodyContent {contentType, contentHash, contentData = MBEmpty contentSize} - msgBodyContent :: [RawMsgBodyContent] -> Parser [RawMsgBodyContent] + msgBodyContent :: [(RawContentType, Int)] -> Parser [RawMsgBodyContent] msgBodyContent [] = pure [] - msgBodyContent (p@RawMsgBodyContent {contentData = MBEmpty size} : ps) = do - s <- A.take size <* A.space <|> A.takeByteString - if B.length s == size - then ((p {contentData = MBFull $ MsgData s} :: RawMsgBodyContent) :) <$> msgBodyContent ps - else pure $ (if B.null s then p else p {contentData = MBPartial size $ MsgData s} :: RawMsgBodyContent) : ps - msgBodyContent _ = fail "expected contentData = MBEmpty" + msgBodyContent ((contentType, size) : ps) = do + contentData <- A.take size <* A.space + ((RawMsgBodyContent {contentType, contentData}) :) <$> msgBodyContent ps + +contentInfoP :: Parser (RawContentType, Int) +contentInfoP = do + contentType <- RawContentType <$> identifierP <* A.char '.' <*> A.takeTill (A.inClass ":, ") + size <- A.char ':' *> A.decimal + pure (contentType, size) + +identifierP :: Parser ByteString +identifierP = B.cons <$> A.letter_ascii <*> A.takeWhile (\c -> A.isAlpha_ascii c || A.isDigit c) serializeRawChatMessage :: RawChatMessage -> ByteString -serializeRawChatMessage = \case - RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} -> - B.unwords - [ maybe "" bshow chatMsgId, - chatMsgEvent, - B.intercalate "," chatMsgParams, - B.unwords $ map serializeContentInfo chatMsgBody, - B.unwords $ map serializeContentData chatMsgBody - ] - RawChatMsgContinuation {prevChatMsgId, continuationId, continuationData} -> - bshow prevChatMsgId <> "." <> bshow continuationId <> " " <> continuationData +serializeRawChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} = + B.unwords + [ maybe "" bshow chatMsgId, + chatMsgEvent, + B.intercalate "," chatMsgParams, + B.unwords $ map serializeBodyContentInfo chatMsgBody, + B.unwords $ map msgContentData chatMsgBody + ] -serializeContentInfo :: RawMsgBodyContent -> ByteString -serializeContentInfo RawMsgBodyContent {contentType = RawContentType ns cType, contentHash, contentData} = - ns <> "." <> cType <> ":" <> bshow (dataLength contentData) <> maybe "" (":" <>) contentHash +serializeBodyContentInfo :: RawMsgBodyContent -> ByteString +serializeBodyContentInfo RawMsgBodyContent {contentType = t, contentData} = + serializeContentInfo (t, B.length contentData) -serializeContentData :: RawMsgBodyContent -> ByteString -serializeContentData RawMsgBodyContent {contentData = MBFull (MsgData s)} = s -serializeContentData _ = "" +serializeContentInfo :: (RawContentType, Int) -> ByteString +serializeContentInfo (RawContentType ns cType, size) = ns <> "." <> cType <> ":" <> bshow size + +msgContentData :: RawMsgBodyContent -> ByteString +msgContentData RawMsgBodyContent {contentData} = contentData <> " " diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 6fa3d3823d..6f9d089b84 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -38,6 +38,8 @@ data Contact type ContactRef = Text +type GroupRef = Text + data Group = Group { groupId :: Int64, localGroupRef :: Text @@ -54,6 +56,16 @@ instance ToJSON Profile where toEncoding = J.genericToEncoding J.defaultOptions instance FromJSON Profile +data GroupProfile = GroupProfile + { groupRef :: GroupRef, + displayName :: Text + } + deriving (Generic, Eq, Show) + +instance ToJSON GroupProfile where toEncoding = J.genericToEncoding J.defaultOptions + +instance FromJSON GroupProfile + data Connection = Connection { connId :: Int64, agentConnId :: ConnId, diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md index 7ed41ce9cb..9821f108f5 100644 --- a/src/Simplex/Chat/protocol.md +++ b/src/Simplex/Chat/protocol.md @@ -2,17 +2,17 @@ ## Design constraints -- the transport message has a fixed size (8 or 16kb) -- the chat message can have multiple parts/attachments -- the chat message including attachments can be of any size -- if the message is partially received, it should be possible to parse and display the received parts +- the transport message has a fixed size (8 or 16kb), but the SMP agent will be updated to support sending messages up to maximum configured size (TBC - 64-256kb) in 8-16Kb blocks. +- the chat message can have multiple content parts, but it should fit the agent message of the variable size. +- one of the chat message types should support transmitting large binaries in chunks that could potentially be interleaved with other messages. For example, image preview would fit the message, but the full size image will be transmitted in chunks later - same for large files. +- using object storage can be effective for large groups, but we will postpone it until content channels are implemented. ## Questions - should content types be: - limited to MIME-types - separate content types vocabulary - - both MIME types and extensions + - both MIME types and extensions (currently we support MIME (m.) and Simplex (x.) namespaces) - allow additional content types namespaces ## Message syntax @@ -20,8 +20,7 @@ The syntax of the message inside agent MSG: ```abnf -agentMessageBody = message / msgContinuation -message = [chatMsgId] SP msgEvent SP [parameters] SP [contentParts [SP msgBodyParts]] +agentMessageBody = [chatMsgId] SP msgEvent SP [parameters] SP [contentParts [SP msgBodyParts]] chatMsgId = 1*DIGIT ; used to refer to previous message; ; in the group should only be used in messages sent to all members, ; which is the main reason not to use external agent ID - @@ -43,28 +42,52 @@ msgEventParent = memberId refMsgId refMsgHash memberId = 8*8(OCTET) ; shared member ID refMsgId = 8*8(OCTET) ; sequential message number - external agent message ID refMsgHash = 16*16(OCTET) ; SHA256 of agent message body -msgContinuation = "#" prevMsgId "." continuationId continuationData ``` ### Example: messages, updates, groups ``` -"3 x.msg.new c.text c.text:5 hello " +"3 x.msg.new c.text x.text:5 hello " "4 x.msg.new c.image i.image/jpg:256,i.image/png:4096 abcd abcd " -"4 x.msg.new c.image x.dag:32,i.image/jpg:8000:hash1,i.image/png:16000:hash2 binary1" -"#4.1 binary1end binary2" -"#4.2 binary2continued" -"#4.3 binary2end " -"5 x.msg.new c.image i.image/jpg:256,i.image/url:160 abcd https://media.example.com/asdf#abcd " -'6 x.msg.update 3 c.text:11,x.dag:16 hello there abcd ' -'7 x.msg.delete 3' -'8 x.msg.new app/v1 i.text/html:NNN,i.text/css:NNN,c.js:NNN,c.json:NNN ... ... ... {...} ' -'9 x.msg.eval 8 c.json:NNN {...} ' -'10 x.msg.new c.text 2 c.text:16,x.dag:32 hello there @123 abcd ' -' x.grp.mem.inv 23456,123 1 c.json NNN {...} ' -' x.grp.mem.acpt 23456 1 c.text NNN ' -' x.grp.mem.intro 23456,234 1 c.json NNN {...} ' -' x.grp.mem.inv 23456,234 1 c.text NNN ' -' x.grp.mem.req 23456,123 1 c.json NNN {...} ' -' x.grp.mem.direct.inv 23456,234 1 text NNN ' +"4 x.msg.new c.image x.dag:32,i.image/jpg:8000,i.image/png:16000 binary1" +"5 x.msg.new c.image,i.image/jpg:150000 i.image/jpg:256 abcd " +"6 x.msg.file 5,1.1 x.file:60000 abcd " +"7 x.msg.file 5,1.2 x.file:60000 abcd " +"8 x.msg.file 5,1.3 x.file:30000 abcd " +'8 x.msg.update 3 x.text:11,x.dag:16 hello there abcd ' +'9 x.msg.delete 3' +'10 x.msg.new app/v1 i.text/html:NNN,i.text/css:NNN,c.js:NNN,c.json:NNN ... ... ... {...} ' +'11 x.msg.eval 8 c.json:NNN {...} ' +'12 x.msg.new c.text x.text:16,x.dag:32 hello there @123 abcd ' +' x.grp.mem.inv 23456,123 x.json:NNN {...} ' +' x.grp.mem.acpt 23456 x.text:NNN ' +' x.grp.mem.intro 23456,234 x.json:NNN {...} ' +' x.grp.mem.inv 23456,234 x.text:NNN ' +' x.grp.mem.req 23456,123 x.json:NNN {...} ' +' x.grp.mem.direct.inv 23456,234 x.text:NNN ' ``` + +### Group protocol + +A -> B: invite to group - `MSG: x.grp.inv G_INV_ID,G_MEM_ID_B,G_MEM_ROLE x.json:NNN ` +user B confirms +B -> A: join group - `MSG: x.grp.acpt G_INV_ID,` +A -> Bg: establish group connection (A: JOIN, B: LET) +A -> group (including B)): announce group member: `MSG: N x.grp.mem.new G_MEM_ID_B,G_MEM_ROLE x.json:NNN ` +subsequent messages between A and B are via group connection +A -> Bg: intro member - `MSG: x.grp.mem.intro G_MEM_ID_M,G_MEM_ROLE x.json:NNN ` +B -> Ag: inv for mem - `MSG: x.grp.mem.inv G_MEM_ID_M,,,` +M is an existing member, messages are via group connection +A -> Mg: fwd inv - `MSG: x.grp.mem.fwd G_MEM_ID_B,,,` +M -> Bg: establish group connection (M: JOIN, B: LET) +M -> B: establish direct connection (M: JOIN, B: LET) +M -> Bg: confirm profile and role - `MSG: x.grp.mem.info G_MEM_ID_M,G_MEM_ROLE x.json:NNN ` +if M is a known contact (profile match) send probe to M: + B -> M (via old DM conn): profile match probe: `MSG: x.grp.mem.probe G_MEM_ID_B,` + M -> B (via old DM conn): probe confirm: `MSG: x.grp.mem.probe.ok G_MEM_ID_M,` + link to the same contact +B -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_M` +M -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_B` + +once all members connected +A -> group: `MSG: N x.grp.mem.ok G_MEM_ID_B` diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index cfd626a744..6752966064 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module ChatTests where diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs index 8dbfe18b75..3a7b5d0902 100644 --- a/tests/ProtocolTests.hs +++ b/tests/ProtocolTests.hs @@ -21,37 +21,27 @@ parseChatMessageTest = describe "Raw chat message format" $ do "5 x.grp.mem.leave " #== RawChatMessage (Just 5) "x.grp.mem.leave" [] [] it "one parameter, no content" $ "6 x.msg.del 3 " #== RawChatMessage (Just 6) "x.msg.del" ["3"] [] - it "with content that fits the message and optional trailing space" $ - "7 x.msg.new c.text c.text:11 hello there " + it "with content that fits the message" $ + "7 x.msg.new c.text x.text:11 hello there " #== RawChatMessage (Just 7) "x.msg.new" ["c.text"] - [RawMsgBodyContent (RawContentType "c" "text") Nothing $ MBFull (MsgData "hello there")] - it "with content that fits the message, without trailing space" $ - "7 x.msg.new c.text c.text:11 hello there" - #== RawChatMessage - (Just 7) - "x.msg.new" - ["c.text"] - [RawMsgBodyContent (RawContentType "c" "text") Nothing $ MBFull (MsgData "hello there")] + [RawMsgBodyContent (RawContentType "x" "text") "hello there"] it "with DAG reference and partial content" $ - "8 x.msg.new c.image x.dag:16,c.text:7,m.image/jpg:64:MDEyMzQ1Njc=,m.image/png:4000:MDEyMzQ1Njc= 0123456789012345 picture abcdef" + "8 x.msg.new c.image x.dag:16,x.text:7,m.image/jpg:6 0123456789012345 picture abcdef " #== RawChatMessage (Just 8) "x.msg.new" ["c.image"] - [ RawMsgBodyContent (RawContentType "x" "dag") Nothing $ MBFull (MsgData "0123456789012345"), - RawMsgBodyContent (RawContentType "c" "text") Nothing $ MBFull (MsgData "picture"), - RawMsgBodyContent (RawContentType "m" "image/jpg") (Just "01234567") $ MBPartial 64 (MsgData "abcdef"), - RawMsgBodyContent (RawContentType "m" "image/png") (Just "01234567") $ MBEmpty 4000 + [ RawMsgBodyContent (RawContentType "x" "dag") "0123456789012345", + RawMsgBodyContent (RawContentType "x" "text") "picture", + RawMsgBodyContent (RawContentType "m" "image/jpg") "abcdef" ] - it "message continuation" $ - "#8.1 abcdef" #== RawChatMsgContinuation 8 1 "abcdef" it "without message id" $ - " x.grp.mem.inv 23456,123 c.json:46 {\"contactRef\":\"john\",\"displayName\":\"John Doe\"}" + " x.grp.mem.inv 23456,123 x.json:46 {\"contactRef\":\"john\",\"displayName\":\"John Doe\"} " #== RawChatMessage Nothing "x.grp.mem.inv" ["23456", "123"] - [RawMsgBodyContent (RawContentType "c" "json") Nothing $ MBFull (MsgData "{\"contactRef\":\"john\",\"displayName\":\"John Doe\"}")] + [RawMsgBodyContent (RawContentType "x" "json") "{\"contactRef\":\"john\",\"displayName\":\"John Doe\"}"] From e99c4bda1e03652d7ac9e3b911826c951725b4db Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 12 Jul 2021 19:00:03 +0100 Subject: [PATCH 09/29] started chat groups protocol (#75) * create group * add user as member, store methods to get group and to create group member * add group member and send member invitation * fix ci: use simplexmq from github * chat protocol: create SMP agent connection when inviting member * update protocol, started group invitation receiving --- migrations/20210612_initial.sql | 31 ++- package.yaml | 5 +- src/Simplex/Chat.hs | 96 +++++---- src/Simplex/Chat/Controller.hs | 7 +- src/Simplex/Chat/Protocol.hs | 100 +++++---- src/Simplex/Chat/Store.hs | 354 +++++++++++++++++++++++++++----- src/Simplex/Chat/Styled.hs | 20 +- src/Simplex/Chat/Types.hs | 109 +++++++++- src/Simplex/Chat/View.hs | 24 ++- src/Simplex/Chat/protocol.md | 16 +- tests/ChatTests.hs | 2 +- tests/Test.hs | 2 +- 12 files changed, 575 insertions(+), 191 deletions(-) diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index e93e3e686b..2619908930 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -19,7 +19,7 @@ CREATE TABLE contacts ( lcr_base TEXT NOT NULL, lcr_suffix INTEGER NOT NULL DEFAULT 0, user_id INTEGER NOT NULL REFERENCES users, - user INTEGER, -- 1 if this contact is a user + is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user created_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (user_id, local_contact_ref) ON CONFLICT FAIL, UNIQUE (user_id, lcr_base, lcr_suffix) ON CONFLICT FAIL @@ -37,18 +37,19 @@ CREATE TABLE known_servers( CREATE TABLE group_profiles ( -- shared group profiles group_profile_id INTEGER PRIMARY KEY, group_ref TEXT NOT NULL, -- this name must not contain spaces - display_name TEXT NOT NULL DEFAULT '', + display_name TEXT NOT NULL, properties TEXT NOT NULL DEFAULT '{}' -- JSON with user or contact profile ); CREATE TABLE groups ( group_id INTEGER PRIMARY KEY, -- local group ID - invited_by INTEGER REFERENCES contacts ON DELETE RESTRICT, - external_group_id BLOB NOT NULL, - local_group_ref TEXT NOT NULL UNIQUE, -- local group name without spaces + local_group_ref TEXT NOT NULL, -- local group name without spaces + lgr_base TEXT NOT NULL, + lgr_suffix INTEGER NOT NULL DEFAULT 0, group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile user_id INTEGER NOT NULL REFERENCES users, - UNIQUE (invited_by, external_group_id) + UNIQUE (user_id, local_group_ref) ON CONFLICT FAIL, + UNIQUE (user_id, lgr_base, lgr_suffix) ON CONFLICT FAIL ); CREATE TABLE group_members ( -- group members, excluding the local user @@ -56,10 +57,20 @@ CREATE TABLE group_members ( -- group members, excluding the local user group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, member_id BLOB NOT NULL, -- shared member ID, unique per group member_role TEXT NOT NULL DEFAULT '', -- owner, admin, member - member_status TEXT NOT NULL DEFAULT '', -- inv | con | full | off + member_status TEXT NOT NULL DEFAULT '', -- new, invited, accepted, connected, ready invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, - UNIQUE (group_id, member_id) + contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles ON DELETE RESTRICT, + contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, + UNIQUE (group_id, member_id), + UNIQUE (group_id, contact_id) +); + +CREATE TABLE group_member_intros ( + group_member_intro_id INTEGER PRIMARY KEY, + group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE, + to_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE, + intro_status TEXT NOT NULL DEFAULT '', -- new, intro, inv, fwd, con + UNIQUE (group_member_id, to_group_member_id) ); CREATE TABLE connections ( -- all SMP agent connections @@ -68,7 +79,7 @@ CREATE TABLE connections ( -- all SMP agent connections conn_level INTEGER NOT NULL DEFAULT 0, via_contact INTEGER REFERENCES contacts (contact_id), conn_status TEXT NOT NULL, - conn_type TEXT NOT NULL, -- contact, member + conn_type TEXT NOT NULL, -- contact, member, member_direct contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, created_at TEXT NOT NULL DEFAULT (datetime('now')), diff --git a/package.yaml b/package.yaml index cabd3cf74e..ff3c72591b 100644 --- a/package.yaml +++ b/package.yaml @@ -19,6 +19,7 @@ dependencies: - base64-bytestring >= 1.0 && < 1.3 - bytestring == 0.10.* - containers == 0.6.* + - cryptonite >= 0.27 && < 0.30 - directory == 1.3.* - exceptions == 0.10.* - file-embed == 0.0.14.* @@ -29,6 +30,7 @@ dependencies: - simple-logger == 0.1.* - simplexmq == 0.3.* - sqlite-simple == 0.4.* + - stm == 2.5.* - terminal == 0.2.* - text == 1.2.* - time == 1.9.* @@ -44,9 +46,6 @@ executables: main: Main.hs dependencies: - simplex-chat - - async == 2.2.* - - simplexmq == 0.3.* - - stm == 2.5.* ghc-options: - -threaded diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 91c60a47ae..65914a207b 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -15,7 +15,8 @@ import Control.Logger.Simple import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader -import Data.Attoparsec.ByteString.Char8 (Parser, ()) +import Crypto.Random (drgNew) +import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (first) import Data.ByteString.Char8 (ByteString) @@ -42,12 +43,11 @@ import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..)) import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Client (smpDefaultConfig) import Simplex.Messaging.Parsers (parseAll) -import Simplex.Messaging.Util (bshow, raceAny_) +import Simplex.Messaging.Util (raceAny_) import System.Exit (exitFailure) import System.IO (hFlush, stdout) import Text.Read (readMaybe) import UnliftIO.Async (race_) -import qualified UnliftIO.Exception as E import UnliftIO.STM data ChatCommand @@ -57,7 +57,7 @@ data ChatCommand | Connect SMPQueueInfo | DeleteContact ContactRef | SendMessage ContactRef ByteString - | NewGroup GroupRef + | NewGroup GroupProfile | AddMember GroupRef ContactRef GroupMemberRole | RemoveMember GroupRef ContactRef | MemberRole GroupRef ContactRef GroupMemberRole @@ -97,9 +97,10 @@ newChatController ChatOpts {dbFile, smpServers} t sendNotification = do currentUser <- getCreateActiveUser chatStore chatTerminal <- newChatTerminal t smpAgent <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} + idsDrg <- newTVarIO =<< drgNew inputQ <- newTBQueueIO $ tbqSize cfg notifyQ <- newTBQueueIO $ tbqSize cfg - pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, inputQ, notifyQ, sendNotification} + pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, idsDrg, inputQ, notifyQ, sendNotification} runSimplexChat :: ChatController -> IO () runSimplexChat = runReaderT (race_ runTerminalInput runChatController) @@ -129,7 +130,7 @@ inputSubscriber = do void . runExceptT $ processChatCommand user cmd `catchError` showChatError processChatCommand :: ChatMonad m => User -> ChatCommand -> m () -processChatCommand User {userId, profile} = \case +processChatCommand user@User {userId, profile} = \case ChatHelp -> printToView chatHelpInfo MarkdownHelp -> printToView markdownInfo AddContact -> do @@ -137,30 +138,49 @@ processChatCommand User {userId, profile} = \case withStore $ \st -> createDirectConnection st userId connId showInvitation qInfo Connect qInfo -> do - connId <- withAgent $ \agent -> joinConnection agent qInfo $ encodeProfile profile + connId <- withAgent $ \a -> joinConnection a qInfo $ encodeProfile profile withStore $ \st -> createDirectConnection st userId connId DeleteContact cRef -> do conns <- withStore $ \st -> getContactConnections st userId cRef - withAgent $ \smp -> forM_ conns $ \Connection {agentConnId} -> - deleteConnection smp agentConnId `catchError` \(_ :: AgentErrorType) -> pure () + withAgent $ \a -> forM_ conns $ \Connection {agentConnId} -> + deleteConnection a agentConnId `catchError` \(_ :: AgentErrorType) -> pure () withStore $ \st -> deleteContact st userId cRef unsetActive $ ActiveC cRef - when (null conns) . throwError . ChatErrorContact $ CENotFound cRef showContactDeleted cRef SendMessage cRef msg -> do - Connection {agentConnId} <- withStore $ \st -> getContactConnection st userId cRef + contact <- withStore $ \st -> getContact st userId cRef let body = MsgBodyContent {contentType = SimplexContentType XCText, contentData = msg} rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent = XMsgNew MTText [] [body], chatDAG = Nothing} - void . withAgent $ \smp -> sendMessage smp agentConnId $ serializeRawChatMessage rawMsg + connId = contactConnId contact + void . withAgent $ \a -> sendMessage a connId $ serializeRawChatMessage rawMsg setActive $ ActiveC cRef - NewGroup _gRef -> pure () - AddMember _gRef _cRef _mRole -> pure () + NewGroup gProfile -> do + gVar <- asks idsDrg + void $ withStore $ \st -> createNewGroup st gVar user gProfile + showGroupCreated gProfile + AddMember gRef cRef memRole -> do + (group, contact) <- withStore $ \st -> (,) <$> getGroup st user gRef <*> getContact st userId cRef + let Group {groupId, groupProfile, membership, members} = group + userRole = memberRole membership + userMemberId = memberId membership + when (userRole < GRAdmin || userRole < memRole) $ throwError $ ChatError CEGroupRole + when (isMember contact members) $ throwError $ ChatError CEGroupDuplicateMember + gVar <- asks idsDrg + (agentConnId, qInfo) <- withAgent createConnection + memberId <- withStore $ \st -> createGroupMember st gVar user groupId (contactId contact) memRole IBUser agentConnId + let chatMsgEvent = XGrpInv (userMemberId, userRole) (memberId, memRole) qInfo groupProfile + rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent, chatDAG = Nothing} + connId = contactConnId contact + void . withAgent $ \a -> sendMessage a connId $ serializeRawChatMessage rawMsg MemberRole _gRef _cRef _mRole -> pure () RemoveMember _gRef _cRef -> pure () LeaveGroup _gRef -> pure () DeleteGroup _gRef -> pure () ListMembers _gRef -> pure () SendGroupMessage _gRef _msg -> pure () + where + isMember :: Contact -> [(GroupMember, Connection)] -> Bool + isMember Contact {contactId} members = isJust $ find ((== Just contactId) . memberContactId . fst) members agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do @@ -175,13 +195,14 @@ processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agen processAgentMessage User {userId, profile} agentConnId agentMessage = do chatDirection <- withStore $ \st -> getConnectionChatDirection st userId agentConnId case chatDirection of - ReceivedDirectMessage Contact {localContactRef = c} -> + ReceivedDirectMessage (CContact ct@Contact {localContactRef = c}) -> case agentMessage of MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of XMsgNew MTText [] body -> newTextMessage c meta $ find (isSimplexContentType XCText) body XInfo _ -> pure () -- TODO profile update + XGrpInv fromMem invMem qInfo groupProfile -> groupInvitation ct fromMem invMem qInfo groupProfile _ -> pure () CON -> do -- TODO update connection status @@ -193,14 +214,14 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do showToast ("@" <> c) "disconnected" unsetActive $ ActiveC c _ -> pure () - ReceivedDirectMessage NewContact {activeConn} -> + ReceivedDirectMessage (CConnection conn) -> case agentMessage of CONF confId connInfo -> do -- TODO update connection status - saveConnInfo activeConn connInfo + saveConnInfo conn connInfo withAgent $ \a -> allowConnection a agentConnId confId $ encodeProfile profile INFO connInfo -> - saveConnInfo activeConn connInfo + saveConnInfo conn connInfo _ -> pure () _ -> pure () where @@ -213,6 +234,11 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do setActive $ ActiveC c _ -> pure () + groupInvitation :: Contact -> (MemberId, GroupMemberRole) -> (MemberId, GroupMemberRole) -> SMPQueueInfo -> GroupProfile -> m () + groupInvitation _ct (fromMemId, fromRole) (memId, memRole) _qInfo _groupProfile = do + when (fromRole < GRAdmin || fromRole < memRole) $ throwError $ ChatError CEGroupRole + when (fromMemId == memId) $ throwError $ ChatError CEGroupDuplicateMember + parseChatMessage :: ByteString -> Either ChatError ChatMessage parseChatMessage msgBody = first ChatErrorMessage (parseAll rawChatMessageP msgBody >>= toChatMessage) @@ -305,26 +331,16 @@ withStore :: ChatMonad m => (forall m'. (MonadUnliftIO m', MonadError StoreError m') => SQLiteStore -> m' a) -> m a -withStore action = do - st <- asks chatStore - runExceptT (action st `E.catch` handleInternal) >>= \case - Right c -> pure c - Left e -> throwError $ storeError e - where - -- TODO when parsing exception happens in store, the agent hangs; - -- changing SQLError to SomeException does not help - handleInternal :: (MonadError StoreError m') => E.SomeException -> m' a - handleInternal e = throwError . SEInternal $ bshow e - storeError :: StoreError -> ChatError - storeError = \case - SEContactNotFound c -> ChatErrorContact $ CENotFound c - e -> ChatErrorStore e +withStore action = + asks chatStore + >>= runExceptT . action + >>= liftEither . first ChatErrorStore chatCommandP :: Parser ChatCommand chatCommandP = ("/help" <|> "/h") $> ChatHelp - <|> ("/group #" <|> "/g #") *> (NewGroup <$> groupRef) - <|> ("/add #" <|> "/a #") *> (AddMember <$> groupRef <* A.space <*> contactRef <* A.space <*> memberRole) + <|> ("/group #" <|> "/g #") *> (NewGroup <$> groupProfile) + <|> ("/add #" <|> "/a #") *> (AddMember <$> groupRef <* A.space <*> contactRef <*> memberRole) <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> groupRef <* A.space <*> contactRef) <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> groupRef) <|> ("/members #" <|> "/ms #") *> (ListMembers <$> groupRef) @@ -338,8 +354,12 @@ chatCommandP = contactRef = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) refChar c = c > ' ' && c /= '#' && c /= '@' groupRef = contactRef + groupProfile = do + gRef <- groupRef + gName <- safeDecodeUtf8 <$> (A.space *> A.takeByteString) <|> pure "" + pure GroupProfile {groupRef = gRef, displayName = if T.null gName then gRef else gName} memberRole = - ("owner" $> GROwner) - <|> ("admin" $> GRAdmin) - <|> ("normal" $> GRMember) - "memberRole" + (" owner" $> GROwner) + <|> (" admin" $> GRAdmin) + <|> (" normal" $> GRMember) + <|> pure GRMember diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 9d39b28fca..fc6fa50820 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -10,6 +10,7 @@ import Control.Exception import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader +import Crypto.Random (ChaChaDRG) import Simplex.Chat.Notification import Simplex.Chat.Store (StoreError) import Simplex.Chat.Terminal @@ -24,6 +25,7 @@ data ChatController = ChatController smpAgent :: AgentClient, chatTerminal :: ChatTerminal, chatStore :: SQLiteStore, + idsDrg :: TVar ChaChaDRG, inputQ :: TBQueue InputEvent, notifyQ :: TBQueue Notification, sendNotification :: Notification -> IO () @@ -32,14 +34,13 @@ data ChatController = ChatController data InputEvent = InputCommand String | InputControl Char data ChatError - = ChatErrorContact ContactError + = ChatError ChatErrorType | ChatErrorMessage String | ChatErrorAgent AgentErrorType | ChatErrorStore StoreError deriving (Show, Exception) -data ContactError = CENotFound ContactRef | CEProfile String - deriving (Show, Exception) +data ChatErrorType = CEGroupRole | CEGroupDuplicateMember deriving (Show, Exception) type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 1449ca9690..6fba8f3b77 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -12,7 +12,6 @@ module Simplex.Chat.Protocol where import Control.Applicative (optional) import Control.Monad ((<=<)) -import Control.Monad.Except (throwError) import Data.Aeson (FromJSON, ToJSON) import qualified Data.Aeson as J import Data.Attoparsec.ByteString.Char8 (Parser) @@ -30,44 +29,38 @@ import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow) data ChatDirection (p :: AParty) where - ReceivedDirectMessage :: Contact -> ChatDirection 'Agent - SentDirectMessage :: Contact -> ChatDirection 'Client - ReceivedGroupMessage :: Group -> Contact -> ChatDirection 'Agent + ReceivedDirectMessage :: ConnContact -> ChatDirection 'Agent + SentDirectMessage :: ConnContact -> ChatDirection 'Client + ReceivedGroupMessage :: Group -> ConnContact -> ChatDirection 'Agent SentGroupMessage :: Group -> ChatDirection 'Client deriving instance Eq (ChatDirection p) deriving instance Show (ChatDirection p) +data ConnContact = CContact Contact | CConnection Connection + deriving (Eq, Show) + data ChatMsgEvent - = XMsgNew {messageType :: MessageType, files :: [(ContentType, Int)], content :: [MsgBodyContent]} + = XMsgNew + { messageType :: MessageType, + files :: [(ContentType, Int)], + content :: [MsgBodyContent] + } | XInfo Profile - | XGrpInv InvitationId MemberId GroupMemberRole GroupProfile - | XGrpAcpt InvitationId SMPQueueInfo + | XGrpInv + { fromMember :: (MemberId, GroupMemberRole), + invitedMember :: (MemberId, GroupMemberRole), + queueInfo :: SMPQueueInfo, + groupProfile :: GroupProfile + } + | XGrpAcpt MemberId | XGrpMemNew MemberId GroupMemberRole Profile | XGrpMemIntro MemberId GroupMemberRole Profile deriving (Eq, Show) -type MemberId = ByteString - data MessageType = MTText | MTImage deriving (Eq, Show) -data GroupMemberRole = GROwner | GRAdmin | GRMember - deriving (Eq, Show) - -toMemberRole :: ByteString -> Either String GroupMemberRole -toMemberRole = \case - "owner" -> Right GROwner - "admin" -> Right GRAdmin - "member" -> Right GRMember - r -> Left $ "invalid group member role " <> B.unpack r - -serializeMemberRole :: GroupMemberRole -> ByteString -serializeMemberRole = \case - GROwner -> "owner" - GRAdmin -> "admin" - GRMember -> "member" - toMsgType :: ByteString -> Either String MessageType toMsgType = \case "c.text" -> Right MTText @@ -96,41 +89,38 @@ toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBod files <- mapM (toContentInfo <=< parseAll contentInfoP) rawFiles let msg = XMsgNew {messageType = t, files, content = body} pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - [] -> throwError "x.msg.new expects at least one parameter" + [] -> Left "x.msg.new expects at least one parameter" "x.info" -> case chatMsgParams of [] -> do profile <- getJSON body pure ChatMessage {chatMsgId, chatMsgEvent = XInfo profile, chatDAG} - _ -> throwError "x.info expects no parameters" + _ -> Left "x.info expects no parameters" "x.grp.inv" -> case chatMsgParams of - [invId', memId', role'] -> do - invId <- B64.decode invId' - memId <- B64.decode memId' - role <- toMemberRole role' - groupProfile <- getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = XGrpInv invId memId role groupProfile, chatDAG} - _ -> throwError "x.grp.inv expects 3 parameters" + [fromMemId, fromRole, memId, role, qInfo] -> do + fromMember <- (,) <$> B64.decode fromMemId <*> toMemberRole fromRole + invitedMember <- (,) <$> B64.decode memId <*> toMemberRole role + msg <- XGrpInv fromMember invitedMember <$> parseAll smpQueueInfoP qInfo <*> getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + _ -> Left "x.grp.inv expects 5 parameters" "x.grp.acpt" -> case chatMsgParams of - [invId, qInfo] -> do - msg <- XGrpAcpt <$> B64.decode invId <*> parseAll smpQueueInfoP qInfo + [memId] -> do + msg <- XGrpAcpt <$> B64.decode memId pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - _ -> throwError "x.grp.acpt expects 2 parameters" - "x.grp.mem.new" -> case chatMsgParams of - [memId, role] -> do - msg <- XGrpMemNew <$> B64.decode memId <*> toMemberRole role <*> getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - _ -> throwError "x.grp.acpt expects 2 parameters" - "x.grp.mem.intro" -> case chatMsgParams of - [memId, role] -> do - msg <- XGrpMemIntro <$> B64.decode memId <*> toMemberRole role <*> getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - _ -> throwError "x.grp.acpt expects 2 parameters" - _ -> throwError $ "unsupported event " <> B.unpack chatMsgEvent + _ -> Left "x.grp.acpt expects one parameter" + "x.grp.mem.new" -> memberMessage chatMsgParams XGrpMemNew body chatDAG + "x.grp.mem.intro" -> memberMessage chatMsgParams XGrpMemIntro body chatDAG + _ -> Left $ "unsupported event " <> B.unpack chatMsgEvent where getDAG :: [MsgBodyContent] -> (Maybe ByteString, [MsgBodyContent]) getDAG body = case break (isContentType SimplexDAG) body of (b, MsgBodyContent SimplexDAG dag : a) -> (Just dag, b <> a) _ -> (Nothing, body) + memberMessage :: + FromJSON a => [ByteString] -> (MemberId -> GroupMemberRole -> a -> ChatMsgEvent) -> [MsgBodyContent] -> Maybe ByteString -> Either String ChatMessage + memberMessage [memId, role] mkMsg body chatDAG = do + msg <- mkMsg <$> B64.decode memId <*> toMemberRole role <*> getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + memberMessage _ _ _ _ = Left "message expects 2 parameters" toContentInfo :: (RawContentType, Int) -> Either String (ContentType, Int) toContentInfo (rawType, size) = (,size) <$> toContentType rawType getJSON :: FromJSON a => [MsgBodyContent] -> Either String a @@ -161,12 +151,18 @@ rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = XInfo profile -> let chatMsgBody = rawWithDAG [jsonBody profile] in RawChatMessage {chatMsgId, chatMsgEvent = "x.info", chatMsgParams = [], chatMsgBody} - XGrpInv invId memId role groupProfile -> - let chatMsgParams = [B64.encode invId, B64.encode memId, serializeMemberRole role] + XGrpInv (fromMemId, fromRole) (memId, role) qInfo groupProfile -> + let chatMsgParams = + [ B64.encode fromMemId, + serializeMemberRole fromRole, + B64.encode memId, + serializeMemberRole role, + serializeSmpQueueInfo qInfo + ] chatMsgBody = rawWithDAG [jsonBody groupProfile] in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.inv", chatMsgParams, chatMsgBody} - XGrpAcpt invId qInfo -> - let chatMsgParams = [B64.encode invId, serializeSmpQueueInfo qInfo] + XGrpAcpt memId -> + let chatMsgParams = [B64.encode memId] in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.acpt", chatMsgParams, chatMsgBody = []} XGrpMemNew memId role profile -> let chatMsgParams = [B64.encode memId, serializeMemberRole role] diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 86a2cb9200..bc7d726d8a 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -8,6 +8,7 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeOperators #-} module Simplex.Chat.Store ( SQLiteStore, @@ -19,16 +20,23 @@ module Simplex.Chat.Store createDirectConnection, createDirectContact, deleteContact, - getContactConnection, + getContact, getContactConnections, getConnectionChatDirection, + createNewGroup, + createGroup, + getGroup, + createGroupMember, ) where +import Control.Concurrent.STM (stateTVar) import Control.Exception (Exception) import qualified Control.Exception as E import Control.Monad.Except import Control.Monad.IO.Unlift +import Crypto.Random (ChaChaDRG, randomBytesGenerate) +import qualified Data.ByteString.Base64 as B64 import Data.ByteString.Char8 (ByteString) import Data.FileEmbed (embedDir, makeRelativeToProject) import Data.Function (on) @@ -39,7 +47,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (decodeUtf8) import Data.Time.Clock (UTCTime) -import Database.SQLite.Simple (NamedParam (..), Only (..), SQLError) +import Database.SQLite.Simple (NamedParam (..), Only (..), SQLError, (:.) (..)) import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Protocol @@ -49,6 +57,7 @@ import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) import Simplex.Messaging.Util (bshow, liftIOEither, (<$$>)) import System.FilePath (takeBaseName, takeExtension) +import UnliftIO.STM -- | The list of migrations in ascending order by date migrations :: [Migration] @@ -71,22 +80,22 @@ handleSQLError err e | otherwise = SEInternal $ bshow e insertedRowId :: DB.Connection -> IO Int64 -insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid();" +insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()" createUser :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> Profile -> Bool -> m User createUser st Profile {contactRef, displayName} activeUser = liftIOEither . checkConstraint SEDuplicateContactRef . withTransaction st $ \db -> do - DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?);" (contactRef, displayName) + DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?)" (contactRef, displayName) profileId <- insertedRowId db - DB.execute db "INSERT INTO users (contact_id, active_user) VALUES (0, ?);" (Only activeUser) + DB.execute db "INSERT INTO users (contact_id, active_user) VALUES (0, ?)" (Only activeUser) userId <- insertedRowId db DB.execute db - "INSERT INTO contacts (contact_profile_id, local_contact_ref, lcr_base, user_id, user) VALUES (?, ?, ?, ?, 1);" - (profileId, contactRef, contactRef, userId) + "INSERT INTO contacts (contact_profile_id, local_contact_ref, lcr_base, user_id, is_user) VALUES (?, ?, ?, ?, ?)" + (profileId, contactRef, contactRef, userId, True) contactId <- insertedRowId db - DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?;" (contactId, userId) - pure . Right $ toUser (userId, activeUser, contactRef, displayName) + DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?" (contactId, userId) + pure . Right $ toUser (userId, contactId, activeUser, contactRef, displayName) getUsers :: SQLiteStore -> IO [User] getUsers st = @@ -95,22 +104,22 @@ getUsers st = <$> DB.query_ db [sql| - SELECT u.user_id, u.active_user, c.local_contact_ref, p.display_name + SELECT u.user_id, u.contact_id, u.active_user, c.local_contact_ref, p.display_name FROM users u JOIN contacts c ON u.contact_id = c.contact_id JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id |] -toUser :: (UserId, Bool, ContactRef, Text) -> User -toUser (userId, activeUser, contactRef, displayName) = +toUser :: (UserId, Int64, Bool, ContactRef, Text) -> User +toUser (userId, userContactId, activeUser, contactRef, displayName) = let profile = Profile {contactRef, displayName} - in User {userId, localContactRef = contactRef, profile, activeUser} + in User {userId, userContactId, localContactRef = contactRef, profile, activeUser} setActiveUser :: MonadUnliftIO m => SQLiteStore -> UserId -> m () setActiveUser st userId = do liftIO . withTransaction st $ \db -> do - DB.execute_ db "UPDATE users SET active_user = 0;" - DB.execute db "UPDATE users SET active_user = 1 WHERE user_id = ?;" (Only userId) + DB.execute_ db "UPDATE users SET active_user = 0" + DB.execute db "UPDATE users SET active_user = 1 WHERE user_id = ?" (Only userId) createDirectConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> m () createDirectConnection st userId agentConnId = @@ -127,7 +136,7 @@ createDirectContact :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> Connection -> Profile -> m () createDirectContact st userId Connection {connId} Profile {contactRef, displayName} = liftIOEither . withTransaction st $ \db -> do - DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?);" (contactRef, displayName) + DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?)" (contactRef, displayName) profileId <- insertedRowId db lcrSuffix <- getLcrSuffix db create db profileId lcrSuffix 20 @@ -141,7 +150,7 @@ createDirectContact st userId Connection {connId} Profile {contactRef, displayNa SELECT lcr_suffix FROM contacts WHERE user_id = :user_id AND lcr_base = :contact_ref ORDER BY lcr_suffix DESC - LIMIT 1; + LIMIT 1 |] [":user_id" := userId, ":contact_ref" := contactRef] create :: DB.Connection -> Int64 -> Int -> Int -> IO (Either StoreError ()) @@ -179,24 +188,62 @@ deleteContact st userId contactRef = FROM connections c JOIN contacts cs ON c.contact_id = cs.contact_id WHERE cs.user_id = :user_id AND cs.local_contact_ref = :contact_ref - ); + ) |] [":user_id" := userId, ":contact_ref" := contactRef] DB.executeNamed db [sql| DELETE FROM contacts - WHERE user_id = :user_id AND local_contact_ref = :contact_ref; + WHERE user_id = :user_id AND local_contact_ref = :contact_ref |] [":user_id" := userId, ":contact_ref" := contactRef] -- TODO return the last connection that is ready, not any last connection -- requires updating connection status -getContactConnection :: - (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m Connection -getContactConnection st userId contactRef = +getContact :: + (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m Contact +getContact st userId localContactRef = + liftIOEither . withTransaction st $ \db -> runExceptT $ do + c@Contact {contactId} <- getContact_ db + activeConn <- getConnection_ db contactId + pure $ (c :: Contact) {activeConn} + where + getContact_ db = ExceptT $ do + toContact + <$> DB.queryNamed + db + [sql| + SELECT c.contact_id, p.contact_ref, p.display_name + FROM contacts c + JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id + WHERE c.user_id = :user_id AND c.local_contact_ref = :local_contact_ref AND c.is_user = :is_user + |] + [":user_id" := userId, ":local_contact_ref" := localContactRef, ":is_user" := False] + getConnection_ db contactId = ExceptT $ do + connection + <$> DB.queryNamed + db + [sql| + SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM connections c + WHERE c.user_id = :user_id AND c.contact_id == :contact_id + ORDER BY c.connection_id DESC + LIMIT 1 + |] + [":user_id" := userId, ":contact_id" := contactId] + toContact [(contactId, contactRef, displayName)] = + let profile = Profile {contactRef, displayName} + in Right Contact {contactId, localContactRef, profile, activeConn = undefined} + toContact _ = Left $ SEContactNotFound localContactRef + connection (connRow : _) = Right $ toConnection connRow + connection _ = Left $ SEContactNotReady localContactRef + +getContactConnections :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m [Connection] +getContactConnections st userId contactRef = liftIOEither . withTransaction st $ \db -> - connection + connections <$> DB.queryNamed db [sql| @@ -207,33 +254,15 @@ getContactConnection st userId contactRef = WHERE c.user_id = :user_id AND cs.user_id = :user_id AND cs.local_contact_ref == :contact_ref - ORDER BY c.connection_id DESC - LIMIT 1; |] [":user_id" := userId, ":contact_ref" := contactRef] where - connection (connRow : _) = Right $ toConnection connRow - connection _ = Left $ SEContactNotFound contactRef + connections [] = Left $ SEContactNotFound contactRef + connections rows = Right $ map toConnection rows -getContactConnections :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactRef -> m [Connection] -getContactConnections st userId contactRef = - liftIO . withTransaction st $ \db -> - map toConnection - <$> DB.queryNamed - db - [sql| - SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at - FROM connections c - JOIN contacts cs ON c.contact_id == cs.contact_id - WHERE c.user_id = :user_id - AND cs.user_id = :user_id - AND cs.local_contact_ref == :contact_ref; - |] - [":user_id" := userId, ":contact_ref" := contactRef] +type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, UTCTime) -toConnection :: - (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, UTCTime) -> Connection +toConnection :: ConnectionRow -> Connection toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) = let entityId = entityId_ connType in Connection {connId, agentConnId, connLevel, viaContact, connStatus, connType, entityId, createdAt} @@ -246,16 +275,16 @@ getConnectionChatDirection :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ConnId -> m (ChatDirection 'Agent) getConnectionChatDirection st userId agentConnId = liftIOEither . withTransaction st $ \db -> do - getConnection db >>= \case + getConnection_ db >>= \case Left e -> pure $ Left e Right c@Connection {connType, entityId} -> case connType of ConnMember -> pure . Left $ SEInternal "group members not supported yet" ConnContact -> ReceivedDirectMessage <$$> case entityId of - Nothing -> pure $ Right NewContact {activeConn = c} - Just cId -> getContact db cId c + Nothing -> pure . Right $ CConnection c + Just cId -> getContact_ db cId c where - getConnection db = + getConnection_ db = connection <$> DB.query db @@ -263,12 +292,12 @@ getConnectionChatDirection st userId agentConnId = SELECT connection_id, agent_conn_id, conn_level, via_contact, conn_status, conn_type, contact_id, group_member_id, created_at FROM connections - WHERE user_id = ? AND agent_conn_id = ?; + WHERE user_id = ? AND agent_conn_id = ? |] (userId, agentConnId) connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEConnectionNotFound agentConnId - getContact db contactId c = + getContact_ db contactId c = toContact contactId c <$> DB.query db @@ -281,12 +310,233 @@ getConnectionChatDirection st userId agentConnId = (userId, contactId) toContact contactId c [(localContactRef, contactRef, displayName)] = let profile = Profile {contactRef, displayName} - in Right Contact {contactId, localContactRef, profile, activeConn = c} + in Right $ CContact Contact {contactId, localContactRef, profile, activeConn = c} toContact _ _ _ = Left $ SEInternal "referenced contact not found" +-- | creates completely new group with a single member - the current user +createNewGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> GroupProfile -> m Group +createNewGroup st gVar User {userId, userContactId, profile} p@GroupProfile {groupRef, displayName} = + liftIOEither . checkConstraint SEDuplicateGroupRef . withTransaction st $ \db -> do + -- group inserted before profile to ensure its local_group_ref is unique + DB.execute db "INSERT INTO groups (local_group_ref, lgr_base, user_id) VALUES (?, ?, ?)" (groupRef, groupRef, userId) + groupId <- insertedRowId db + DB.execute db "INSERT INTO group_profiles (group_ref, display_name) VALUES (?, ?)" (groupRef, displayName) + profileId <- insertedRowId db + DB.execute db "UPDATE groups SET group_profile_id = ? WHERE group_id = ?" (profileId, groupId) + memberId <- randomId gVar 12 + createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId + groupMemberId <- insertedRowId db + let membership = + GroupMember + { groupMemberId, + memberId, + memberRole = GROwner, + memberStatus = GSMemReady, + invitedBy = IBUser, + memberProfile = profile, + memberContactId = Just userContactId + } + pure $ Right Group {groupId, localGroupRef = groupRef, groupProfile = p, members = [], membership} + +-- | creates a new group record for the group the current user was invited to +createGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> Contact -> GroupProfile -> m Group +createGroup st gVar User {userId, userContactId, profile} contact p@GroupProfile {groupRef, displayName} = + liftIOEither . withTransaction st $ \db -> do + DB.execute db "INSERT INTO group_profiles (group_ref, display_name) VALUES (?, ?)" (groupRef, displayName) + profileId <- insertedRowId db + lgrSuffix <- getLgrSuffix db + group <- create db profileId lgrSuffix 20 + pure group + where + -- createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId + -- groupMemberId <- insertedRowId db + -- let membership = + -- GroupMember + -- { groupMemberId, + -- memberId, + -- memberRole = GROwner, + -- memberStatus = GSMemReady, + -- invitedBy = IBUser, + -- memberProfile = profile, + -- memberContactId = Just userContactId + -- } + -- pure $ Right Group {groupId, localGroupRef = groupRef, groupProfile = p, members = [], membership} + + getLgrSuffix :: DB.Connection -> IO Int + getLgrSuffix db = + maybe 0 ((+ 1) . fromOnly) . listToMaybe + <$> DB.queryNamed + db + [sql| + SELECT lgr_suffix FROM groups + WHERE user_id = :user_id AND lgr_base = :group_ref + ORDER BY lgr_suffix DESC + LIMIT 1 + |] + [":user_id" := userId, ":group_ref" := groupRef] + create :: DB.Connection -> Int64 -> Int -> Int -> IO (Either StoreError Group) + create _ _ _ 0 = pure $ Left SEDuplicateGroupRef + create db profileId lgrSuffix attempts = do + let lgr = localGroupRef' lgrSuffix + E.try (insertGroup lgr) >>= \case + Right () -> do + groupId <- insertedRowId db + pure $ Right Group {groupId, localGroupRef = lgr, groupProfile = p, members = undefined, membership = undefined} + Left e + | DB.sqlError e == DB.ErrorConstraint -> create db profileId (lgrSuffix + 1) (attempts - 1) + | otherwise -> E.throwIO e + where + localGroupRef' 0 = groupRef + localGroupRef' n = groupRef <> T.pack ('_' : show n) + insertGroup lgr = + DB.execute + db + [sql| + INSERT INTO groups + (group_profile_id, local_group_ref, lgr_base, lgr_suffix, user_id) VALUES (?, ?, ?, ?, ?) + |] + (profileId, lgr, groupRef, lgrSuffix, userId) + +-- TODO return the last connection that is ready, not any last connection +-- requires updating connection status +getGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> User -> GroupRef -> m Group +getGroup st User {userId, userContactId} localGroupRef = + liftIOEither . withTransaction st $ \db -> runExceptT $ do + g@Group {groupId} <- getGroup_ db + members <- getMembers_ db groupId + membership <- getUserMember_ db groupId + pure g {members, membership} + where + getGroup_ :: DB.Connection -> ExceptT StoreError IO Group + getGroup_ db = ExceptT $ do + toGroup + <$> DB.query + db + [sql| + SELECT g.group_id, p.group_ref, p.display_name + FROM groups g + JOIN group_profiles p ON p.group_profile_id = g.group_profile_id + WHERE g.local_group_ref = ? AND g.user_id = ? + |] + (localGroupRef, userId) + toGroup :: [(Int64, GroupRef, Text)] -> Either StoreError Group + toGroup [(groupId, groupRef, displayName)] = + let groupProfile = GroupProfile {groupRef, displayName} + in Right Group {groupId, localGroupRef, groupProfile, members = undefined, membership = undefined} + toGroup _ = Left $ SEGroupNotFound localGroupRef + getMembers_ :: DB.Connection -> Int64 -> ExceptT StoreError IO [(GroupMember, Connection)] + getMembers_ db groupId = ExceptT $ do + Right . map toContactMember + <$> DB.query + db + [sql| + SELECT + m.group_member_id, m.member_id, m.member_role, m.member_status, + m.invited_by, m.contact_id, p.contact_ref, p.display_name, + c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM group_members m + JOIN groups g ON g.group_id = m.group_id + JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id + JOIN connections c ON c.group_member_id = m.group_member_id + WHERE g.group_id = ? + ORDER BY c.connection_id DESC + LIMIT 1 + |] + (Only groupId) + getUserMember_ :: DB.Connection -> Int64 -> ExceptT StoreError IO GroupMember + getUserMember_ db groupId = ExceptT $ do + userMember + <$> DB.query + db + [sql| + SELECT + m.group_member_id, m.member_id, m.member_role, m.member_status, + m.invited_by, m.contact_id, p.contact_ref, p.display_name + FROM group_members m + JOIN groups g ON g.group_id = m.group_id + JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id + WHERE g.group_id = ? AND m.contact_id = ? + |] + (groupId, userContactId) + toContactMember :: (GroupMemberRow :. ConnectionRow) -> (GroupMember, Connection) + toContactMember (memberRow :. connRow) = (toGroupMember memberRow, toConnection connRow) + toGroupMember :: GroupMemberRow -> GroupMember + toGroupMember (groupMemberId, memberId, memberRole, memberStatus, invitedById, memberContactId, contactRef, displayName) = + let memberProfile = Profile {contactRef, displayName} + invitedBy = toInvitedBy userContactId invitedById + in GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} + userMember :: [GroupMemberRow] -> Either StoreError GroupMember + userMember [memberRow] = Right $ toGroupMember memberRow + userMember _ = Left SEGroupWithoutUser + +type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberStatus, Maybe Int64, Maybe Int64, ContactRef, Text) + +createGroupMember :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Int64 -> GroupMemberRole -> InvitedBy -> ConnId -> m MemberId +createGroupMember st gVar User {userId, userContactId} groupId contactId memberRole invitedBy agentConnId = + liftIOEither . withTransaction st $ \db -> do + let invitedById = fromInvitedBy userContactId invitedBy + memberId <- createWithRandomId gVar $ createMember_ db groupId contactId memberRole GSMemInvited invitedById + groupMemberId <- insertedRowId db + liftIO $ createMemberConnection_ db groupMemberId + pure memberId + where + createMemberConnection_ :: DB.Connection -> Int64 -> IO () + createMemberConnection_ db groupMemberId = + DB.execute + db + [sql| + INSERT INTO connections + (user_id, agent_conn_id, conn_status, conn_type, group_member_id) VALUES (?,?,?,?,?); + |] + (userId, agentConnId, ConnNew, ConnMember, groupMemberId) + +createMember_ :: DB.Connection -> Int64 -> Int64 -> GroupMemberRole -> GroupMemberStatus -> Maybe Int64 -> ByteString -> IO () +createMember_ db groupId contactId memberRole memberStatus invitedBy memberId = + DB.executeNamed + db + [sql| + INSERT INTO group_members + ( group_id, member_id, member_role, member_status, invited_by, + contact_profile_id, contact_id) + VALUES + (:group_id,:member_id,:member_role,:member_status,:invited_by, + (SELECT contact_profile_id FROM contacts WHERE contact_id = :contact_id), + :contact_id) + |] + [ ":group_id" := groupId, + ":member_id" := memberId, + ":member_role" := memberRole, + ":member_status" := memberStatus, + ":invited_by" := invitedBy, + ":contact_id" := contactId + ] + +createWithRandomId :: TVar ChaChaDRG -> (ByteString -> IO ()) -> IO (Either StoreError ByteString) +createWithRandomId gVar create = tryCreate 3 + where + tryCreate :: Int -> IO (Either StoreError ByteString) + tryCreate 0 = pure $ Left SEUniqueID + tryCreate n = do + id' <- randomId gVar 12 + E.try (create id') >>= \case + Right _ -> pure $ Right id' + Left e + | DB.sqlError e == DB.ErrorConstraint -> tryCreate (n - 1) + | otherwise -> pure . Left . SEInternal $ bshow e + +randomId :: TVar ChaChaDRG -> Int -> IO ByteString +randomId gVar n = B64.encode <$> (atomically . stateTVar gVar $ randomBytesGenerate n) + data StoreError = SEDuplicateContactRef | SEContactNotFound ContactRef + | SEContactNotReady ContactRef + | SEDuplicateGroupRef + | SEGroupNotFound GroupRef + | SEGroupWithoutUser + | SEDuplicateGroupMember | SEConnectionNotFound ConnId + | SEUniqueID | SEInternal ByteString deriving (Show, Exception) diff --git a/src/Simplex/Chat/Styled.hs b/src/Simplex/Chat/Styled.hs index 45e7f87c21..c344ddf4f6 100644 --- a/src/Simplex/Chat/Styled.hs +++ b/src/Simplex/Chat/Styled.hs @@ -3,7 +3,6 @@ module Simplex.Chat.Styled ( StyledString (..), - bPlain, plain, styleMarkdown, styleMarkdownText, @@ -28,12 +27,6 @@ instance Monoid StyledString where mempty = plain "" instance IsString StyledString where fromString = plain -plain :: String -> StyledString -plain = Styled [] - -bPlain :: ByteString -> StyledString -bPlain = Styled [] . B.unpack - styleMarkdownText :: Text -> StyledString styleMarkdownText = styleMarkdown . parseMarkdown @@ -48,12 +41,19 @@ wrap c s = plain [c] <> s <> plain [c] class StyledFormat a where styled :: Format -> a -> StyledString + plain :: a -> StyledString -instance StyledFormat String where styled = Styled . sgr +instance StyledFormat String where + styled = Styled . sgr + plain = Styled [] -instance StyledFormat ByteString where styled f = styled f . B.unpack +instance StyledFormat ByteString where + styled f = styled f . B.unpack + plain = Styled [] . B.unpack -instance StyledFormat Text where styled f = styled f . T.unpack +instance StyledFormat Text where + styled f = styled f . T.unpack + plain = Styled [] . T.unpack sgr :: Format -> [SGR] sgr = \case diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 6f9d089b84..81a7a5b677 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -1,6 +1,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Simplex.Chat.Types where @@ -8,10 +9,15 @@ module Simplex.Chat.Types where import Data.Aeson (FromJSON, ToJSON) import qualified Data.Aeson as J import Data.ByteString.Char8 (ByteString) +import qualified Data.ByteString.Char8 as B import Data.Int (Int64) import Data.Text (Text) import Data.Time.Clock (UTCTime) -import Database.SQLite.Simple.FromField (FromField (..)) +import Data.Typeable (Typeable) +import Database.SQLite.Simple (ResultError (..), SQLData (..)) +import Database.SQLite.Simple.FromField (FieldParser, FromField (..), returnError) +import Database.SQLite.Simple.Internal (Field (..)) +import Database.SQLite.Simple.Ok (Ok (Ok)) import Database.SQLite.Simple.ToField (ToField (..)) import GHC.Generics import Simplex.Messaging.Agent.Protocol (ConnId) @@ -19,6 +25,7 @@ import Simplex.Messaging.Agent.Store.SQLite (fromTextField_) data User = User { userId :: UserId, + userContactId :: Int64, localContactRef :: ContactRef, profile :: Profile, activeUser :: Bool @@ -26,23 +33,27 @@ data User = User type UserId = Int64 -data Contact - = Contact - { contactId :: Int64, - localContactRef :: ContactRef, - profile :: Profile, - activeConn :: Connection - } - | NewContact {activeConn :: Connection} +data Contact = Contact + { contactId :: Int64, + localContactRef :: ContactRef, + profile :: Profile, + activeConn :: Connection + } deriving (Eq, Show) +contactConnId :: Contact -> ConnId +contactConnId Contact {activeConn = Connection {agentConnId}} = agentConnId + type ContactRef = Text type GroupRef = Text data Group = Group { groupId :: Int64, - localGroupRef :: Text + localGroupRef :: Text, + groupProfile :: GroupProfile, + members :: [(GroupMember, Connection)], + membership :: GroupMember } deriving (Eq, Show) @@ -66,6 +77,84 @@ instance ToJSON GroupProfile where toEncoding = J.genericToEncoding J.defaultOpt instance FromJSON GroupProfile +data GroupMember = GroupMember + { groupMemberId :: Int64, + memberId :: MemberId, + memberRole :: GroupMemberRole, + memberStatus :: GroupMemberStatus, + invitedBy :: InvitedBy, + memberProfile :: Profile, + memberContactId :: Maybe Int64 + } + deriving (Eq, Show) + +type MemberId = ByteString + +data InvitedBy = IBContact Int64 | IBUser | IBUnknown + deriving (Eq, Show) + +toInvitedBy :: Int64 -> Maybe Int64 -> InvitedBy +toInvitedBy userCtId (Just ctId) + | userCtId == ctId = IBUser + | otherwise = IBContact ctId +toInvitedBy _ Nothing = IBUnknown + +fromInvitedBy :: Int64 -> InvitedBy -> Maybe Int64 +fromInvitedBy userCtId = \case + IBUnknown -> Nothing + IBContact ctId -> Just ctId + IBUser -> Just userCtId + +data GroupMemberRole = GRMember | GRAdmin | GROwner + deriving (Eq, Show, Ord) + +instance FromField GroupMemberRole where fromField = fromBlobField_ toMemberRole + +instance ToField GroupMemberRole where toField = toField . serializeMemberRole + +toMemberRole :: ByteString -> Either String GroupMemberRole +toMemberRole = \case + "owner" -> Right GROwner + "admin" -> Right GRAdmin + "member" -> Right GRMember + r -> Left $ "invalid group member role " <> B.unpack r + +serializeMemberRole :: GroupMemberRole -> ByteString +serializeMemberRole = \case + GROwner -> "owner" + GRAdmin -> "admin" + GRMember -> "member" + +fromBlobField_ :: Typeable k => (ByteString -> Either String k) -> FieldParser k +fromBlobField_ p = \case + f@(Field (SQLBlob b) _) -> + case p b of + Right k -> Ok k + Left e -> returnError ConversionFailed f ("couldn't parse field: " ++ e) + f -> returnError ConversionFailed f "expecting SQLBlob column type" + +data GroupMemberStatus = GSMemInvited | GSMemAccepted | GSMemConnected | GSMemReady + deriving (Eq, Show) + +instance FromField GroupMemberStatus where fromField = fromTextField_ memberStatusT + +instance ToField GroupMemberStatus where toField = toField . serializeMemberStatus + +memberStatusT :: Text -> Maybe GroupMemberStatus +memberStatusT = \case + "invited" -> Just GSMemInvited + "accepted" -> Just GSMemAccepted + "connected" -> Just GSMemConnected + "ready" -> Just GSMemReady + _ -> Nothing + +serializeMemberStatus :: GroupMemberStatus -> Text +serializeMemberStatus = \case + GSMemInvited -> "invited" + GSMemAccepted -> "accepted" + GSMemConnected -> "connected" + GSMemReady -> "ready" + data Connection = Connection { connId :: Int64, agentConnId :: ConnId, diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 0a34d67c4a..bb7cc01ec7 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -1,6 +1,8 @@ {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Simplex.Chat.View @@ -12,6 +14,7 @@ module Simplex.Chat.View showContactDisconnected, showReceivedMessage, showSentMessage, + showGroupCreated, safeDecodeUtf8, ) where @@ -26,6 +29,7 @@ import Data.Time.Format (defaultTimeLocale, formatTime) import Data.Time.LocalTime (TimeZone, ZonedTime, getCurrentTimeZone, getZonedTime, localDay, localTimeOfDay, timeOfDayToTime, utcToLocalTime, zonedTimeToLocalTime) import Simplex.Chat.Controller import Simplex.Chat.Markdown +import Simplex.Chat.Store (StoreError (..)) import Simplex.Chat.Styled import Simplex.Chat.Terminal (printToTerminal) import Simplex.Chat.Types @@ -56,11 +60,14 @@ showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage showSentMessage :: ChatReader m => ContactRef -> ByteString -> m () showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) +showGroupCreated :: ChatReader m => GroupProfile -> m () +showGroupCreated = printToView . groupCreated + invitation :: SMPQueueInfo -> [StyledString] invitation qInfo = [ "pass this invitation to your contact (via another channel): ", "", - (bPlain . serializeSmpQueueInfo) qInfo, + (plain . serializeSmpQueueInfo) qInfo, "", "and ask them to connect: /c " ] @@ -74,6 +81,9 @@ contactConnected c = [ttyContact c <> " is connected"] contactDisconnected :: ContactRef -> [StyledString] contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] +groupCreated :: GroupProfile -> [StyledString] +groupCreated GroupProfile {groupRef, displayName} = ["group " <> ttyGroup groupRef <> " (" <> plain displayName <> ") is created"] + receivedMessage :: ContactRef -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage c utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime @@ -114,9 +124,11 @@ msgPlain = map styleMarkdownText . T.lines chatError :: ChatError -> [StyledString] chatError = \case - ChatErrorContact e -> case e of - CENotFound c -> ["no contact " <> ttyContact c] - CEProfile s -> ["invalid profile: " <> plain s] + ChatErrorStore err -> case err of + SEContactNotFound c -> ["no contact " <> ttyContact c] + SEContactNotReady c -> ["contact " <> ttyContact c <> " is not active yet"] + SEDuplicateGroupRef -> ["group with this alias already exists"] + e -> ["chat db error: " <> plain (show e)] ChatErrorAgent err -> case err of -- CONN e -> case e of -- -- TODO replace with ChatErrorContact errors, these errors should never happen @@ -138,8 +150,8 @@ ttyToContact c = styled (Colored Cyan) $ "@" <> c <> " " ttyFromContact :: ContactRef -> StyledString ttyFromContact c = styled (Colored Yellow) $ c <> "> " --- ttyGroup :: Group -> StyledString --- ttyGroup (Group g) = styled (Colored Blue) $ "#" <> g +ttyGroup :: GroupRef -> StyledString +ttyGroup g = styled (Colored Blue) $ "#" <> g -- ttyFromGroup :: Group -> Contact -> StyledString -- ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md index 9821f108f5..d363800b48 100644 --- a/src/Simplex/Chat/protocol.md +++ b/src/Simplex/Chat/protocol.md @@ -69,13 +69,15 @@ refMsgHash = 16*16(OCTET) ; SHA256 of agent message body ### Group protocol -A -> B: invite to group - `MSG: x.grp.inv G_INV_ID,G_MEM_ID_B,G_MEM_ROLE x.json:NNN ` +#### Add group member + +A -> B: invite to group - `MSG: x.grp.inv G_MEM_ID_A,G_MEM_ROLE_A,G_MEM_ID_B,G_MEM_ROLE_B, x.json:NNN ` user B confirms -B -> A: join group - `MSG: x.grp.acpt G_INV_ID,` -A -> Bg: establish group connection (A: JOIN, B: LET) -A -> group (including B)): announce group member: `MSG: N x.grp.mem.new G_MEM_ID_B,G_MEM_ROLE x.json:NNN ` +B -> A: establish group connection (B: JOIN, A: LET) +B -> Ag: join group - `in SMP confirmation: x.grp.acpt G_MEM_ID_B` +A -> group (including B)): announce group member: `MSG: N x.grp.mem.new G_MEM_ID_B,G_MEM_ROLE_B x.json:NNN ` subsequent messages between A and B are via group connection -A -> Bg: intro member - `MSG: x.grp.mem.intro G_MEM_ID_M,G_MEM_ROLE x.json:NNN ` +A -> Bg: intro member - `MSG: x.grp.mem.intro G_MEM_ID_M,G_MEM_ROLE_M x.json:NNN ` B -> Ag: inv for mem - `MSG: x.grp.mem.inv G_MEM_ID_M,,,` M is an existing member, messages are via group connection A -> Mg: fwd inv - `MSG: x.grp.mem.fwd G_MEM_ID_B,,,` @@ -91,3 +93,7 @@ M -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_B` once all members connected A -> group: `MSG: N x.grp.mem.ok G_MEM_ID_B` + +#### Send group message + +`MSG: N x.msg.new G_MEM_ROLE, x.json:NNN ` diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 6752966064..95947f1ceb 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -20,7 +20,7 @@ bobProfile = Profile {contactRef = "bob", displayName = "Bob"} testAddContact :: Spec testAddContact = describe "add chat contact" $ - xit "add contact and send/receive message" $ + it "add contact and send/receive message" $ testChat2 aliceProfile bobProfile $ \alice bob -> do alice ##> "/a" Just inv <- invitation <$> getWindow alice diff --git a/tests/Test.hs b/tests/Test.hs index f9c0b770e0..990d86ca0b 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -10,5 +10,5 @@ main = do hspec $ do describe "SimpleX chat markdown" markdownTests describe "SimpleX chat protocol" protocolTests - describe "SimpleX chat client" testAddContact + xdescribe "SimpleX chat client" testAddContact removeDirectoryRecursive "tests/tmp" From e9d931059b1096ca7379b9f6352cc5bd682951f7 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 14 Jul 2021 20:11:41 +0100 Subject: [PATCH 10/29] use shared namespace for usernames, contact names and group names (#76) * test adding same contact, add display_names table and functions * rename display_name -> full_name * use shared namespace for usernames, contact names and group names --- migrations/20210612_initial.sql | 50 ++++-- src/Simplex/Chat.hs | 71 ++++---- src/Simplex/Chat/Protocol_.hs | 44 ----- src/Simplex/Chat/Store.hs | 309 +++++++++++++++----------------- src/Simplex/Chat/Terminal.hs | 2 +- src/Simplex/Chat/Types.hs | 18 +- src/Simplex/Chat/View.hs | 32 ++-- tests/ChatTests.hs | 28 ++- 8 files changed, 262 insertions(+), 292 deletions(-) delete mode 100644 src/Simplex/Chat/Protocol_.hs diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index 2619908930..1da08a17ed 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -1,7 +1,7 @@ CREATE TABLE contact_profiles ( -- remote user profile contact_profile_id INTEGER PRIMARY KEY, - contact_ref TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces - display_name TEXT NOT NULL, + display_name TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces + full_name TEXT NOT NULL, properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties ); @@ -9,20 +9,35 @@ CREATE TABLE users ( user_id INTEGER PRIMARY KEY, contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, - active_user INTEGER -- 1 for active user + local_display_name TEXT NOT NULL UNIQUE, + active_user INTEGER NOT NULL DEFAULT 0, -- 1 for active user + FOREIGN KEY (user_id, local_display_name) + REFERENCES display_names (user_id, local_display_name) + ON DELETE RESTRICT + DEFERRABLE INITIALLY DEFERRED ); +CREATE TABLE display_names ( + user_id INTEGER NOT NULL REFERENCES users, + local_display_name TEXT NOT NULL, + ldn_base TEXT NOT NULL, + ldn_suffix INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, local_display_name) ON CONFLICT FAIL, + UNIQUE (user_id, ldn_base, ldn_suffix) ON CONFLICT FAIL +) WITHOUT ROWID; + CREATE TABLE contacts ( contact_id INTEGER PRIMARY KEY, - contact_profile_id INTEGER UNIQUE REFERENCES contact_profiles, -- NULL if it's an incognito profile - local_contact_ref TEXT NOT NULL, - lcr_base TEXT NOT NULL, - lcr_suffix INTEGER NOT NULL DEFAULT 0, + contact_profile_id INTEGER REFERENCES contact_profiles, -- NULL if it's an incognito profile user_id INTEGER NOT NULL REFERENCES users, + local_display_name TEXT NOT NULL, is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user created_at TEXT NOT NULL DEFAULT (datetime('now')), - UNIQUE (user_id, local_contact_ref) ON CONFLICT FAIL, - UNIQUE (user_id, lcr_base, lcr_suffix) ON CONFLICT FAIL + FOREIGN KEY (user_id, local_display_name) + REFERENCES display_names (user_id, local_display_name) + ON DELETE RESTRICT, + UNIQUE (user_id, local_display_name), + UNIQUE (user_id, contact_profile_id) ); CREATE TABLE known_servers( @@ -36,20 +51,21 @@ CREATE TABLE known_servers( CREATE TABLE group_profiles ( -- shared group profiles group_profile_id INTEGER PRIMARY KEY, - group_ref TEXT NOT NULL, -- this name must not contain spaces - display_name TEXT NOT NULL, + display_name TEXT NOT NULL, -- this name must not contain spaces + full_name TEXT NOT NULL, properties TEXT NOT NULL DEFAULT '{}' -- JSON with user or contact profile ); CREATE TABLE groups ( group_id INTEGER PRIMARY KEY, -- local group ID - local_group_ref TEXT NOT NULL, -- local group name without spaces - lgr_base TEXT NOT NULL, - lgr_suffix INTEGER NOT NULL DEFAULT 0, - group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile user_id INTEGER NOT NULL REFERENCES users, - UNIQUE (user_id, local_group_ref) ON CONFLICT FAIL, - UNIQUE (user_id, lgr_base, lgr_suffix) ON CONFLICT FAIL + local_display_name TEXT NOT NULL, -- local group name without spaces + group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile + FOREIGN KEY (user_id, local_display_name) + REFERENCES display_names (user_id, local_display_name) + ON DELETE RESTRICT, + UNIQUE (user_id, local_display_name), + UNIQUE (user_id, group_profile_id) ); CREATE TABLE group_members ( -- group members, excluding the local user diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 65914a207b..c77c7cbff4 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -55,16 +55,16 @@ data ChatCommand | MarkdownHelp | AddContact | Connect SMPQueueInfo - | DeleteContact ContactRef - | SendMessage ContactRef ByteString + | DeleteContact ContactName + | SendMessage ContactName ByteString | NewGroup GroupProfile - | AddMember GroupRef ContactRef GroupMemberRole - | RemoveMember GroupRef ContactRef - | MemberRole GroupRef ContactRef GroupMemberRole - | LeaveGroup GroupRef - | DeleteGroup GroupRef - | ListMembers GroupRef - | SendGroupMessage GroupRef ByteString + | AddMember GroupName ContactName GroupMemberRole + | RemoveMember GroupName ContactName + | MemberRole GroupName ContactName GroupMemberRole + | LeaveGroup GroupName + | DeleteGroup GroupName + | ListMembers GroupName + | SendGroupMessage GroupName ByteString deriving (Show) cfg :: AgentConfig @@ -195,7 +195,7 @@ processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agen processAgentMessage User {userId, profile} agentConnId agentMessage = do chatDirection <- withStore $ \st -> getConnectionChatDirection st userId agentConnId case chatDirection of - ReceivedDirectMessage (CContact ct@Contact {localContactRef = c}) -> + ReceivedDirectMessage (CContact ct@Contact {localDisplayName = c}) -> case agentMessage of MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody @@ -225,7 +225,7 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do _ -> pure () _ -> pure () where - newTextMessage :: ContactRef -> MsgMeta -> Maybe MsgBodyContent -> m () + newTextMessage :: ContactName -> MsgMeta -> Maybe MsgBodyContent -> m () newTextMessage c meta = \case Just MsgBodyContent {contentData = bs} -> do let text = safeDecodeUtf8 bs @@ -268,17 +268,17 @@ getCreateActiveUser st = do newUser = do putStrLn "No user profiles found, it will be created now.\n\ - \Please choose your alias and your profile name.\n\ + \Please choose your display name and your full name.\n\ \They will be sent to your contacts when you connect.\n\ \They are only stored on your device and you can change them later." loop where loop = do - contactRef <- getContactRef - displayName <- T.pack <$> getWithPrompt "profile name (optional)" - liftIO (runExceptT $ createUser st Profile {contactRef, displayName} True) >>= \case - Left SEDuplicateContactRef -> do - putStrLn "chosen alias already used by another profile on this device, choose another one" + displayName <- getContactName + fullName <- T.pack <$> getWithPrompt "full name (optional)" + liftIO (runExceptT $ createUser st Profile {displayName, fullName} True) >>= \case + Left SEDuplicateName -> do + putStrLn "chosen display name is already used by another profile on this device, choose another one" loop Left e -> putStrLn ("database error " <> show e) >> exitFailure Right user -> pure user @@ -302,14 +302,14 @@ getCreateActiveUser st = do liftIO $ setActiveUser st (userId user) pure user userStr :: User -> String - userStr User {localContactRef, profile = Profile {displayName}} = - T.unpack $ localContactRef <> if T.null displayName then "" else " (" <> displayName <> ")" - getContactRef :: IO ContactRef - getContactRef = do - contactRef <- getWithPrompt "alias (no spaces)" - if null contactRef || isJust (find (== ' ') contactRef) - then putStrLn "alias has space(s), choose another one" >> getContactRef - else pure $ T.pack contactRef + userStr User {localDisplayName, profile = Profile {fullName}} = + T.unpack $ localDisplayName <> if T.null fullName then "" else " (" <> fullName <> ")" + getContactName :: IO ContactName + getContactName = do + displayName <- getWithPrompt "display name (no spaces)" + if null displayName || isJust (find (== ' ') displayName) + then putStrLn "display name has space(s), choose another one" >> getContactName + else pure $ T.pack displayName getWithPrompt :: String -> IO String getWithPrompt s = putStr (s <> ": ") >> hFlush stdout >> getLine @@ -340,24 +340,23 @@ chatCommandP :: Parser ChatCommand chatCommandP = ("/help" <|> "/h") $> ChatHelp <|> ("/group #" <|> "/g #") *> (NewGroup <$> groupProfile) - <|> ("/add #" <|> "/a #") *> (AddMember <$> groupRef <* A.space <*> contactRef <*> memberRole) - <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> groupRef <* A.space <*> contactRef) - <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> groupRef) - <|> ("/members #" <|> "/ms #") *> (ListMembers <$> groupRef) - <|> A.char '#' *> (SendGroupMessage <$> groupRef <* A.space <*> A.takeByteString) + <|> ("/add #" <|> "/a #") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) + <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> displayName <* A.space <*> displayName) + <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> displayName) + <|> ("/members #" <|> "/ms #") *> (ListMembers <$> displayName) + <|> A.char '#' *> (SendGroupMessage <$> displayName <* A.space <*> A.takeByteString) <|> ("/add" <|> "/a") $> AddContact <|> ("/connect " <|> "/c ") *> (Connect <$> smpQueueInfoP) - <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> contactRef) - <|> A.char '@' *> (SendMessage <$> contactRef <*> (A.space *> A.takeByteString)) + <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> displayName) + <|> A.char '@' *> (SendMessage <$> displayName <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp where - contactRef = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) + displayName = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) refChar c = c > ' ' && c /= '#' && c /= '@' - groupRef = contactRef groupProfile = do - gRef <- groupRef + gRef <- displayName gName <- safeDecodeUtf8 <$> (A.space *> A.takeByteString) <|> pure "" - pure GroupProfile {groupRef = gRef, displayName = if T.null gName then gRef else gName} + pure GroupProfile {displayName = gRef, fullName = if T.null gName then gRef else gName} memberRole = (" owner" $> GROwner) <|> (" admin" $> GRAdmin) diff --git a/src/Simplex/Chat/Protocol_.hs b/src/Simplex/Chat/Protocol_.hs deleted file mode 100644 index b241ac42b2..0000000000 --- a/src/Simplex/Chat/Protocol_.hs +++ /dev/null @@ -1,44 +0,0 @@ -{-# LANGUAGE DuplicateRecordFields #-} - -module Simplex.Chat.Protocol_ where - -import Data.ByteString (ByteString) -import Data.Text (Text) -import Simplex.Messaging.Agent.Protocol (ConnId) - -data ChatEvent = GroupEvent | MessageEvent | InfoEvent - -data Profile = Profile - { profileId :: ByteString, - displayName :: Text - } - -data Contact = Contact - { contactId :: ByteString, - profile :: Profile, - connections :: [Connection] - } - -data Connection = Connection - { connId :: ConnId, - connLevel :: Int, - viaConn :: ConnId - } - -data GroupMember = GroupMember - { groupId :: ByteString, - sharedMemberId :: ByteString, - contact :: Contact, - memberRole :: GroupMemberRole, - memberStatus :: GroupMemberStatus - } - -data GroupMemberRole = GROwner | GRAdmin | GRStandard - -data GroupMemberStatus = GSInvited | GSConnected | GSConnectedAll - -data Group = Group - { groupId :: ByteString, - displayName :: Text, - members :: [GroupMember] - } diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index bc7d726d8a..86e8ffb5f2 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -8,6 +8,7 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} module Simplex.Chat.Store @@ -83,19 +84,17 @@ insertedRowId :: DB.Connection -> IO Int64 insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()" createUser :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> Profile -> Bool -> m User -createUser st Profile {contactRef, displayName} activeUser = - liftIOEither . checkConstraint SEDuplicateContactRef . withTransaction st $ \db -> do - DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?)" (contactRef, displayName) - profileId <- insertedRowId db - DB.execute db "INSERT INTO users (contact_id, active_user) VALUES (0, ?)" (Only activeUser) +createUser st Profile {displayName, fullName} activeUser = + liftIOEither . checkConstraint SEDuplicateName . withTransaction st $ \db -> do + DB.execute db "INSERT INTO users (local_display_name, active_user, contact_id) VALUES (?, ?, 0)" (displayName, activeUser) userId <- insertedRowId db - DB.execute - db - "INSERT INTO contacts (contact_profile_id, local_contact_ref, lcr_base, user_id, is_user) VALUES (?, ?, ?, ?, ?)" - (profileId, contactRef, contactRef, userId, True) + DB.execute db "INSERT INTO display_names (local_display_name, ldn_base, user_id) VALUES (?, ?, ?)" (displayName, displayName, userId) + DB.execute db "INSERT INTO contact_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) + profileId <- insertedRowId db + DB.execute db "INSERT INTO contacts (contact_profile_id, local_display_name, user_id, is_user) VALUES (?, ?, ?, ?)" (profileId, displayName, userId, True) contactId <- insertedRowId db DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?" (contactId, userId) - pure . Right $ toUser (userId, contactId, activeUser, contactRef, displayName) + pure . Right $ toUser (userId, contactId, activeUser, displayName, fullName) getUsers :: SQLiteStore -> IO [User] getUsers st = @@ -104,16 +103,16 @@ getUsers st = <$> DB.query_ db [sql| - SELECT u.user_id, u.contact_id, u.active_user, c.local_contact_ref, p.display_name + SELECT u.user_id, u.contact_id, u.active_user, u.local_display_name, p.full_name FROM users u JOIN contacts c ON u.contact_id = c.contact_id JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id |] -toUser :: (UserId, Int64, Bool, ContactRef, Text) -> User -toUser (userId, userContactId, activeUser, contactRef, displayName) = - let profile = Profile {contactRef, displayName} - in User {userId, userContactId, localContactRef = contactRef, profile, activeUser} +toUser :: (UserId, Int64, Bool, ContactName, Text) -> User +toUser (userId, userContactId, activeUser, displayName, fullName) = + let profile = Profile {displayName, fullName} + in User {userId, userContactId, localDisplayName = displayName, profile, activeUser} setActiveUser :: MonadUnliftIO m => SQLiteStore -> UserId -> m () setActiveUser st userId = do @@ -134,51 +133,17 @@ createDirectConnection st userId agentConnId = createDirectContact :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> Connection -> Profile -> m () -createDirectContact st userId Connection {connId} Profile {contactRef, displayName} = - liftIOEither . withTransaction st $ \db -> do - DB.execute db "INSERT INTO contact_profiles (contact_ref, display_name) VALUES (?, ?)" (contactRef, displayName) - profileId <- insertedRowId db - lcrSuffix <- getLcrSuffix db - create db profileId lcrSuffix 20 - where - getLcrSuffix :: DB.Connection -> IO Int - getLcrSuffix db = - maybe 0 ((+ 1) . fromOnly) . listToMaybe - <$> DB.queryNamed - db - [sql| - SELECT lcr_suffix FROM contacts - WHERE user_id = :user_id AND lcr_base = :contact_ref - ORDER BY lcr_suffix DESC - LIMIT 1 - |] - [":user_id" := userId, ":contact_ref" := contactRef] - create :: DB.Connection -> Int64 -> Int -> Int -> IO (Either StoreError ()) - create _ _ _ 0 = pure $ Left SEDuplicateContactRef - create db profileId lcrSuffix attempts = do - let lcr = localContactRef' lcrSuffix - E.try (insertUser lcr) >>= \case - Right () -> do - contactId <- insertedRowId db - DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) - pure $ Right () - Left e - | DB.sqlError e == DB.ErrorConstraint -> create db profileId (lcrSuffix + 1) (attempts - 1) - | otherwise -> E.throwIO e - where - localContactRef' 0 = contactRef - localContactRef' n = contactRef <> T.pack ('_' : show n) - insertUser lcr = - DB.execute - db - [sql| - INSERT INTO contacts - (contact_profile_id, local_contact_ref, lcr_base, lcr_suffix, user_id) VALUES (?, ?, ?, ?, ?) - |] - (profileId, lcr, contactRef, lcrSuffix, userId) +createDirectContact st userId Connection {connId} Profile {displayName, fullName} = + liftIOEither . withTransaction st $ \db -> + withLocalDisplayName db userId displayName $ \localDisplayName' -> do + DB.execute db "INSERT INTO contact_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) + profileId <- insertedRowId db + DB.execute db "INSERT INTO contacts (contact_profile_id, local_display_name, user_id) VALUES (?, ?, ?)" (profileId, localDisplayName', userId) + contactId <- insertedRowId db + DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) -deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactRef -> m () -deleteContact st userId contactRef = +deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactName -> m () +deleteContact st userId displayName = liftIO . withTransaction st $ \db -> do DB.executeNamed db @@ -187,23 +152,30 @@ deleteContact st userId contactRef = SELECT connection_id FROM connections c JOIN contacts cs ON c.contact_id = cs.contact_id - WHERE cs.user_id = :user_id AND cs.local_contact_ref = :contact_ref + WHERE cs.user_id = :user_id AND cs.local_display_name = :display_name ) |] - [":user_id" := userId, ":contact_ref" := contactRef] + [":user_id" := userId, ":display_name" := displayName] DB.executeNamed db [sql| DELETE FROM contacts - WHERE user_id = :user_id AND local_contact_ref = :contact_ref + WHERE user_id = :user_id AND local_display_name = :display_name |] - [":user_id" := userId, ":contact_ref" := contactRef] + [":user_id" := userId, ":display_name" := displayName] + DB.executeNamed + db + [sql| + DELETE FROM display_names + WHERE user_id = :user_id AND local_display_name = :display_name + |] + [":user_id" := userId, ":display_name" := displayName] -- TODO return the last connection that is ready, not any last connection -- requires updating connection status getContact :: - (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m Contact -getContact st userId localContactRef = + (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactName -> m Contact +getContact st userId localDisplayName = liftIOEither . withTransaction st $ \db -> runExceptT $ do c@Contact {contactId} <- getContact_ db activeConn <- getConnection_ db contactId @@ -214,12 +186,12 @@ getContact st userId localContactRef = <$> DB.queryNamed db [sql| - SELECT c.contact_id, p.contact_ref, p.display_name + SELECT c.contact_id, p.display_name, p.full_name FROM contacts c JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id - WHERE c.user_id = :user_id AND c.local_contact_ref = :local_contact_ref AND c.is_user = :is_user + WHERE c.user_id = :user_id AND c.local_display_name = :local_display_name AND c.is_user = :is_user |] - [":user_id" := userId, ":local_contact_ref" := localContactRef, ":is_user" := False] + [":user_id" := userId, ":local_display_name" := localDisplayName, ":is_user" := False] getConnection_ db contactId = ExceptT $ do connection <$> DB.queryNamed @@ -233,15 +205,15 @@ getContact st userId localContactRef = LIMIT 1 |] [":user_id" := userId, ":contact_id" := contactId] - toContact [(contactId, contactRef, displayName)] = - let profile = Profile {contactRef, displayName} - in Right Contact {contactId, localContactRef, profile, activeConn = undefined} - toContact _ = Left $ SEContactNotFound localContactRef + toContact [(contactId, displayName, fullName)] = + let profile = Profile {displayName, fullName} + in Right Contact {contactId, localDisplayName, profile, activeConn = undefined} + toContact _ = Left $ SEContactNotFound localDisplayName connection (connRow : _) = Right $ toConnection connRow - connection _ = Left $ SEContactNotReady localContactRef + connection _ = Left $ SEContactNotReady localDisplayName -getContactConnections :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactRef -> m [Connection] -getContactConnections st userId contactRef = +getContactConnections :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactName -> m [Connection] +getContactConnections st userId displayName = liftIOEither . withTransaction st $ \db -> connections <$> DB.queryNamed @@ -253,11 +225,11 @@ getContactConnections st userId contactRef = JOIN contacts cs ON c.contact_id == cs.contact_id WHERE c.user_id = :user_id AND cs.user_id = :user_id - AND cs.local_contact_ref == :contact_ref + AND cs.local_display_name == :display_name |] - [":user_id" := userId, ":contact_ref" := contactRef] + [":user_id" := userId, ":display_name" := displayName] where - connections [] = Left $ SEContactNotFound contactRef + connections [] = Left $ SEContactNotFound displayName connections rows = Right $ map toConnection rows type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, UTCTime) @@ -302,27 +274,26 @@ getConnectionChatDirection st userId agentConnId = <$> DB.query db [sql| - SELECT c.local_contact_ref, p.contact_ref, p.display_name + SELECT c.local_display_name, p.display_name, p.full_name FROM contacts c JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id WHERE c.user_id = ? AND c.contact_id = ? |] (userId, contactId) - toContact contactId c [(localContactRef, contactRef, displayName)] = - let profile = Profile {contactRef, displayName} - in Right $ CContact Contact {contactId, localContactRef, profile, activeConn = c} + toContact contactId c [(localDisplayName, displayName, fullName)] = + let profile = Profile {displayName, fullName} + in Right $ CContact Contact {contactId, localDisplayName, profile, activeConn = c} toContact _ _ _ = Left $ SEInternal "referenced contact not found" -- | creates completely new group with a single member - the current user createNewGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> GroupProfile -> m Group -createNewGroup st gVar User {userId, userContactId, profile} p@GroupProfile {groupRef, displayName} = - liftIOEither . checkConstraint SEDuplicateGroupRef . withTransaction st $ \db -> do - -- group inserted before profile to ensure its local_group_ref is unique - DB.execute db "INSERT INTO groups (local_group_ref, lgr_base, user_id) VALUES (?, ?, ?)" (groupRef, groupRef, userId) - groupId <- insertedRowId db - DB.execute db "INSERT INTO group_profiles (group_ref, display_name) VALUES (?, ?)" (groupRef, displayName) +createNewGroup st gVar User {userId, userContactId, profile} p@GroupProfile {displayName, fullName} = + liftIOEither . checkConstraint SEDuplicateName . withTransaction st $ \db -> do + DB.execute db "INSERT INTO display_names (local_display_name, ldn_base, user_id) VALUES (?, ?, ?)" (displayName, displayName, userId) + DB.execute db "INSERT INTO group_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) profileId <- insertedRowId db - DB.execute db "UPDATE groups SET group_profile_id = ? WHERE group_id = ?" (profileId, groupId) + DB.execute db "INSERT INTO groups (local_display_name, user_id, group_profile_id) VALUES (?, ?, ?)" (displayName, userId, profileId) + groupId <- insertedRowId db memberId <- randomId gVar 12 createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId groupMemberId <- insertedRowId db @@ -336,71 +307,44 @@ createNewGroup st gVar User {userId, userContactId, profile} p@GroupProfile {gro memberProfile = profile, memberContactId = Just userContactId } - pure $ Right Group {groupId, localGroupRef = groupRef, groupProfile = p, members = [], membership} + pure $ Right Group {groupId, localDisplayName = displayName, groupProfile = p, members = [], membership} -- | creates a new group record for the group the current user was invited to createGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> Contact -> GroupProfile -> m Group -createGroup st gVar User {userId, userContactId, profile} contact p@GroupProfile {groupRef, displayName} = - liftIOEither . withTransaction st $ \db -> do - DB.execute db "INSERT INTO group_profiles (group_ref, display_name) VALUES (?, ?)" (groupRef, displayName) - profileId <- insertedRowId db - lgrSuffix <- getLgrSuffix db - group <- create db profileId lgrSuffix 20 - pure group - where - -- createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId - -- groupMemberId <- insertedRowId db - -- let membership = - -- GroupMember - -- { groupMemberId, - -- memberId, - -- memberRole = GROwner, - -- memberStatus = GSMemReady, - -- invitedBy = IBUser, - -- memberProfile = profile, - -- memberContactId = Just userContactId - -- } - -- pure $ Right Group {groupId, localGroupRef = groupRef, groupProfile = p, members = [], membership} +createGroup st gVar User {userId, userContactId, profile} contact p@GroupProfile {displayName, fullName} = + liftIOEither . withTransaction st $ \db -> + withLocalDisplayName db userId displayName $ \localDisplayName -> do + DB.execute db "INSERT INTO group_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) + profileId <- insertedRowId db + DB.execute + db + [sql| + INSERT INTO groups + (group_profile_id, local_display_name, user_id) VALUES (?, ?, ?) + |] + (profileId, localDisplayName, userId) + groupId <- insertedRowId db + pure Group {groupId, localDisplayName, groupProfile = p, members = undefined, membership = undefined} - getLgrSuffix :: DB.Connection -> IO Int - getLgrSuffix db = - maybe 0 ((+ 1) . fromOnly) . listToMaybe - <$> DB.queryNamed - db - [sql| - SELECT lgr_suffix FROM groups - WHERE user_id = :user_id AND lgr_base = :group_ref - ORDER BY lgr_suffix DESC - LIMIT 1 - |] - [":user_id" := userId, ":group_ref" := groupRef] - create :: DB.Connection -> Int64 -> Int -> Int -> IO (Either StoreError Group) - create _ _ _ 0 = pure $ Left SEDuplicateGroupRef - create db profileId lgrSuffix attempts = do - let lgr = localGroupRef' lgrSuffix - E.try (insertGroup lgr) >>= \case - Right () -> do - groupId <- insertedRowId db - pure $ Right Group {groupId, localGroupRef = lgr, groupProfile = p, members = undefined, membership = undefined} - Left e - | DB.sqlError e == DB.ErrorConstraint -> create db profileId (lgrSuffix + 1) (attempts - 1) - | otherwise -> E.throwIO e - where - localGroupRef' 0 = groupRef - localGroupRef' n = groupRef <> T.pack ('_' : show n) - insertGroup lgr = - DB.execute - db - [sql| - INSERT INTO groups - (group_profile_id, local_group_ref, lgr_base, lgr_suffix, user_id) VALUES (?, ?, ?, ?, ?) - |] - (profileId, lgr, groupRef, lgrSuffix, userId) +-- where +-- createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId +-- groupMemberId <- insertedRowId db +-- let membership = +-- GroupMember +-- { groupMemberId, +-- memberId, +-- memberRole = GROwner, +-- memberStatus = GSMemReady, +-- invitedBy = IBUser, +-- memberProfile = profile, +-- memberContactId = Just userContactId +-- } +-- pure $ Right Group {groupId, localDisplayName = displayName, groupProfile = p, members = [], membership} -- TODO return the last connection that is ready, not any last connection -- requires updating connection status -getGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> User -> GroupRef -> m Group -getGroup st User {userId, userContactId} localGroupRef = +getGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> User -> GroupName -> m Group +getGroup st User {userId, userContactId} localDisplayName = liftIOEither . withTransaction st $ \db -> runExceptT $ do g@Group {groupId} <- getGroup_ db members <- getMembers_ db groupId @@ -413,17 +357,17 @@ getGroup st User {userId, userContactId} localGroupRef = <$> DB.query db [sql| - SELECT g.group_id, p.group_ref, p.display_name + SELECT g.group_id, p.display_name, p.full_name FROM groups g JOIN group_profiles p ON p.group_profile_id = g.group_profile_id - WHERE g.local_group_ref = ? AND g.user_id = ? + WHERE g.local_display_name = ? AND g.user_id = ? |] - (localGroupRef, userId) - toGroup :: [(Int64, GroupRef, Text)] -> Either StoreError Group - toGroup [(groupId, groupRef, displayName)] = - let groupProfile = GroupProfile {groupRef, displayName} - in Right Group {groupId, localGroupRef, groupProfile, members = undefined, membership = undefined} - toGroup _ = Left $ SEGroupNotFound localGroupRef + (localDisplayName, userId) + toGroup :: [(Int64, GroupName, Text)] -> Either StoreError Group + toGroup [(groupId, displayName, fullName)] = + let groupProfile = GroupProfile {displayName, fullName} + in Right Group {groupId, localDisplayName, groupProfile, members = undefined, membership = undefined} + toGroup _ = Left $ SEGroupNotFound localDisplayName getMembers_ :: DB.Connection -> Int64 -> ExceptT StoreError IO [(GroupMember, Connection)] getMembers_ db groupId = ExceptT $ do Right . map toContactMember @@ -432,7 +376,7 @@ getGroup st User {userId, userContactId} localGroupRef = [sql| SELECT m.group_member_id, m.member_id, m.member_role, m.member_status, - m.invited_by, m.contact_id, p.contact_ref, p.display_name, + m.invited_by, m.contact_id, p.display_name, p.full_name, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at FROM group_members m @@ -452,7 +396,7 @@ getGroup st User {userId, userContactId} localGroupRef = [sql| SELECT m.group_member_id, m.member_id, m.member_role, m.member_status, - m.invited_by, m.contact_id, p.contact_ref, p.display_name + m.invited_by, m.contact_id, p.display_name, p.full_name FROM group_members m JOIN groups g ON g.group_id = m.group_id JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id @@ -462,15 +406,15 @@ getGroup st User {userId, userContactId} localGroupRef = toContactMember :: (GroupMemberRow :. ConnectionRow) -> (GroupMember, Connection) toContactMember (memberRow :. connRow) = (toGroupMember memberRow, toConnection connRow) toGroupMember :: GroupMemberRow -> GroupMember - toGroupMember (groupMemberId, memberId, memberRole, memberStatus, invitedById, memberContactId, contactRef, displayName) = - let memberProfile = Profile {contactRef, displayName} + toGroupMember (groupMemberId, memberId, memberRole, memberStatus, invitedById, memberContactId, displayName, fullName) = + let memberProfile = Profile {displayName, fullName} invitedBy = toInvitedBy userContactId invitedById in GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} userMember :: [GroupMemberRow] -> Either StoreError GroupMember userMember [memberRow] = Right $ toGroupMember memberRow userMember _ = Left SEGroupWithoutUser -type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberStatus, Maybe Int64, Maybe Int64, ContactRef, Text) +type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberStatus, Maybe Int64, Maybe Int64, ContactName, Text) createGroupMember :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Int64 -> GroupMemberRole -> InvitedBy -> ConnId -> m MemberId createGroupMember st gVar User {userId, userContactId} groupId contactId memberRole invitedBy agentConnId = @@ -512,6 +456,42 @@ createMember_ db groupId contactId memberRole memberStatus invitedBy memberId = ":contact_id" := contactId ] +-- | Saves unique local display name based on passed displayName, suffixed with _N if required. +-- This function should be called inside transaction. +withLocalDisplayName :: forall a. DB.Connection -> UserId -> Text -> (Text -> IO a) -> IO (Either StoreError a) +withLocalDisplayName db userId displayName action = getLdnSuffix >>= (`tryCreateName` 20) + where + getLdnSuffix :: IO Int + getLdnSuffix = + maybe 0 ((+ 1) . fromOnly) . listToMaybe + <$> DB.queryNamed + db + [sql| + SELECT ldn_suffix FROM display_names + WHERE user_id = :user_id AND ldn_base = :display_name + ORDER BY ldn_suffix DESC + LIMIT 1 + |] + [":user_id" := userId, ":display_name" := displayName] + tryCreateName :: Int -> Int -> IO (Either StoreError a) + tryCreateName _ 0 = pure $ Left SEDuplicateName + tryCreateName ldnSuffix attempts = do + let ldn = displayName <> (if ldnSuffix == 0 then "" else T.pack $ '_' : show ldnSuffix) + E.try (insertName ldn) >>= \case + Right () -> Right <$> action ldn + Left e + | DB.sqlError e == DB.ErrorConstraint -> tryCreateName (ldnSuffix + 1) (attempts - 1) + | otherwise -> E.throwIO e + where + insertName ldn = + DB.execute + db + [sql| + INSERT INTO display_names + (local_display_name, ldn_base, ldn_suffix, user_id) VALUES (?, ?, ?, ?) + |] + (ldn, displayName, ldnSuffix, userId) + createWithRandomId :: TVar ChaChaDRG -> (ByteString -> IO ()) -> IO (Either StoreError ByteString) createWithRandomId gVar create = tryCreate 3 where @@ -529,11 +509,10 @@ randomId :: TVar ChaChaDRG -> Int -> IO ByteString randomId gVar n = B64.encode <$> (atomically . stateTVar gVar $ randomBytesGenerate n) data StoreError - = SEDuplicateContactRef - | SEContactNotFound ContactRef - | SEContactNotReady ContactRef - | SEDuplicateGroupRef - | SEGroupNotFound GroupRef + = SEDuplicateName + | SEContactNotFound ContactName + | SEContactNotReady ContactName + | SEGroupNotFound GroupName | SEGroupWithoutUser | SEDuplicateGroupMember | SEConnectionNotFound ConnId diff --git a/src/Simplex/Chat/Terminal.hs b/src/Simplex/Chat/Terminal.hs index d3d5cec6ef..ea29e2b24c 100644 --- a/src/Simplex/Chat/Terminal.hs +++ b/src/Simplex/Chat/Terminal.hs @@ -15,7 +15,7 @@ import System.Terminal import System.Terminal.Internal (LocalTerminal, Terminal, VirtualTerminal) import UnliftIO.STM -data ActiveTo = ActiveNone | ActiveC ContactRef +data ActiveTo = ActiveNone | ActiveC ContactName deriving (Eq) data ChatTerminal = ChatTerminal diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 81a7a5b677..7d7e616025 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -26,7 +26,7 @@ import Simplex.Messaging.Agent.Store.SQLite (fromTextField_) data User = User { userId :: UserId, userContactId :: Int64, - localContactRef :: ContactRef, + localDisplayName :: ContactName, profile :: Profile, activeUser :: Bool } @@ -35,7 +35,7 @@ type UserId = Int64 data Contact = Contact { contactId :: Int64, - localContactRef :: ContactRef, + localDisplayName :: ContactName, profile :: Profile, activeConn :: Connection } @@ -44,13 +44,13 @@ data Contact = Contact contactConnId :: Contact -> ConnId contactConnId Contact {activeConn = Connection {agentConnId}} = agentConnId -type ContactRef = Text +type ContactName = Text -type GroupRef = Text +type GroupName = Text data Group = Group { groupId :: Int64, - localGroupRef :: Text, + localDisplayName :: GroupName, groupProfile :: GroupProfile, members :: [(GroupMember, Connection)], membership :: GroupMember @@ -58,8 +58,8 @@ data Group = Group deriving (Eq, Show) data Profile = Profile - { contactRef :: ContactRef, - displayName :: Text + { displayName :: ContactName, + fullName :: Text } deriving (Generic, Eq, Show) @@ -68,8 +68,8 @@ instance ToJSON Profile where toEncoding = J.genericToEncoding J.defaultOptions instance FromJSON Profile data GroupProfile = GroupProfile - { groupRef :: GroupRef, - displayName :: Text + { displayName :: GroupName, + fullName :: Text } deriving (Generic, Eq, Show) diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index bb7cc01ec7..226f1888fd 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -45,19 +45,19 @@ showInvitation = printToView . invitation showChatError :: ChatReader m => ChatError -> m () showChatError = printToView . chatError -showContactDeleted :: ChatReader m => ContactRef -> m () +showContactDeleted :: ChatReader m => ContactName -> m () showContactDeleted = printToView . contactDeleted -showContactConnected :: ChatReader m => ContactRef -> m () +showContactConnected :: ChatReader m => ContactName -> m () showContactConnected = printToView . contactConnected -showContactDisconnected :: ChatReader m => ContactRef -> m () +showContactDisconnected :: ChatReader m => ContactName -> m () showContactDisconnected = printToView . contactDisconnected -showReceivedMessage :: ChatReader m => ContactRef -> UTCTime -> Text -> MsgIntegrity -> m () +showReceivedMessage :: ChatReader m => ContactName -> UTCTime -> Text -> MsgIntegrity -> m () showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage c utcTime msg mOk) -showSentMessage :: ChatReader m => ContactRef -> ByteString -> m () +showSentMessage :: ChatReader m => ContactName -> ByteString -> m () showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) showGroupCreated :: ChatReader m => GroupProfile -> m () @@ -72,19 +72,19 @@ invitation qInfo = "and ask them to connect: /c " ] -contactDeleted :: ContactRef -> [StyledString] +contactDeleted :: ContactName -> [StyledString] contactDeleted c = [ttyContact c <> " is deleted"] -contactConnected :: ContactRef -> [StyledString] +contactConnected :: ContactName -> [StyledString] contactConnected c = [ttyContact c <> " is connected"] -contactDisconnected :: ContactRef -> [StyledString] +contactDisconnected :: ContactName -> [StyledString] contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] groupCreated :: GroupProfile -> [StyledString] -groupCreated GroupProfile {groupRef, displayName} = ["group " <> ttyGroup groupRef <> " (" <> plain displayName <> ") is created"] +groupCreated GroupProfile {displayName, fullName} = ["group " <> ttyGroup displayName <> " (" <> plain fullName <> ") is created"] -receivedMessage :: ContactRef -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] +receivedMessage :: ContactName -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage c utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime pure $ prependFirst (t <> " " <> ttyFromContact c) (msgPlain msg) ++ showIntegrity mOk @@ -110,7 +110,7 @@ receivedMessage c utcTime msg mOk = do msgError :: String -> [StyledString] msgError s = [styled (Colored Red) s] -sentMessage :: ContactRef -> ByteString -> IO [StyledString] +sentMessage :: ContactName -> ByteString -> IO [StyledString] sentMessage c msg = do time <- formatTime defaultTimeLocale "%H:%M" <$> getZonedTime pure $ prependFirst (styleTime time <> " " <> ttyToContact c) (msgPlain $ safeDecodeUtf8 msg) @@ -125,9 +125,9 @@ msgPlain = map styleMarkdownText . T.lines chatError :: ChatError -> [StyledString] chatError = \case ChatErrorStore err -> case err of + SEDuplicateName -> ["this display name is already used by user, contact or group"] SEContactNotFound c -> ["no contact " <> ttyContact c] SEContactNotReady c -> ["contact " <> ttyContact c <> " is not active yet"] - SEDuplicateGroupRef -> ["group with this alias already exists"] e -> ["chat db error: " <> plain (show e)] ChatErrorAgent err -> case err of -- CONN e -> case e of @@ -141,16 +141,16 @@ chatError = \case printToView :: (MonadUnliftIO m, MonadReader ChatController m) => [StyledString] -> m () printToView s = asks chatTerminal >>= liftIO . (`printToTerminal` s) -ttyContact :: ContactRef -> StyledString +ttyContact :: ContactName -> StyledString ttyContact = styled (Colored Green) -ttyToContact :: ContactRef -> StyledString +ttyToContact :: ContactName -> StyledString ttyToContact c = styled (Colored Cyan) $ "@" <> c <> " " -ttyFromContact :: ContactRef -> StyledString +ttyFromContact :: ContactName -> StyledString ttyFromContact c = styled (Colored Yellow) $ c <> "> " -ttyGroup :: GroupRef -> StyledString +ttyGroup :: GroupName -> StyledString ttyGroup g = styled (Colored Blue) $ "#" <> g -- ttyFromGroup :: Group -> Contact -> StyledString diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 95947f1ceb..a13c591d01 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -6,17 +6,17 @@ import ChatClient import Control.Concurrent.Async (concurrently_) import Control.Concurrent.STM import Data.Char (isDigit) -import Data.List (dropWhileEnd, find, isPrefixOf) +import Data.List (dropWhileEnd, isPrefixOf) import Simplex.Chat.Controller import Simplex.Chat.Types (Profile (..)) import System.Terminal.Internal (VirtualTerminal (..)) import Test.Hspec aliceProfile :: Profile -aliceProfile = Profile {contactRef = "alice", displayName = "Alice"} +aliceProfile = Profile {displayName = "alice", fullName = "Alice"} bobProfile :: Profile -bobProfile = Profile {contactRef = "bob", displayName = "Bob"} +bobProfile = Profile {displayName = "bob", fullName = "Bob"} testAddContact :: Spec testAddContact = describe "add chat contact" $ @@ -32,6 +32,22 @@ testAddContact = describe "add chat contact" $ bob <# "alice> hello" bob #> "@alice hi" alice <# "bob> hi" + -- test adding the same contact one more time - local name will be different + alice ##> "/a" + Just inv' <- invitation <$> getWindow alice + bob ##> ("/c " <> inv') + concurrently_ + (bob <## "alice_1 is connected") + (alice <## "bob_1 is connected") + alice #> "@bob_1 hello" + bob <# "alice_1> hello" + bob #> "@alice_1 hi" + alice <# "bob_1> hi" + -- test deleting contact + alice ##> "/d bob_1" + alice <## "bob_1 is deleted" + chatCommand alice "@bob_1 hey" + alice <## "no contact bob_1" (##>) :: TestCC -> String -> IO () (##>) cc cmd = do @@ -67,7 +83,11 @@ getWindow (TestCC _ t _) = do if win' /= win then pure win' else retry invitation :: [String] -> Maybe String -invitation win = dropWhileEnd (== ' ') <$> find ("smp::" `isPrefixOf`) win +invitation win = lastMaybe $ map (dropWhileEnd (== ' ')) $ filter ("smp::" `isPrefixOf`) win lastOutput :: [String] -> String lastOutput win = dropWhileEnd (== ' ') $ win !! (length win - 2) -- (- 2) to exclude prompt + +lastMaybe :: [a] -> Maybe a +lastMaybe [] = Nothing +lastMaybe xs = Just $ last xs From f1a44383fa705315c1432f5565d8ea600ab5c110 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 16 Jul 2021 07:40:55 +0100 Subject: [PATCH 11/29] chat groups: establish connection between host and invitee members (#77) * create group after invitation * add group invitation to db, show sent and received group invitations * test creating group and sending invitation * establish group connections (WIP) * connect user to the inviter, notification, member classification --- migrations/20210612_initial.sql | 5 +- package.yaml | 1 + src/Simplex/Chat.hs | 137 +++++++++---- src/Simplex/Chat/Controller.hs | 9 +- src/Simplex/Chat/Help.hs | 19 +- src/Simplex/Chat/Protocol.hs | 19 +- src/Simplex/Chat/Store.hs | 334 ++++++++++++++++++-------------- src/Simplex/Chat/Styled.hs | 3 +- src/Simplex/Chat/Types.hs | 45 ++++- src/Simplex/Chat/View.hs | 80 +++++++- src/Simplex/Chat/protocol.md | 60 ++++++ tests/ChatClient.hs | 3 + tests/ChatTests.hs | 58 ++++-- tests/Test.hs | 5 +- 14 files changed, 531 insertions(+), 247 deletions(-) diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index 1da08a17ed..26fdbb9348 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -61,6 +61,7 @@ CREATE TABLE groups ( user_id INTEGER NOT NULL REFERENCES users, local_display_name TEXT NOT NULL, -- local group name without spaces group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile + inv_queue_info BLOB, FOREIGN KEY (user_id, local_display_name) REFERENCES display_names (user_id, local_display_name) ON DELETE RESTRICT, @@ -72,8 +73,8 @@ CREATE TABLE group_members ( -- group members, excluding the local user group_member_id INTEGER PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, member_id BLOB NOT NULL, -- shared member ID, unique per group - member_role TEXT NOT NULL DEFAULT '', -- owner, admin, member - member_status TEXT NOT NULL DEFAULT '', -- new, invited, accepted, connected, ready + member_role TEXT NOT NULL, -- owner, admin, member + member_status TEXT NOT NULL, -- new, invited, accepted, connected, ready invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles ON DELETE RESTRICT, contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, diff --git a/package.yaml b/package.yaml index ff3c72591b..90dbff3df3 100644 --- a/package.yaml +++ b/package.yaml @@ -18,6 +18,7 @@ dependencies: - base >= 4.7 && < 5 - base64-bytestring >= 1.0 && < 1.3 - bytestring == 0.10.* + - composition == 1.0.* - containers == 0.6.* - cryptonite >= 0.27 && < 0.30 - directory == 1.3.* diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index c77c7cbff4..3cae435112 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -23,7 +23,7 @@ import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.Functor (($>)) import Data.List (find) -import Data.Maybe (isJust) +import Data.Maybe (isJust, isNothing) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) @@ -59,6 +59,7 @@ data ChatCommand | SendMessage ContactName ByteString | NewGroup GroupProfile | AddMember GroupName ContactName GroupMemberRole + | JoinGroup GroupName | RemoveMember GroupName ContactName | MemberRole GroupName ContactName GroupMemberRole | LeaveGroup GroupName @@ -138,40 +139,43 @@ processChatCommand user@User {userId, profile} = \case withStore $ \st -> createDirectConnection st userId connId showInvitation qInfo Connect qInfo -> do - connId <- withAgent $ \a -> joinConnection a qInfo $ encodeProfile profile + connId <- withAgent $ \a -> joinConnection a qInfo . directMessage $ XInfo profile withStore $ \st -> createDirectConnection st userId connId - DeleteContact cRef -> do - conns <- withStore $ \st -> getContactConnections st userId cRef + DeleteContact cName -> do + conns <- withStore $ \st -> getContactConnections st userId cName withAgent $ \a -> forM_ conns $ \Connection {agentConnId} -> deleteConnection a agentConnId `catchError` \(_ :: AgentErrorType) -> pure () - withStore $ \st -> deleteContact st userId cRef - unsetActive $ ActiveC cRef - showContactDeleted cRef - SendMessage cRef msg -> do - contact <- withStore $ \st -> getContact st userId cRef - let body = MsgBodyContent {contentType = SimplexContentType XCText, contentData = msg} - rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent = XMsgNew MTText [] [body], chatDAG = Nothing} - connId = contactConnId contact - void . withAgent $ \a -> sendMessage a connId $ serializeRawChatMessage rawMsg - setActive $ ActiveC cRef + withStore $ \st -> deleteContact st userId cName + unsetActive $ ActiveC cName + showContactDeleted cName + SendMessage cName msg -> do + contact <- withStore $ \st -> getContact st userId cName + let msgEvent = XMsgNew MTText [] [MsgBodyContent {contentType = SimplexContentType XCText, contentData = msg}] + sendDirectMessage (contactConnId contact) msgEvent + setActive $ ActiveC cName NewGroup gProfile -> do gVar <- asks idsDrg - void $ withStore $ \st -> createNewGroup st gVar user gProfile - showGroupCreated gProfile - AddMember gRef cRef memRole -> do - (group, contact) <- withStore $ \st -> (,) <$> getGroup st user gRef <*> getContact st userId cRef + group <- withStore $ \st -> createNewGroup st gVar user gProfile + showGroupCreated group + AddMember gName cName memRole -> do + (group, contact) <- withStore $ \st -> (,) <$> getGroup st user gName <*> getContact st userId cName let Group {groupId, groupProfile, membership, members} = group userRole = memberRole membership userMemberId = memberId membership when (userRole < GRAdmin || userRole < memRole) $ throwError $ ChatError CEGroupRole - when (isMember contact members) $ throwError $ ChatError CEGroupDuplicateMember + when (isMember contact members) . throwError . ChatError $ CEGroupDuplicateMember cName + when (memberStatus membership == GSMemInvited) . throwError . ChatError $ CEGroupNotJoined gName + when (memberStatus membership < GSMemReady) . throwError . ChatError $ CEGroupMemberNotReady gVar <- asks idsDrg (agentConnId, qInfo) <- withAgent createConnection - memberId <- withStore $ \st -> createGroupMember st gVar user groupId (contactId contact) memRole IBUser agentConnId - let chatMsgEvent = XGrpInv (userMemberId, userRole) (memberId, memRole) qInfo groupProfile - rawMsg = rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent, chatDAG = Nothing} - connId = contactConnId contact - void . withAgent $ \a -> sendMessage a connId $ serializeRawChatMessage rawMsg + GroupMember {memberId} <- withStore $ \st -> createGroupMember st gVar user groupId contact memRole agentConnId + let msg = XGrpInv $ GroupInvitation (userMemberId, userRole) (memberId, memRole) qInfo groupProfile + sendDirectMessage (contactConnId contact) msg + showSentGroupInvitation group cName + JoinGroup gName -> do + ReceivedGroupInvitation {fromMember, invitedMember, queueInfo} <- withStore $ \st -> getGroupInvitation st user gName + agentConnId <- withAgent $ \a -> joinConnection a queueInfo . directMessage . XGrpAcpt $ memberId invitedMember + withStore $ \st -> createMemberConnection st userId (groupMemberId fromMember) agentConnId MemberRole _gRef _cRef _mRole -> pure () RemoveMember _gRef _cRef -> pure () LeaveGroup _gRef -> pure () @@ -179,7 +183,7 @@ processChatCommand user@User {userId, profile} = \case ListMembers _gRef -> pure () SendGroupMessage _gRef _msg -> pure () where - isMember :: Contact -> [(GroupMember, Connection)] -> Bool + isMember :: Contact -> [(GroupMember, Maybe Connection)] -> Bool isMember Contact {contactId} members = isJust $ find ((== Just contactId) . memberContactId . fst) members agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () @@ -192,8 +196,8 @@ agentSubscriber = do void . runExceptT $ processAgentMessage user connId msg `catchError` (liftIO . print) processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () -processAgentMessage User {userId, profile} agentConnId agentMessage = do - chatDirection <- withStore $ \st -> getConnectionChatDirection st userId agentConnId +processAgentMessage user@User {userId, profile} agentConnId agentMessage = do + chatDirection <- withStore $ \st -> getConnectionChatDirection st user agentConnId case chatDirection of ReceivedDirectMessage (CContact ct@Contact {localDisplayName = c}) -> case agentMessage of @@ -202,11 +206,11 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do case chatMsgEvent of XMsgNew MTText [] body -> newTextMessage c meta $ find (isSimplexContentType XCText) body XInfo _ -> pure () -- TODO profile update - XGrpInv fromMem invMem qInfo groupProfile -> groupInvitation ct fromMem invMem qInfo groupProfile + XGrpInv gInv -> saveGroupInvitation ct gInv _ -> pure () CON -> do -- TODO update connection status - showContactConnected c + showContactConnected ct showToast ("@" <> c) "connected" setActive $ ActiveC c END -> do @@ -219,11 +223,52 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do CONF confId connInfo -> do -- TODO update connection status saveConnInfo conn connInfo - withAgent $ \a -> allowConnection a agentConnId confId $ encodeProfile profile + withAgent $ \a -> allowConnection a agentConnId confId . directMessage $ XInfo profile INFO connInfo -> saveConnInfo conn connInfo _ -> pure () - _ -> pure () + ReceivedGroupMessage gName m -> + case agentMessage of + CONF confId connInfo -> do + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case chatMsgEvent of + XGrpAcpt memId + | memId == memberId m -> do + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemAccepted + withAgent $ \a -> allowConnection a agentConnId confId "" + | otherwise -> pure () -- TODO error not matching member ID + _ -> pure () -- TODO show/log error, other events in SMP confirmation + CON -> do + Group {membership, members} <- withStore $ \st -> getGroup st user gName + -- TODO because the contact is not created instantly, if the member is not created from contact, + -- it should still have a unique local display name. + -- If it is created from contact it can still be duplicated on the member (and match the contact) + case invitedBy m of + IBUser -> do + -- sender was invited by the current user + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected + sendGroupMessage members $ XGrpMemNew (memberId m) (memberRole m) (memberProfile m) + showConnectedGroupMember gName $ displayName (memberProfile m :: Profile) + forM_ (filter (\m' -> memberStatus m' >= GSMemConnected) . map fst $ members) $ \m' -> + sendDirectMessage agentConnId $ XGrpMemIntro (memberId m') (memberRole m') (memberProfile m') + _ -> do + if Just (invitedBy membership) == (IBContact <$> memberContactId m) + then do + -- sender invited the current user + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected + showUserConnectedToGroup gName + pure () + else do + showConnectedGroupMember gName $ displayName (memberProfile m :: Profile) + MSG meta msgBody -> do + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody + case chatMsgEvent of + XGrpMemNew memId memRole memProfile -> do + Group {membership, members} <- withStore $ \st -> getGroup st user gName + when (memberId membership /= memId && isNothing (find ((== memId) . memberId . fst) members)) $ + withStore $ \st -> pure () -- add new member as GSMemAccepted + _ -> pure () + _ -> pure () where newTextMessage :: ContactName -> MsgMeta -> Maybe MsgBodyContent -> m () newTextMessage c meta = \case @@ -234,10 +279,12 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do setActive $ ActiveC c _ -> pure () - groupInvitation :: Contact -> (MemberId, GroupMemberRole) -> (MemberId, GroupMemberRole) -> SMPQueueInfo -> GroupProfile -> m () - groupInvitation _ct (fromMemId, fromRole) (memId, memRole) _qInfo _groupProfile = do + saveGroupInvitation :: Contact -> GroupInvitation -> m () + saveGroupInvitation ct@Contact {localDisplayName} inv@(GroupInvitation (fromMemId, fromRole) (memId, memRole) _ _) = do when (fromRole < GRAdmin || fromRole < memRole) $ throwError $ ChatError CEGroupRole - when (fromMemId == memId) $ throwError $ ChatError CEGroupDuplicateMember + when (fromMemId == memId) $ throwError $ ChatError CEGroupDuplicateMemberId + group <- withStore $ \st -> createGroupInvitation st user ct inv + showReceivedGroupInvitation group localDisplayName parseChatMessage :: ByteString -> Either ChatError ChatMessage parseChatMessage msgBody = first ChatErrorMessage (parseAll rawChatMessageP msgBody >>= toChatMessage) @@ -250,10 +297,17 @@ processAgentMessage User {userId, profile} agentConnId agentMessage = do withStore $ \st -> createDirectContact st userId activeConn p _ -> pure () -- TODO show/log error, other events in SMP confirmation -encodeProfile :: Profile -> ByteString -encodeProfile profile = - let chatMsg = ChatMessage {chatMsgId = Nothing, chatMsgEvent = XInfo profile, chatDAG = Nothing} - in serializeRawChatMessage $ rawChatMessage chatMsg +sendDirectMessage :: ChatMonad m => ConnId -> ChatMsgEvent -> m () +sendDirectMessage agentConnId chatMsgEvent = + void . withAgent $ \a -> sendMessage a agentConnId $ directMessage chatMsgEvent + +directMessage :: ChatMsgEvent -> ByteString +directMessage chatMsgEvent = + serializeRawChatMessage $ + rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent, chatDAG = Nothing} + +sendGroupMessage :: ChatMonad m => [(GroupMember, Maybe Connection)] -> ChatMsgEvent -> m () +sendGroupMessage _members _chatMsgEvent = pure () getCreateActiveUser :: SQLiteStore -> IO User getCreateActiveUser st = do @@ -341,6 +395,7 @@ chatCommandP = ("/help" <|> "/h") $> ChatHelp <|> ("/group #" <|> "/g #") *> (NewGroup <$> groupProfile) <|> ("/add #" <|> "/a #") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) + <|> ("/join #" <|> "/j #") *> (JoinGroup <$> displayName) <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> displayName <* A.space <*> displayName) <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> displayName) <|> ("/members #" <|> "/ms #") *> (ListMembers <$> displayName) @@ -354,9 +409,9 @@ chatCommandP = displayName = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) refChar c = c > ' ' && c /= '#' && c /= '@' groupProfile = do - gRef <- displayName - gName <- safeDecodeUtf8 <$> (A.space *> A.takeByteString) <|> pure "" - pure GroupProfile {displayName = gRef, fullName = if T.null gName then gRef else gName} + gName <- displayName + fullName' <- safeDecodeUtf8 <$> (A.space *> A.takeByteString) <|> pure "" + pure GroupProfile {displayName = gName, fullName = if T.null fullName' then gName else fullName'} memberRole = (" owner" $> GROwner) <|> (" admin" $> GRAdmin) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index fc6fa50820..4517a1b73f 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -40,7 +40,14 @@ data ChatError | ChatErrorStore StoreError deriving (Show, Exception) -data ChatErrorType = CEGroupRole | CEGroupDuplicateMember deriving (Show, Exception) +data ChatErrorType + = CEGroupRole + | CEGroupDuplicateMember ContactName + | CEGroupDuplicateMemberId + | CEGroupNotJoined GroupName + | CEGroupMemberNotReady + | CEGroupInternal String + deriving (Show, Exception) type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index d3dbd9606b..d120864c36 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -11,27 +11,26 @@ chatHelpInfo :: [StyledString] chatHelpInfo = map styleMarkdown - [ Markdown (Colored Cyan) "Using Simplex chat prototype.", + [ highlight "Using Simplex chat prototype.", "Follow these steps to set up a connection:", "", - Markdown (Colored Green) "Step 1: " <> Markdown (Colored Cyan) "/add bob" <> " -- Alice adds her contact, Bob (she can use any name).", + Markdown (Colored Green) "Step 1: " <> highlight "/add" <> " -- Alice adds a contact.", indent <> "Alice should send the invitation printed by the /add command", indent <> "to her contact, Bob, out-of-band, via any trusted channel.", "", - Markdown (Colored Green) "Step 2: " <> Markdown (Colored Cyan) "/connect alice " <> " -- Bob accepts the invitation.", - indent <> "Bob also can use any name for his contact, Alice,", - indent <> "followed by the invitation he received out-of-band.", + Markdown (Colored Green) "Step 2: " <> highlight "/connect " <> " -- Bob accepts the invitation.", + indent <> "Bob should use the invitation he received out-of-band.", "", Markdown (Colored Green) "Step 3: " <> "Bob and Alice are notified that the connection is set up,", indent <> "both can now send messages:", - indent <> Markdown (Colored Cyan) "@bob Hello, Bob!" <> " -- Alice messages Bob.", - indent <> Markdown (Colored Cyan) "@alice Hey, Alice!" <> " -- Bob replies to Alice.", + indent <> highlight "@bob Hello, Bob!" <> " -- Alice messages Bob (assuming Bob has display name 'bob').", + indent <> highlight "@alice Hey, Alice!" <> " -- Bob replies to Alice.", "", Markdown (Colored Green) "Other commands:", - indent <> Markdown (Colored Cyan) "/delete" <> " -- deletes contact and all messages with them.", - indent <> Markdown (Colored Cyan) "/markdown" <> " -- prints the supported markdown syntax.", + indent <> highlight "/delete " <> " -- deletes contact and all messages with them.", + indent <> highlight "/markdown" <> " -- prints the supported markdown syntax.", "", - "The commands may be abbreviated to a single letter: " <> listCommands ["/a", "/c", "/d", "/m"] + "The commands may be abbreviated to a single letter: " <> listCommands ["/a", "/c", "/d", "/h", "/m"] ] where listCommands = mconcat . intersperse ", " . map highlight diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 6fba8f3b77..1fe10b4939 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -30,9 +30,9 @@ import Simplex.Messaging.Util (bshow) data ChatDirection (p :: AParty) where ReceivedDirectMessage :: ConnContact -> ChatDirection 'Agent - SentDirectMessage :: ConnContact -> ChatDirection 'Client - ReceivedGroupMessage :: Group -> ConnContact -> ChatDirection 'Agent - SentGroupMessage :: Group -> ChatDirection 'Client + SentDirectMessage :: Contact -> ChatDirection 'Client + ReceivedGroupMessage :: GroupName -> GroupMember -> ChatDirection 'Agent + SentGroupMessage :: GroupName -> ChatDirection 'Client deriving instance Eq (ChatDirection p) @@ -48,12 +48,7 @@ data ChatMsgEvent content :: [MsgBodyContent] } | XInfo Profile - | XGrpInv - { fromMember :: (MemberId, GroupMemberRole), - invitedMember :: (MemberId, GroupMemberRole), - queueInfo :: SMPQueueInfo, - groupProfile :: GroupProfile - } + | XGrpInv GroupInvitation | XGrpAcpt MemberId | XGrpMemNew MemberId GroupMemberRole Profile | XGrpMemIntro MemberId GroupMemberRole Profile @@ -99,8 +94,8 @@ toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBod [fromMemId, fromRole, memId, role, qInfo] -> do fromMember <- (,) <$> B64.decode fromMemId <*> toMemberRole fromRole invitedMember <- (,) <$> B64.decode memId <*> toMemberRole role - msg <- XGrpInv fromMember invitedMember <$> parseAll smpQueueInfoP qInfo <*> getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + inv <- GroupInvitation fromMember invitedMember <$> parseAll smpQueueInfoP qInfo <*> getJSON body + pure ChatMessage {chatMsgId, chatMsgEvent = XGrpInv inv, chatDAG} _ -> Left "x.grp.inv expects 5 parameters" "x.grp.acpt" -> case chatMsgParams of [memId] -> do @@ -151,7 +146,7 @@ rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = XInfo profile -> let chatMsgBody = rawWithDAG [jsonBody profile] in RawChatMessage {chatMsgId, chatMsgEvent = "x.info", chatMsgParams = [], chatMsgBody} - XGrpInv (fromMemId, fromRole) (memId, role) qInfo groupProfile -> + XGrpInv (GroupInvitation (fromMemId, fromRole) (memId, role) qInfo groupProfile) -> let chatMsgParams = [ B64.encode fromMemId, serializeMemberRole fromRole, diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 86e8ffb5f2..d902cd42c1 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DuplicateRecordFields #-} @@ -25,9 +26,12 @@ module Simplex.Chat.Store getContactConnections, getConnectionChatDirection, createNewGroup, - createGroup, + createGroupInvitation, getGroup, + getGroupInvitation, createGroupMember, + createMemberConnection, + updateGroupMemberStatus, ) where @@ -42,7 +46,7 @@ import Data.ByteString.Char8 (ByteString) import Data.FileEmbed (embedDir, makeRelativeToProject) import Data.Function (on) import Data.Int (Int64) -import Data.List (sortBy) +import Data.List (find, sortBy) import Data.Maybe (listToMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -53,10 +57,10 @@ import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Protocol import Simplex.Chat.Types -import Simplex.Messaging.Agent.Protocol (AParty (..), ConnId) +import Simplex.Messaging.Agent.Protocol (AParty (..), ConnId, SMPQueueInfo) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore, withTransaction) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) -import Simplex.Messaging.Util (bshow, liftIOEither, (<$$>)) +import Simplex.Messaging.Util (bshow, liftIOEither) import System.FilePath (takeBaseName, takeExtension) import UnliftIO.STM @@ -83,7 +87,9 @@ handleSQLError err e insertedRowId :: DB.Connection -> IO Int64 insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()" -createUser :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> Profile -> Bool -> m User +type StoreMonad m = (MonadUnliftIO m, MonadError StoreError m) + +createUser :: StoreMonad m => SQLiteStore -> Profile -> Bool -> m User createUser st Profile {displayName, fullName} activeUser = liftIOEither . checkConstraint SEDuplicateName . withTransaction st $ \db -> do DB.execute db "INSERT INTO users (local_display_name, active_user, contact_id) VALUES (?, ?, 0)" (displayName, activeUser) @@ -131,8 +137,7 @@ createDirectConnection st userId agentConnId = |] (userId, agentConnId, ConnNew, ConnContact) -createDirectContact :: - (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> Connection -> Profile -> m () +createDirectContact :: StoreMonad m => SQLiteStore -> UserId -> Connection -> Profile -> m () createDirectContact st userId Connection {connId} Profile {displayName, fullName} = liftIOEither . withTransaction st $ \db -> withLocalDisplayName db userId displayName $ \localDisplayName' -> do @@ -174,7 +179,7 @@ deleteContact st userId displayName = -- TODO return the last connection that is ready, not any last connection -- requires updating connection status getContact :: - (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactName -> m Contact + StoreMonad m => SQLiteStore -> UserId -> ContactName -> m Contact getContact st userId localDisplayName = liftIOEither . withTransaction st $ \db -> runExceptT $ do c@Contact {contactId} <- getContact_ db @@ -212,7 +217,7 @@ getContact st userId localDisplayName = connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEContactNotReady localDisplayName -getContactConnections :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ContactName -> m [Connection] +getContactConnections :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m [Connection] getContactConnections st userId displayName = liftIOEither . withTransaction st $ \db -> connections @@ -234,6 +239,8 @@ getContactConnections st userId displayName = type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, UTCTime) +type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Int64, Maybe Int64, Maybe UTCTime) + toConnection :: ConnectionRow -> Connection toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) = let entityId = entityId_ connType @@ -243,20 +250,27 @@ toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, entityId_ ConnContact = contactId entityId_ ConnMember = groupMemberId -getConnectionChatDirection :: - (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> UserId -> ConnId -> m (ChatDirection 'Agent) -getConnectionChatDirection st userId agentConnId = - liftIOEither . withTransaction st $ \db -> do - getConnection_ db >>= \case - Left e -> pure $ Left e - Right c@Connection {connType, entityId} -> case connType of - ConnMember -> pure . Left $ SEInternal "group members not supported yet" - ConnContact -> - ReceivedDirectMessage <$$> case entityId of - Nothing -> pure . Right $ CConnection c - Just cId -> getContact_ db cId c +toMaybeConnection :: MaybeConnectionRow -> Maybe Connection +toMaybeConnection (Just connId, Just agentConnId, Just connLevel, viaContact, Just connStatus, Just connType, contactId, groupMemberId, Just createdAt) = + Just $ toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) +toMaybeConnection _ = Nothing + +getConnectionChatDirection :: StoreMonad m => SQLiteStore -> User -> ConnId -> m (ChatDirection 'Agent) +getConnectionChatDirection st User {userId, userContactId} agentConnId = + liftIOEither . withTransaction st $ \db -> runExceptT $ do + c@Connection {connType, entityId} <- getConnection_ db + case connType of + ConnMember -> + case entityId of + Nothing -> throwError $ SEInternal "group member without connection" + Just groupMemberId -> uncurry ReceivedGroupMessage <$> getGroupAndMember_ db groupMemberId + ConnContact -> + ReceivedDirectMessage <$> case entityId of + Nothing -> pure $ CConnection c + Just contactId -> getContact_ db contactId c where - getConnection_ db = + getConnection_ :: DB.Connection -> ExceptT StoreError IO Connection + getConnection_ db = ExceptT $ do connection <$> DB.query db @@ -267,9 +281,11 @@ getConnectionChatDirection st userId agentConnId = WHERE user_id = ? AND agent_conn_id = ? |] (userId, agentConnId) + connection :: [ConnectionRow] -> Either StoreError Connection connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEConnectionNotFound agentConnId - getContact_ db contactId c = + getContact_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO ConnContact + getContact_ db contactId c = ExceptT $ do toContact contactId c <$> DB.query db @@ -280,96 +296,94 @@ getConnectionChatDirection st userId agentConnId = WHERE c.user_id = ? AND c.contact_id = ? |] (userId, contactId) + toContact :: Int64 -> Connection -> [(ContactName, Text, Text)] -> Either StoreError ConnContact toContact contactId c [(localDisplayName, displayName, fullName)] = let profile = Profile {displayName, fullName} in Right $ CContact Contact {contactId, localDisplayName, profile, activeConn = c} toContact _ _ _ = Left $ SEInternal "referenced contact not found" + getGroupAndMember_ :: DB.Connection -> Int64 -> ExceptT StoreError IO (GroupName, GroupMember) + getGroupAndMember_ db groupMemberId = ExceptT $ do + toGroupAndMember + <$> DB.query + db + [sql| + SELECT + g.local_display_name, + m.group_member_id, m.member_id, m.member_role, m.member_status, + m.invited_by, m.contact_id, p.display_name, p.full_name + FROM group_members m + JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id + JOIN groups g ON g.group_id = m.group_id + WHERE m.group_member_id = ? + |] + (Only groupMemberId) + toGroupAndMember :: [Only GroupName :. GroupMemberRow] -> Either StoreError (GroupName, GroupMember) + toGroupAndMember [Only groupName :. memberRow] = Right (groupName, toGroupMember userContactId memberRow) + toGroupAndMember _ = Left $ SEInternal "referenced group member not found" -- | creates completely new group with a single member - the current user -createNewGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> GroupProfile -> m Group -createNewGroup st gVar User {userId, userContactId, profile} p@GroupProfile {displayName, fullName} = +createNewGroup :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> User -> GroupProfile -> m Group +createNewGroup st gVar user groupProfile = liftIOEither . checkConstraint SEDuplicateName . withTransaction st $ \db -> do - DB.execute db "INSERT INTO display_names (local_display_name, ldn_base, user_id) VALUES (?, ?, ?)" (displayName, displayName, userId) + let GroupProfile {displayName, fullName} = groupProfile + uId = userId user + DB.execute db "INSERT INTO display_names (local_display_name, ldn_base, user_id) VALUES (?, ?, ?)" (displayName, displayName, uId) DB.execute db "INSERT INTO group_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) profileId <- insertedRowId db - DB.execute db "INSERT INTO groups (local_display_name, user_id, group_profile_id) VALUES (?, ?, ?)" (displayName, userId, profileId) + DB.execute db "INSERT INTO groups (local_display_name, user_id, group_profile_id) VALUES (?, ?, ?)" (displayName, uId, profileId) groupId <- insertedRowId db memberId <- randomId gVar 12 - createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId - groupMemberId <- insertedRowId db - let membership = - GroupMember - { groupMemberId, - memberId, - memberRole = GROwner, - memberStatus = GSMemReady, - invitedBy = IBUser, - memberProfile = profile, - memberContactId = Just userContactId - } - pure $ Right Group {groupId, localDisplayName = displayName, groupProfile = p, members = [], membership} + membership <- createContactMember_ db user groupId user (memberId, GROwner) GSMemFull IBUser + pure $ Right Group {groupId, localDisplayName = displayName, groupProfile, members = [], membership} -- | creates a new group record for the group the current user was invited to -createGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> Contact -> GroupProfile -> m Group -createGroup st gVar User {userId, userContactId, profile} contact p@GroupProfile {displayName, fullName} = - liftIOEither . withTransaction st $ \db -> - withLocalDisplayName db userId displayName $ \localDisplayName -> do +createGroupInvitation :: + StoreMonad m => SQLiteStore -> User -> Contact -> GroupInvitation -> m Group +createGroupInvitation st user contact GroupInvitation {fromMember, invitedMember, queueInfo, groupProfile} = + liftIOEither . withTransaction st $ \db -> do + let GroupProfile {displayName, fullName} = groupProfile + uId = userId user + withLocalDisplayName db uId displayName $ \localDisplayName -> do DB.execute db "INSERT INTO group_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) profileId <- insertedRowId db - DB.execute - db - [sql| - INSERT INTO groups - (group_profile_id, local_display_name, user_id) VALUES (?, ?, ?) - |] - (profileId, localDisplayName, userId) + DB.execute db "INSERT INTO groups (group_profile_id, local_display_name, inv_queue_info, user_id) VALUES (?, ?, ?, ?)" (profileId, localDisplayName, queueInfo, uId) groupId <- insertedRowId db - pure Group {groupId, localDisplayName, groupProfile = p, members = undefined, membership = undefined} - --- where --- createMember_ db groupId userContactId GROwner GSMemReady (Just userContactId) memberId --- groupMemberId <- insertedRowId db --- let membership = --- GroupMember --- { groupMemberId, --- memberId, --- memberRole = GROwner, --- memberStatus = GSMemReady, --- invitedBy = IBUser, --- memberProfile = profile, --- memberContactId = Just userContactId --- } --- pure $ Right Group {groupId, localDisplayName = displayName, groupProfile = p, members = [], membership} + member <- createContactMember_ db user groupId contact fromMember GSMemFull IBUnknown + membership <- createContactMember_ db user groupId user invitedMember GSMemInvited (IBContact $ contactId contact) + pure Group {groupId, localDisplayName, groupProfile, members = [(member, Nothing)], membership} -- TODO return the last connection that is ready, not any last connection -- requires updating connection status -getGroup :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> User -> GroupName -> m Group -getGroup st User {userId, userContactId} localDisplayName = - liftIOEither . withTransaction st $ \db -> runExceptT $ do - g@Group {groupId} <- getGroup_ db - members <- getMembers_ db groupId - membership <- getUserMember_ db groupId - pure g {members, membership} +getGroup :: StoreMonad m => SQLiteStore -> User -> GroupName -> m Group +getGroup st user localDisplayName = + liftIOEither . withTransaction st $ \db -> runExceptT $ fst <$> getGroup_ db user localDisplayName + +getGroup_ :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO (Group, Maybe SMPQueueInfo) +getGroup_ db User {userId, userContactId} localDisplayName = do + (g@Group {groupId}, qInfo) <- getGroupRec_ + allMembers <- getMembers_ groupId + (members, membership) <- liftEither $ splitUserMember_ allMembers + pure (g {members, membership}, qInfo) where - getGroup_ :: DB.Connection -> ExceptT StoreError IO Group - getGroup_ db = ExceptT $ do + getGroupRec_ :: ExceptT StoreError IO (Group, Maybe SMPQueueInfo) + getGroupRec_ = ExceptT $ do toGroup <$> DB.query db [sql| - SELECT g.group_id, p.display_name, p.full_name + SELECT g.group_id, p.display_name, p.full_name, g.inv_queue_info FROM groups g JOIN group_profiles p ON p.group_profile_id = g.group_profile_id WHERE g.local_display_name = ? AND g.user_id = ? |] (localDisplayName, userId) - toGroup :: [(Int64, GroupName, Text)] -> Either StoreError Group - toGroup [(groupId, displayName, fullName)] = + toGroup :: [(Int64, GroupName, Text, Maybe SMPQueueInfo)] -> Either StoreError (Group, Maybe SMPQueueInfo) + toGroup [(groupId, displayName, fullName, qInfo)] = let groupProfile = GroupProfile {displayName, fullName} - in Right Group {groupId, localDisplayName, groupProfile, members = undefined, membership = undefined} + in Right (Group {groupId, localDisplayName, groupProfile, members = undefined, membership = undefined}, qInfo) toGroup _ = Left $ SEGroupNotFound localDisplayName - getMembers_ :: DB.Connection -> Int64 -> ExceptT StoreError IO [(GroupMember, Connection)] - getMembers_ db groupId = ExceptT $ do + getMembers_ :: Int64 -> ExceptT StoreError IO [(GroupMember, Maybe Connection)] + getMembers_ groupId = ExceptT $ do Right . map toContactMember <$> DB.query db @@ -380,81 +394,99 @@ getGroup st User {userId, userContactId} localDisplayName = c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at FROM group_members m - JOIN groups g ON g.group_id = m.group_id JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id - JOIN connections c ON c.group_member_id = m.group_member_id - WHERE g.group_id = ? - ORDER BY c.connection_id DESC - LIMIT 1 + LEFT JOIN connections c ON c.connection_id = ( + SELECT max(cc.connection_id) + FROM connections cc + where cc.group_member_id = c.group_member_id + ) + WHERE m.group_id = ? |] (Only groupId) - getUserMember_ :: DB.Connection -> Int64 -> ExceptT StoreError IO GroupMember - getUserMember_ db groupId = ExceptT $ do - userMember - <$> DB.query - db - [sql| - SELECT - m.group_member_id, m.member_id, m.member_role, m.member_status, - m.invited_by, m.contact_id, p.display_name, p.full_name - FROM group_members m - JOIN groups g ON g.group_id = m.group_id - JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id - WHERE g.group_id = ? AND m.contact_id = ? - |] - (groupId, userContactId) - toContactMember :: (GroupMemberRow :. ConnectionRow) -> (GroupMember, Connection) - toContactMember (memberRow :. connRow) = (toGroupMember memberRow, toConnection connRow) - toGroupMember :: GroupMemberRow -> GroupMember - toGroupMember (groupMemberId, memberId, memberRole, memberStatus, invitedById, memberContactId, displayName, fullName) = - let memberProfile = Profile {displayName, fullName} - invitedBy = toInvitedBy userContactId invitedById - in GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} - userMember :: [GroupMemberRow] -> Either StoreError GroupMember - userMember [memberRow] = Right $ toGroupMember memberRow - userMember _ = Left SEGroupWithoutUser + toContactMember :: (GroupMemberRow :. MaybeConnectionRow) -> (GroupMember, Maybe Connection) + toContactMember (memberRow :. connRow) = (toGroupMember userContactId memberRow, toMaybeConnection connRow) + splitUserMember_ :: [(GroupMember, Maybe Connection)] -> Either StoreError ([(GroupMember, Maybe Connection)], GroupMember) + splitUserMember_ allMembers = + let (b, a) = break ((== Just userContactId) . memberContactId . fst) allMembers + in case a of + [] -> Left SEGroupWithoutUser + u : ms -> Right (b <> ms, fst u) + +getGroupInvitation :: StoreMonad m => SQLiteStore -> User -> GroupName -> m ReceivedGroupInvitation +getGroupInvitation st user localDisplayName = + liftIOEither . withTransaction st $ \db -> runExceptT $ do + (Group {membership, members, groupProfile}, qInfo) <- getGroup_ db user localDisplayName + when (memberStatus membership /= GSMemInvited) $ throwError SEGroupAlreadyJoined + case (qInfo, findFromContact (invitedBy membership) members) of + (Just queueInfo, Just (fromMember, Nothing)) -> + pure ReceivedGroupInvitation {fromMember, invitedMember = membership, queueInfo, groupProfile} + _ -> throwError SENoGroupInvitation + where + findFromContact :: InvitedBy -> [(GroupMember, Maybe Connection)] -> Maybe (GroupMember, Maybe Connection) + findFromContact (IBContact contactId) = find (\(m, _) -> memberContactId m == Just contactId) + findFromContact _ = const Nothing type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberStatus, Maybe Int64, Maybe Int64, ContactName, Text) -createGroupMember :: (MonadUnliftIO m, MonadError StoreError m) => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Int64 -> GroupMemberRole -> InvitedBy -> ConnId -> m MemberId -createGroupMember st gVar User {userId, userContactId} groupId contactId memberRole invitedBy agentConnId = - liftIOEither . withTransaction st $ \db -> do - let invitedById = fromInvitedBy userContactId invitedBy - memberId <- createWithRandomId gVar $ createMember_ db groupId contactId memberRole GSMemInvited invitedById - groupMemberId <- insertedRowId db - liftIO $ createMemberConnection_ db groupMemberId - pure memberId - where - createMemberConnection_ :: DB.Connection -> Int64 -> IO () - createMemberConnection_ db groupMemberId = - DB.execute - db - [sql| - INSERT INTO connections - (user_id, agent_conn_id, conn_status, conn_type, group_member_id) VALUES (?,?,?,?,?); - |] - (userId, agentConnId, ConnNew, ConnMember, groupMemberId) +toGroupMember :: Int64 -> GroupMemberRow -> GroupMember +toGroupMember userContactId (groupMemberId, memberId, memberRole, memberStatus, invitedById, memberContactId, displayName, fullName) = + let memberProfile = Profile {displayName, fullName} + invitedBy = toInvitedBy userContactId invitedById + in GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} -createMember_ :: DB.Connection -> Int64 -> Int64 -> GroupMemberRole -> GroupMemberStatus -> Maybe Int64 -> ByteString -> IO () -createMember_ db groupId contactId memberRole memberStatus invitedBy memberId = - DB.executeNamed +createGroupMember :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Contact -> GroupMemberRole -> ConnId -> m GroupMember +createGroupMember st gVar user groupId contact memberRole agentConnId = + liftIOEither . withTransaction st $ \db -> + createWithRandomId gVar $ \memId -> do + member <- createContactMember_ db user groupId contact (memId, memberRole) GSMemInvited IBUser + groupMemberId <- insertedRowId db + createMemberConnection_ db (userId user) groupMemberId agentConnId + pure member + +createMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> ConnId -> m () +createMemberConnection st userId groupMemberId agentConnId = + liftIO . withTransaction st $ \db -> createMemberConnection_ db userId groupMemberId agentConnId + +updateGroupMemberStatus :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> GroupMemberStatus -> m () +updateGroupMemberStatus _st _userId _groupMemberId _memberStatus = pure () + +createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> IO () +createMemberConnection_ db userId groupMemberId agentConnId = + DB.execute db [sql| - INSERT INTO group_members - ( group_id, member_id, member_role, member_status, invited_by, - contact_profile_id, contact_id) - VALUES - (:group_id,:member_id,:member_role,:member_status,:invited_by, - (SELECT contact_profile_id FROM contacts WHERE contact_id = :contact_id), - :contact_id) + INSERT INTO connections + (user_id, agent_conn_id, conn_status, conn_type, group_member_id) VALUES (?,?,?,?,?); |] - [ ":group_id" := groupId, - ":member_id" := memberId, - ":member_role" := memberRole, - ":member_status" := memberStatus, - ":invited_by" := invitedBy, - ":contact_id" := contactId - ] + (userId, agentConnId, ConnNew, ConnMember, groupMemberId) + +createContactMember_ :: IsContact a => DB.Connection -> User -> Int64 -> a -> MemberInfo -> GroupMemberStatus -> InvitedBy -> IO GroupMember +createContactMember_ db User {userContactId} groupId userOrContact (memberId, memberRole) memberStatus invitedBy = do + insertMember_ + groupMemberId <- insertedRowId db + let memberProfile = profile' userOrContact + memberContactId = Just $ contactId' userOrContact + pure GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} + where + insertMember_ = + DB.executeNamed + db + [sql| + INSERT INTO group_members + ( group_id, member_id, member_role, member_status, invited_by, + contact_profile_id, contact_id) + VALUES + (:group_id,:member_id,:member_role,:member_status,:invited_by, + (SELECT contact_profile_id FROM contacts WHERE contact_id = :contact_id), + :contact_id) + |] + [ ":group_id" := groupId, + ":member_id" := memberId, + ":member_role" := memberRole, + ":member_status" := memberStatus, + ":invited_by" := fromInvitedBy userContactId invitedBy, + ":contact_id" := contactId' userOrContact + ] -- | Saves unique local display name based on passed displayName, suffixed with _N if required. -- This function should be called inside transaction. @@ -492,15 +524,15 @@ withLocalDisplayName db userId displayName action = getLdnSuffix >>= (`tryCreate |] (ldn, displayName, ldnSuffix, userId) -createWithRandomId :: TVar ChaChaDRG -> (ByteString -> IO ()) -> IO (Either StoreError ByteString) +createWithRandomId :: forall a. TVar ChaChaDRG -> (ByteString -> IO a) -> IO (Either StoreError a) createWithRandomId gVar create = tryCreate 3 where - tryCreate :: Int -> IO (Either StoreError ByteString) + tryCreate :: Int -> IO (Either StoreError a) tryCreate 0 = pure $ Left SEUniqueID tryCreate n = do id' <- randomId gVar 12 E.try (create id') >>= \case - Right _ -> pure $ Right id' + Right x -> pure $ Right x Left e | DB.sqlError e == DB.ErrorConstraint -> tryCreate (n - 1) | otherwise -> pure . Left . SEInternal $ bshow e @@ -515,6 +547,8 @@ data StoreError | SEGroupNotFound GroupName | SEGroupWithoutUser | SEDuplicateGroupMember + | SEGroupAlreadyJoined + | SENoGroupInvitation | SEConnectionNotFound ConnId | SEUniqueID | SEInternal ByteString diff --git a/src/Simplex/Chat/Styled.hs b/src/Simplex/Chat/Styled.hs index c344ddf4f6..9bbb88d277 100644 --- a/src/Simplex/Chat/Styled.hs +++ b/src/Simplex/Chat/Styled.hs @@ -3,10 +3,9 @@ module Simplex.Chat.Styled ( StyledString (..), - plain, + StyledFormat (..), styleMarkdown, styleMarkdownText, - styled, sLength, ) where diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 7d7e616025..605f315a17 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -20,9 +20,21 @@ import Database.SQLite.Simple.Internal (Field (..)) import Database.SQLite.Simple.Ok (Ok (Ok)) import Database.SQLite.Simple.ToField (ToField (..)) import GHC.Generics -import Simplex.Messaging.Agent.Protocol (ConnId) +import Simplex.Messaging.Agent.Protocol (ConnId, SMPQueueInfo) import Simplex.Messaging.Agent.Store.SQLite (fromTextField_) +class IsContact a where + contactId' :: a -> Int64 + profile' :: a -> Profile + +instance IsContact User where + contactId' = userContactId + profile' = profile + +instance IsContact Contact where + contactId' = contactId + profile' = profile + data User = User { userId :: UserId, userContactId :: Int64, @@ -52,7 +64,7 @@ data Group = Group { groupId :: Int64, localDisplayName :: GroupName, groupProfile :: GroupProfile, - members :: [(GroupMember, Connection)], + members :: [(GroupMember, Maybe Connection)], membership :: GroupMember } deriving (Eq, Show) @@ -77,6 +89,24 @@ instance ToJSON GroupProfile where toEncoding = J.genericToEncoding J.defaultOpt instance FromJSON GroupProfile +data GroupInvitation = GroupInvitation + { fromMember :: MemberInfo, + invitedMember :: MemberInfo, + queueInfo :: SMPQueueInfo, + groupProfile :: GroupProfile + } + deriving (Eq, Show) + +data ReceivedGroupInvitation = ReceivedGroupInvitation + { fromMember :: GroupMember, + invitedMember :: GroupMember, + queueInfo :: SMPQueueInfo, + groupProfile :: GroupProfile + } + deriving (Eq, Show) + +type MemberInfo = (MemberId, GroupMemberRole) + data GroupMember = GroupMember { groupMemberId :: Int64, memberId :: MemberId, @@ -133,8 +163,13 @@ fromBlobField_ p = \case Left e -> returnError ConversionFailed f ("couldn't parse field: " ++ e) f -> returnError ConversionFailed f "expecting SQLBlob column type" -data GroupMemberStatus = GSMemInvited | GSMemAccepted | GSMemConnected | GSMemReady - deriving (Eq, Show) +data GroupMemberStatus + = GSMemInvited -- member received (or sent to) invitation + | GSMemAccepted -- member accepted invitation + | GSMemConnected -- member created the group connection with the inviting member + | GSMemReady -- member connections are forwarded to all previous members + | GSMemFull -- member created group connections with all previous members + deriving (Eq, Show, Ord) instance FromField GroupMemberStatus where fromField = fromTextField_ memberStatusT @@ -146,6 +181,7 @@ memberStatusT = \case "accepted" -> Just GSMemAccepted "connected" -> Just GSMemConnected "ready" -> Just GSMemReady + "full" -> Just GSMemFull _ -> Nothing serializeMemberStatus :: GroupMemberStatus -> Text @@ -154,6 +190,7 @@ serializeMemberStatus = \case GSMemAccepted -> "accepted" GSMemConnected -> "connected" GSMemReady -> "ready" + GSMemFull -> "full" data Connection = Connection { connId :: Int64, diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 226f1888fd..dee04bb327 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -15,6 +15,10 @@ module Simplex.Chat.View showReceivedMessage, showSentMessage, showGroupCreated, + showSentGroupInvitation, + showReceivedGroupInvitation, + showConnectedGroupMember, + showUserConnectedToGroup, safeDecodeUtf8, ) where @@ -22,6 +26,7 @@ where import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.ByteString.Char8 (ByteString) +import Data.Composition ((.:)) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (DiffTime, UTCTime) @@ -48,7 +53,7 @@ showChatError = printToView . chatError showContactDeleted :: ChatReader m => ContactName -> m () showContactDeleted = printToView . contactDeleted -showContactConnected :: ChatReader m => ContactName -> m () +showContactConnected :: ChatReader m => Contact -> m () showContactConnected = printToView . contactConnected showContactDisconnected :: ChatReader m => ContactName -> m () @@ -60,29 +65,59 @@ showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage showSentMessage :: ChatReader m => ContactName -> ByteString -> m () showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) -showGroupCreated :: ChatReader m => GroupProfile -> m () +showGroupCreated :: ChatReader m => Group -> m () showGroupCreated = printToView . groupCreated +showSentGroupInvitation :: ChatReader m => Group -> ContactName -> m () +showSentGroupInvitation = printToView .: sentGroupInvitation + +showReceivedGroupInvitation :: ChatReader m => Group -> ContactName -> m () +showReceivedGroupInvitation = printToView .: receivedGroupInvitation + +showConnectedGroupMember :: ChatReader m => GroupName -> ContactName -> m () +showConnectedGroupMember = printToView .: connectedGroupMember + +showUserConnectedToGroup :: ChatReader m => GroupName -> m () +showUserConnectedToGroup = printToView . userConnectedToGroup + invitation :: SMPQueueInfo -> [StyledString] invitation qInfo = [ "pass this invitation to your contact (via another channel): ", "", (plain . serializeSmpQueueInfo) qInfo, "", - "and ask them to connect: /c " + "and ask them to connect: " <> highlight' "/c " ] contactDeleted :: ContactName -> [StyledString] contactDeleted c = [ttyContact c <> " is deleted"] -contactConnected :: ContactName -> [StyledString] -contactConnected c = [ttyContact c <> " is connected"] +contactConnected :: Contact -> [StyledString] +contactConnected ct = [ttyFullContact ct <> " is connected"] contactDisconnected :: ContactName -> [StyledString] contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] -groupCreated :: GroupProfile -> [StyledString] -groupCreated GroupProfile {displayName, fullName} = ["group " <> ttyGroup displayName <> " (" <> plain fullName <> ") is created"] +groupCreated :: Group -> [StyledString] +groupCreated g@Group {localDisplayName} = + [ "group " <> ttyFullGroup g <> " is created", + "use " <> highlight ("/a #" <> localDisplayName <> " ") <> " to add members" + ] + +sentGroupInvitation :: Group -> ContactName -> [StyledString] +sentGroupInvitation g c = ["invitation to join the group " <> ttyFullGroup g <> " sent to " <> ttyContact c] + +receivedGroupInvitation :: Group -> ContactName -> [StyledString] +receivedGroupInvitation g@Group {localDisplayName} c = + [ ttyContact c <> " invites you to join the group " <> ttyFullGroup g, + "use " <> highlight ("/j #" <> localDisplayName) <> " to accept" + ] + +connectedGroupMember :: GroupName -> ContactName -> [StyledString] +connectedGroupMember g c = [ttyContact c <> " joined the group " <> ttyGroup g] + +userConnectedToGroup :: GroupName -> [StyledString] +userConnectedToGroup g = ["you joined the group " <> ttyGroup g] receivedMessage :: ContactName -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage c utcTime msg mOk = do @@ -124,10 +159,20 @@ msgPlain = map styleMarkdownText . T.lines chatError :: ChatError -> [StyledString] chatError = \case + ChatError err -> case err of + CEGroupDuplicateMember c -> ["contact " <> ttyContact c <> " is already in the group"] + CEGroupDuplicateMemberId -> ["cannot add member - duplicate member ID"] + CEGroupRole -> ["insufficient role for this group command"] + CEGroupNotJoined g -> ["you did not join this group, use " <> highlight ("/join #" <> g)] + CEGroupMemberNotReady -> ["you cannot invite other members yet, try later"] + CEGroupInternal s -> ["chat group bug: " <> plain s] + -- e -> ["chat error: " <> plain (show e)] ChatErrorStore err -> case err of SEDuplicateName -> ["this display name is already used by user, contact or group"] SEContactNotFound c -> ["no contact " <> ttyContact c] SEContactNotReady c -> ["contact " <> ttyContact c <> " is not active yet"] + SEGroupNotFound g -> ["no group " <> ttyGroup g] + SEGroupAlreadyJoined -> ["you already joined this group"] e -> ["chat db error: " <> plain (show e)] ChatErrorAgent err -> case err of -- CONN e -> case e of @@ -136,7 +181,7 @@ chatError = \case -- DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] -- SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] e -> ["smp agent error: " <> plain (show e)] - e -> ["chat error: " <> plain (show e)] + ChatErrorMessage e -> ["chat message error: " <> plain (show e)] printToView :: (MonadUnliftIO m, MonadReader ChatController m) => [StyledString] -> m () printToView s = asks chatTerminal >>= liftIO . (`printToTerminal` s) @@ -144,6 +189,10 @@ printToView s = asks chatTerminal >>= liftIO . (`printToTerminal` s) ttyContact :: ContactName -> StyledString ttyContact = styled (Colored Green) +ttyFullContact :: Contact -> StyledString +ttyFullContact Contact {localDisplayName, profile = Profile {fullName}} = + ttyContact localDisplayName <> optFullName localDisplayName fullName + ttyToContact :: ContactName -> StyledString ttyToContact c = styled (Colored Cyan) $ "@" <> c <> " " @@ -153,6 +202,21 @@ ttyFromContact c = styled (Colored Yellow) $ c <> "> " ttyGroup :: GroupName -> StyledString ttyGroup g = styled (Colored Blue) $ "#" <> g +ttyFullGroup :: Group -> StyledString +ttyFullGroup Group {localDisplayName, groupProfile = GroupProfile {fullName}} = + ttyGroup localDisplayName <> optFullName localDisplayName fullName + +optFullName :: Text -> Text -> StyledString +optFullName localDisplayName fullName + | localDisplayName == fullName = "" + | otherwise = plain (" (" <> fullName <> ")") + +highlight :: StyledFormat a => a -> StyledString +highlight = styled (Colored Cyan) + +highlight' :: String -> StyledString +highlight' = highlight + -- ttyFromGroup :: Group -> Contact -> StyledString -- ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md index d363800b48..adec3f48f3 100644 --- a/src/Simplex/Chat/protocol.md +++ b/src/Simplex/Chat/protocol.md @@ -97,3 +97,63 @@ A -> group: `MSG: N x.grp.mem.ok G_MEM_ID_B` #### Send group message `MSG: N x.msg.new G_MEM_ROLE, x.json:NNN ` + +#### Group member statuses + +1. Me + - invited + - accepted + - connected to member who invited me + - announced to group + - x.grp.mem.new to group + - confirmed as connected to group + - this happens once member who invited me sends x.grp.mem.ok to group +1. Member that I invited: + - invited + - accepted + - connected to me + - announced to group + - this happens after x.grp.mem.new but before introductions are sent. + This message is used to determine which members should be additionally introduced if they were announced before (or in "parallel"). + - confirmed as connected to group +2. Member who invited me + - invited_me + - connected to me + - I won't know whether this member was announced or confirmed to group - with the correctly functioning clients it must have happened. +3. Prior member introduced to me after I joined (x.grp.mem.intro) + - introduced + - sent invitation + - connected + - connected directly (or confirmed existing contact) +4. Member I was introduced to after that member joined (via x.grp.mem.fwd) + - announced via x.grp.mem.new + - received invitation + - connected + - connected directly (or confirmed existing contact) + +#### Introductions + +1. Introductions I sent to members I invited + - the time of joining is determined by the time of creating the connection and sending the x.grp.mem.new message to the group. + - introductions of the members who were connected before the new member should be sent - how to determine which members were connected before? + - use time stamp of creating connection, possibly in the member record - not very reliable, as time can change. + - use record ID - requires changing the schema, as currently members are added as invited, not as connected. So possibly invited members should be tracked in a separate table, and all members should still be tracked together to ensure that memberId is unique. + - record ID is also not 100% sufficient, as there can be forks in message history and I may need to intro the member I invited to the member that was announced after my member in my chronology, but in another graph branch. + - some other mechanism that allows to establish who should be connected to whom and whether I should introduce or another member (in case of forks - although maybe we both can introduce and eventually two group connections will be created between these members and they would just ignore the first one - although in cases of multiple branches in the graph it can be N connections). + - introductions/member connection statuses: + - created introduction + - sent to the member I invited + - received the invitation from the member I invited + - forwarded this invitation to previously connected member + - received confirmation from member I invited + - received confirmation from member I forwarded to + - completed introduction and recorded that these members are now fully connected to each other +2. Introductions I received from the member who invited me + - if somebody else sends such introduction - this is an error (can be logged or ignored) + - duplicate memberId is an error (e.g. it is a member that was announced in the group broadcast - I should be introduced to this member, and not the other way around? Although it can happen in case of fork and maybe I should establish the connection anyway). + - member connection status in this case is just a member status from part 3, so maybe no need to track invitations separately and just put SMPQueueInfo on member record. +3. Invitation forwarded to me by any prior member + - any admin/owner can add members, so they can forward their queue invitations - I should just check forwarding member permission + - duplicate memberId is an error + - unannounced memberId is an error - I should have seen member announcement prior to receiving this forwarded invitation. Fork would not happen here as it is the same member that announces and forwards the invitation, so they should be in order. + - member connection status in this case is just a member status from part 4, so maybe no need to track invitations separately and just put SMPQueueInfo on member record. diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 4b5907ee77..be249edbeb 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -13,6 +13,7 @@ import Simplex.Chat.Controller (ChatController (..)) import Simplex.Chat.Options import Simplex.Chat.Store import Simplex.Chat.Types (Profile) +import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import qualified System.Terminal as C import System.Terminal.Internal (VirtualTerminal, VirtualTerminalSettings (..), withVirtualTerminal) @@ -51,6 +52,8 @@ virtualSimplexChat dbFile profile = do testChat2 :: Profile -> Profile -> (TestCC -> TestCC -> IO ()) -> IO () testChat2 p1 p2 test = do + createDirectoryIfMissing False "tests/tmp" tc1 <- virtualSimplexChat testDB1 p1 tc2 <- virtualSimplexChat testDB2 p2 test tc1 tc2 + removeDirectoryRecursive "tests/tmp" diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index a13c591d01..28c847621d 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -18,16 +18,23 @@ aliceProfile = Profile {displayName = "alice", fullName = "Alice"} bobProfile :: Profile bobProfile = Profile {displayName = "bob", fullName = "Bob"} -testAddContact :: Spec -testAddContact = describe "add chat contact" $ - it "add contact and send/receive message" $ - testChat2 aliceProfile bobProfile $ \alice bob -> do +chatTests :: Spec +chatTests = do + describe "direct messages" $ + it "add contact and send/receive message" testAddContact + describe "chat groups" $ + it "add contacts, create group and send/receive messages" testGroup + +testAddContact :: IO () +testAddContact = + testChat2 aliceProfile bobProfile $ + \alice bob -> do alice ##> "/a" Just inv <- invitation <$> getWindow alice bob ##> ("/c " <> inv) concurrently_ - (bob <## "alice is connected") - (alice <## "bob is connected") + (bob <## "alice (Alice) is connected") + (alice <## "bob (Bob) is connected") alice #> "@bob hello" bob <# "alice> hello" bob #> "@alice hi" @@ -37,8 +44,8 @@ testAddContact = describe "add chat contact" $ Just inv' <- invitation <$> getWindow alice bob ##> ("/c " <> inv') concurrently_ - (bob <## "alice_1 is connected") - (alice <## "bob_1 is connected") + (bob <## "alice_1 (Alice) is connected") + (alice <## "bob_1 (Bob) is connected") alice #> "@bob_1 hello" bob <# "alice_1> hello" bob #> "@alice_1 hi" @@ -46,21 +53,46 @@ testAddContact = describe "add chat contact" $ -- test deleting contact alice ##> "/d bob_1" alice <## "bob_1 is deleted" - chatCommand alice "@bob_1 hey" + alice #:> "@bob_1 hey" alice <## "no contact bob_1" +testGroup :: IO () +testGroup = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + alice #:> "/g #team" + -- TODO this occasionally fails in case getWindow is run before the command above is printed + alice <## "use /a #team to add members" + alice ##> "/a #team bob admin" + alice <## "invitation to join the group #team sent to bob" + bob <## "use /j #team to accept" + bob ##> "/j #team" + concurrently_ + (alice <## "bob joined the group #team") + (bob <## "you joined the group #team") + +connectUsers :: TestCC -> TestCC -> IO () +connectUsers cc1 cc2 = do + cc1 ##> "/a" + Just inv <- invitation <$> getWindow cc1 + cc2 ##> ("/c " <> inv) + concurrently_ + (cc2 <## "alice (Alice) is connected") + (cc1 <## "bob (Bob) is connected") + (##>) :: TestCC -> String -> IO () (##>) cc cmd = do - chatCommand cc cmd + cc #:> cmd cc <## cmd (#>) :: TestCC -> String -> IO () (#>) cc cmd = do - chatCommand cc cmd + cc #:> cmd cc <# cmd -chatCommand :: TestCC -> String -> IO () -chatCommand (TestCC cc _ _) cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd +(#:>) :: TestCC -> String -> IO () +(#:>) (TestCC cc _ _) cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd (<##) :: TestCC -> String -> Expectation cc <## line = (lastOutput <$> getWindow cc) `shouldReturn` line diff --git a/tests/Test.hs b/tests/Test.hs index 990d86ca0b..ef5368a7ee 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -1,14 +1,11 @@ import ChatTests import MarkdownTests import ProtocolTests -import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import Test.Hspec main :: IO () main = do - createDirectoryIfMissing False "tests/tmp" hspec $ do describe "SimpleX chat markdown" markdownTests describe "SimpleX chat protocol" protocolTests - xdescribe "SimpleX chat client" testAddContact - removeDirectoryRecursive "tests/tmp" + xdescribe "SimpleX chat client" chatTests From 94f89ed8f714ec3173975be3ccc93a683ca834a1 Mon Sep 17 00:00:00 2001 From: Nikita Poberezkin <39944650+npoberezkin@users.noreply.github.com> Date: Sat, 24 Jul 2021 11:57:10 +0300 Subject: [PATCH 12/29] merge master to v4 (#83) * update ghc version to 8.10.4 for Docker build (#67) * make broader check for WSL on notifications (#68) * update readme: network topology and disclaimer on encryption design (#73) * update readme with the disclaimer on encryption design and explanation of the network topology * corrections * remove old disclaimer Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> * create appDir if absent (#79) Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- Dockerfile | 4 +-- README.md | 49 +++++++++++++++++++++----------- src/Simplex/Chat/Notification.hs | 5 ++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index c03da4ac63..a5d91ab2dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM haskell:8.8.4 AS build-stage +FROM haskell:8.10.4 AS build-stage # if you encounter "version `GLIBC_2.28' not found" error when running # chat client executable, build with the following base image instead: -# FROM haskell:8.8.4-stretch AS build-stage +# FROM haskell:8.10.4-stretch AS build-stage COPY . /project WORKDIR /project RUN stack install diff --git a/README.md b/README.md index 8ddf47d0db..93dd4f9e66 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # SimpleX chat -## Federated, private, secure, decentralized +## Private, secure, decentralized [![GitHub build](https://github.com/simplex-chat/simplex-chat/workflows/build/badge.svg)](https://github.com/simplex-chat/simplex-chat/actions?query=workflow%3Abuild) [![GitHub release](https://img.shields.io/github/v/release/simplex-chat/simplex-chat)](https://github.com/simplex-chat/simplex-chat/releases) @@ -17,7 +17,9 @@ See [simplex.chat](https://simplex.chat) website for chat demo and the explanati ## Table of contents -- [Features](#features) +- [Disclaimer](#disclaimer) +- [Network topology](#network-topology) +- [Current features of the terminal chat](#current-features-of-the-terminal-chat) - [Installation](#installation) - [Download chat client](#download-chat-client) - [Build from source](#build-from-source) @@ -28,10 +30,27 @@ See [simplex.chat](https://simplex.chat) website for chat demo and the explanati - [How to use SimpleX chat](#how-to-use-simplex-chat) - [Access chat history](#access-chat-history) - [Roadmap](#roadmap) -- [Disclaimer](#disclaimer) - [License](#license) -## Features +## Disclaimer + +This is WIP implementation of SimpleX chat that implements a new network topology for asynchronous communication combining the advantages and avoiding the disadvantages of federated and P2P networks. + +If you expect a software being reliable most of the time and doing something useful, then this is probably not ready for you yet. We do use it for terminal chat though, and it seems to work most of the time - we would really appreciate if you try it and give us your feedback. + +**Please note:** The main differentiation of SimpleX network is the approach to internet message routing rather than encryption; for that reason no sufficient attention was paid to either TCP transport level encryption or to E2E encryption protocols - they are implemented in an ad hoc way based on RSA and AES algorithms. See [SMP protocol](https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#appendix-a) on TCP transport encryption protocol (AEAD-GCM scheme, with an AES key negotiation based on RSA key hash known to the client in advance) and [this section](https://github.com/simplex-chat/simplexmq/blob/master/rfcs/2021-01-26-crypto.md#e2e-encryption) on E2E encryption protocol (an ad hoc hybrid scheme a la PGP). These protocols will change in a consumer ready version to something more robust. + +## Network topology + +SimpleX is a decentralized client-server network that uses redundant, disposable nodes to asynchronously pass the messages via message queues, providing receiver and sender anonymity. + +Unlike P2P networks, all messages are passed through one or several (for redundancy) servers, that do not even need to have persistence (in fact, the current [SMP server implementation](https://github.com/simplex-chat/simplexmq#smp-server) uses in-memory message storage, persisting only the queue records) - it provides better metadata protection than P2P designs, as no global participant ID is required, and avoids many [problems of P2P networks](https://github.com/simplex-chat/simplex-chat/blob/master/simplex.md#comparison-with-p2p-messaging-protocols). + +Unlike federated networks, the participating server nodes do NOT have records of the users, do NOT communicate with each other, do NOT store messages after they are delivered to the recipients, and there is no way to discover the full list of participating servers - it avoids the problem of metadata visibility that federated networks suffer from and better protects the network, as servers do not communicate with each other. Each server node provides unidirectional "dumb pipes" to the users, that do authorization without authentication, having no knowledge of the the users or their contacts. Each queue is assigned two RSA keys - one for receiver and one for sender - and each queue access is authorized with a signature created using a respective key's private counterpart. + +The routing of messages relies on the knowledge of client devices how user contacts and groups map at any given moment of time to these disposable queues on server nodes. + +## Current features of the terminal chat - 1-to-1 chat with multiple people in the same terminal window. - Auto-populated recipient name - just type your messages to reply to the sender once the connection is established. @@ -171,20 +190,18 @@ order by internal_id desc; ## Roadmap -1. Switch to application level chat protocol. This will allow to separate physical server connection management from logical chat contacts, and to support all common chat functions. -2. SMP queue redundancy and rotation in SMP agent protocol. -3. Symmetric groups support in SMP agent protocol, as a foundation for chat groups. -4. Delivery confirmation in SMP agent protocol. -5. Multi-agent/device data synchronisation - to use chat on multiple devices. +The consumer ready system will have these parts implemented: + +1. Application level chat protocol. This will allow to separate physical server connection management from logical chat contacts, and to support all common chat functions. Currently in progress in [v4 branch](https://github.com/simplex-chat/simplex-chat/tree/v4). +2. Symmetric groups support in SMP agent protocol, as a foundation for chat groups. +3. SMP queue redundancy and rotation in SMP agent protocol. +4. Message delivery confirmation in SMP agent protocol. +5. Multi-agent/device data synchronization - to use chat on multiple devices. 6. Synchronous streams support in SMP and SMP agent protocols, to support file transfer. -7. Terminal chat UI and mobile apps. -8. Scripts for simple SMP server deployment to hosting providers: Linode, Digital Ocean and Heroku. +7. Desktop and mobile apps. +8. Scripts for simple SMP server deployment to hosting providers: Linode ([done](https://github.com/simplex-chat/simplexmq#deploy-smp-server-on-linode)), Digital Ocean and Heroku. 9. Public broadcast channels. -10. Optional public contact/group addresses using DNS to establish connections, but not using it to send and receive messages - in this way you will keep all your contacts and groups even if you lose the control of the domain. - -## Disclaimer - -System and protocol security have not been audited yet. As such, it is not advised to use SimpleX chat for high security communications. +10. Optional public contact/group addresses using DNS-based contact addresses (like email) to establish connections, but not using it to route messages - in this way you will keep all your contacts and groups even if you lose the control of the domain. ## License diff --git a/src/Simplex/Chat/Notification.hs b/src/Simplex/Chat/Notification.hs index 718d3c18f5..66d99137ba 100644 --- a/src/Simplex/Chat/Notification.hs +++ b/src/Simplex/Chat/Notification.hs @@ -9,7 +9,7 @@ import Data.Char (toLower) import Data.List (isInfixOf) import Data.Text (Text) import qualified Data.Text as T -import System.Directory (doesFileExist, getAppUserDataDirectory) +import System.Directory (createDirectoryIfMissing, doesFileExist, getAppUserDataDirectory) import System.FilePath (combine) import System.Info (os) import System.Process (readCreateProcess, shell) @@ -25,7 +25,7 @@ initializeNotifications = case os of False -> pure $ notify linuxScript True -> do v <- readFile "/proc/sys/kernel/osrelease" - if "wsl" `isInfixOf` map toLower v + if "Microsoft" `isInfixOf` v || "WSL" `isInfixOf` v then initWinNotify else pure $ notify linuxScript _ -> pure . const $ pure () @@ -49,6 +49,7 @@ winScript path Notification {title, text} = "powershell.exe \"" <> T.pack path < savePowershellScript :: IO FilePath savePowershellScript = do appDir <- getAppUserDataDirectory "simplex" + createDirectoryIfMissing False appDir let psScript = combine appDir "win-toast-notify.ps1" writeFile psScript From 189cd7e09db6fea88021bd725547bd284120af85 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 24 Jul 2021 10:26:28 +0100 Subject: [PATCH 13/29] core chat groups protocol for adding members (#78) * add category and local display name to group members, extend member status * additional chat commands, serialization * parse all chat messages * draft group protocol implementation * group protocol: connect new member to existing members (TODO fix race condition with contact connection) * send/receive group messages (race condition still there - the 3rd member cannot send either group or direct messages to the 2nd member - CONN SIMPLEX) * send x.grp.mem.info and x.ok in SMP confirmation * fix host user adding new member, update simplexmq to fix sqlite concurrency, remove logs, make # optional in chat commands * more precise view messages about members joining and connecting * track connection status; only send messages to active members (TODO change to current members); group name autocomplete after joining the group * track via which group the contact was added; show only one message when a contact fully connected; group tests * test sending messages to the new direct contacts created via the group * update simplexmq to include .cabal file * remove unused import --- migrations/20210612_initial.sql | 30 +- src/Simplex/Chat.hs | 337 +++++++++++++++------ src/Simplex/Chat/Controller.hs | 5 +- src/Simplex/Chat/Input.hs | 2 +- src/Simplex/Chat/Notification.hs | 1 - src/Simplex/Chat/Protocol.hs | 206 +++++++------ src/Simplex/Chat/Store.hs | 482 ++++++++++++++++++++++++++----- src/Simplex/Chat/Terminal.hs | 2 +- src/Simplex/Chat/Types.hs | 200 +++++++++++-- src/Simplex/Chat/View.hs | 116 +++++--- src/Simplex/Chat/protocol.md | 16 +- stack.yaml | 2 +- tests/ChatClient.hs | 49 +++- tests/ChatTests.hs | 161 ++++++++++- 14 files changed, 1262 insertions(+), 347 deletions(-) diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index 26fdbb9348..ed10094cbe 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -14,6 +14,7 @@ CREATE TABLE users ( FOREIGN KEY (user_id, local_display_name) REFERENCES display_names (user_id, local_display_name) ON DELETE RESTRICT + ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED ); @@ -32,10 +33,12 @@ CREATE TABLE contacts ( user_id INTEGER NOT NULL REFERENCES users, local_display_name TEXT NOT NULL, is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user + via_group INTEGER REFERENCES groups (group_id) ON DELETE SET NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')), FOREIGN KEY (user_id, local_display_name) REFERENCES display_names (user_id, local_display_name) - ON DELETE RESTRICT, + ON DELETE RESTRICT + ON UPDATE CASCADE, UNIQUE (user_id, local_display_name), UNIQUE (user_id, contact_profile_id) ); @@ -64,7 +67,8 @@ CREATE TABLE groups ( inv_queue_info BLOB, FOREIGN KEY (user_id, local_display_name) REFERENCES display_names (user_id, local_display_name) - ON DELETE RESTRICT, + ON DELETE RESTRICT + ON UPDATE CASCADE, UNIQUE (user_id, local_display_name), UNIQUE (user_id, group_profile_id) ); @@ -74,20 +78,32 @@ CREATE TABLE group_members ( -- group members, excluding the local user group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, member_id BLOB NOT NULL, -- shared member ID, unique per group member_role TEXT NOT NULL, -- owner, admin, member - member_status TEXT NOT NULL, -- new, invited, accepted, connected, ready + member_category TEXT NOT NULL, -- see GroupMemberCategory + member_status TEXT NOT NULL, -- see GroupMemberStatus invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator + group_queue_info BLOB, + direct_queue_info BLOB, + user_id INTEGER NOT NULL REFERENCES users, + local_display_name TEXT NOT NULL, -- should be the same as contact contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles ON DELETE RESTRICT, contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, + FOREIGN KEY (user_id, local_display_name) + REFERENCES display_names (user_id, local_display_name) + ON DELETE RESTRICT + ON UPDATE CASCADE, UNIQUE (group_id, member_id), - UNIQUE (group_id, contact_id) + UNIQUE (group_id, contact_id), + UNIQUE (group_id, local_display_name) ); CREATE TABLE group_member_intros ( group_member_intro_id INTEGER PRIMARY KEY, - group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE, + re_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE, to_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE, - intro_status TEXT NOT NULL DEFAULT '', -- new, intro, inv, fwd, con - UNIQUE (group_member_id, to_group_member_id) + group_queue_info BLOB, + direct_queue_info BLOB, + intro_status TEXT NOT NULL, -- see GroupMemberIntroStatus + UNIQUE (re_group_member_id, to_group_member_id) ); CREATE TABLE connections ( -- all SMP agent connections diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 3cae435112..d5b4e3a2ea 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -23,7 +23,7 @@ import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.Functor (($>)) import Data.List (find) -import Data.Maybe (isJust, isNothing) +import Data.Maybe (isJust) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) @@ -77,7 +77,7 @@ cfg = connIdBytes = 12, tbqSize = 16, dbFile = undefined, -- filled in from options - dbPoolSize = 4, + dbPoolSize = 1, smpCfg = smpDefaultConfig } @@ -85,7 +85,7 @@ logCfg :: LogConfig logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} simplexChat :: WithTerminal t => ChatOpts -> t -> IO () -simplexChat opts t = do +simplexChat opts t = -- setLogLevel LogInfo -- LogError -- withGlobalLogging logCfg $ do initializeNotifications @@ -94,7 +94,7 @@ simplexChat opts t = do newChatController :: WithTerminal t => ChatOpts -> t -> (Notification -> IO ()) -> IO ChatController newChatController ChatOpts {dbFile, smpServers} t sendNotification = do - chatStore <- createStore (dbFile <> ".chat.db") 4 + chatStore <- createStore (dbFile <> ".chat.db") 1 currentUser <- getCreateActiveUser chatStore chatTerminal <- newChatTerminal t smpAgent <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} @@ -126,6 +126,7 @@ inputSubscriber = do Right cmd -> do case cmd of SendMessage c msg -> showSentMessage c msg + SendGroupMessage g msg -> showSentGroupMessage g msg _ -> printToView [plain s] user <- asks currentUser void . runExceptT $ processChatCommand user cmd `catchError` showChatError @@ -150,7 +151,7 @@ processChatCommand user@User {userId, profile} = \case showContactDeleted cName SendMessage cName msg -> do contact <- withStore $ \st -> getContact st userId cName - let msgEvent = XMsgNew MTText [] [MsgBodyContent {contentType = SimplexContentType XCText, contentData = msg}] + let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] sendDirectMessage (contactConnId contact) msgEvent setActive $ ActiveC cName NewGroup gProfile -> do @@ -162,29 +163,39 @@ processChatCommand user@User {userId, profile} = \case let Group {groupId, groupProfile, membership, members} = group userRole = memberRole membership userMemberId = memberId membership - when (userRole < GRAdmin || userRole < memRole) $ throwError $ ChatError CEGroupRole + when (userRole < GRAdmin || userRole < memRole) $ throwError $ ChatError CEGroupUserRole when (isMember contact members) . throwError . ChatError $ CEGroupDuplicateMember cName when (memberStatus membership == GSMemInvited) . throwError . ChatError $ CEGroupNotJoined gName - when (memberStatus membership < GSMemReady) . throwError . ChatError $ CEGroupMemberNotReady + unless (memberActive membership) . throwError . ChatError $ CEGroupMemberNotActive gVar <- asks idsDrg (agentConnId, qInfo) <- withAgent createConnection - GroupMember {memberId} <- withStore $ \st -> createGroupMember st gVar user groupId contact memRole agentConnId + GroupMember {memberId} <- withStore $ \st -> createContactGroupMember st gVar user groupId contact memRole agentConnId let msg = XGrpInv $ GroupInvitation (userMemberId, userRole) (memberId, memRole) qInfo groupProfile sendDirectMessage (contactConnId contact) msg showSentGroupInvitation group cName + setActive $ ActiveG gName JoinGroup gName -> do - ReceivedGroupInvitation {fromMember, invitedMember, queueInfo} <- withStore $ \st -> getGroupInvitation st user gName - agentConnId <- withAgent $ \a -> joinConnection a queueInfo . directMessage . XGrpAcpt $ memberId invitedMember - withStore $ \st -> createMemberConnection st userId (groupMemberId fromMember) agentConnId - MemberRole _gRef _cRef _mRole -> pure () - RemoveMember _gRef _cRef -> pure () - LeaveGroup _gRef -> pure () - DeleteGroup _gRef -> pure () - ListMembers _gRef -> pure () - SendGroupMessage _gRef _msg -> pure () + ReceivedGroupInvitation {fromMember, userMember, queueInfo} <- withStore $ \st -> getGroupInvitation st user gName + agentConnId <- withAgent $ \a -> joinConnection a queueInfo . directMessage . XGrpAcpt $ memberId userMember + withStore $ \st -> do + createMemberConnection st userId (groupMemberId fromMember) agentConnId + updateGroupMemberStatus st userId (groupMemberId fromMember) GSMemAccepted + updateGroupMemberStatus st userId (groupMemberId userMember) GSMemAccepted + MemberRole _gName _cName _mRole -> pure () + RemoveMember _gName _cName -> pure () + LeaveGroup _gName -> pure () + DeleteGroup _gName -> pure () + ListMembers _gName -> pure () + SendGroupMessage gName msg -> do + -- TODO save sent messages + -- TODO save pending message delivery for members without connections + Group {members} <- withStore $ \st -> getGroup st user gName + let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] + sendGroupMessage members msgEvent + setActive $ ActiveG gName where - isMember :: Contact -> [(GroupMember, Maybe Connection)] -> Bool - isMember Contact {contactId} members = isJust $ find ((== Just contactId) . memberContactId . fst) members + isMember :: Contact -> [GroupMember] -> Bool + isMember Contact {contactId} members = isJust $ find ((== Just contactId) . memberContactId) members agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do @@ -199,92 +210,230 @@ processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agen processAgentMessage user@User {userId, profile} agentConnId agentMessage = do chatDirection <- withStore $ \st -> getConnectionChatDirection st user agentConnId case chatDirection of - ReceivedDirectMessage (CContact ct@Contact {localDisplayName = c}) -> + ReceivedDMContact ct@Contact {localDisplayName = c, activeConn} -> case agentMessage of MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of - XMsgNew MTText [] body -> newTextMessage c meta $ find (isSimplexContentType XCText) body + XMsgNew (MsgContent MTText [] body) -> newTextMessage c meta $ find (isSimplexContentType XCText) body XInfo _ -> pure () -- TODO profile update - XGrpInv gInv -> saveGroupInvitation ct gInv + XGrpInv gInv -> processGroupInvitation ct gInv _ -> pure () - CON -> do - -- TODO update connection status - showContactConnected ct - showToast ("@" <> c) "connected" - setActive $ ActiveC c - END -> do - showContactDisconnected c - showToast ("@" <> c) "disconnected" - unsetActive $ ActiveC c - _ -> pure () - ReceivedDirectMessage (CConnection conn) -> - case agentMessage of - CONF confId connInfo -> do - -- TODO update connection status - saveConnInfo conn connInfo - withAgent $ \a -> allowConnection a agentConnId confId . directMessage $ XInfo profile - INFO connInfo -> - saveConnInfo conn connInfo - _ -> pure () - ReceivedGroupMessage gName m -> - case agentMessage of CONF confId connInfo -> do + -- confirming direct connection with a member + withStore $ \st -> updateConnectionStatus st activeConn ConnRequested ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of - XGrpAcpt memId - | memId == memberId m -> do - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemAccepted - withAgent $ \a -> allowConnection a agentConnId confId "" - | otherwise -> pure () -- TODO error not matching member ID - _ -> pure () -- TODO show/log error, other events in SMP confirmation + XGrpMemInfo _memId _memProfile -> do + -- TODO check member ID + -- TODO update member profile + acceptAgentConnection activeConn confId XOk + _ -> messageError "CONF from member must have x.grp.mem.info" + INFO connInfo -> do + withStore $ \st -> updateConnectionStatus st activeConn ConnSndReady + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case chatMsgEvent of + XGrpMemInfo _memId _memProfile -> do + -- TODO check member ID + -- TODO update member profile + pure () + XOk -> pure () + _ -> messageError "INFO from member must have x.grp.mem.info or x.ok" CON -> do - Group {membership, members} <- withStore $ \st -> getGroup st user gName - -- TODO because the contact is not created instantly, if the member is not created from contact, - -- it should still have a unique local display name. - -- If it is created from contact it can still be duplicated on the member (and match the contact) - case invitedBy m of - IBUser -> do - -- sender was invited by the current user - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected - sendGroupMessage members $ XGrpMemNew (memberId m) (memberRole m) (memberProfile m) - showConnectedGroupMember gName $ displayName (memberProfile m :: Profile) - forM_ (filter (\m' -> memberStatus m' >= GSMemConnected) . map fst $ members) $ \m' -> - sendDirectMessage agentConnId $ XGrpMemIntro (memberId m') (memberRole m') (memberProfile m') + withStore $ \st -> updateConnectionStatus st activeConn ConnReady + withStore (\st -> getViaGroupMember st user ct) >>= \case + Nothing -> do + showContactConnected ct + setActive $ ActiveC c + showToast (c <> "> ") "connected" + Just (gName, m) -> + when (memberIsReady m) $ notifyMemberConnected gName m + END -> do + showContactDisconnected c + showToast (c <> "> ") "disconnected" + unsetActive $ ActiveC c + _ -> messageError $ "unexpected agent event: " <> T.pack (show agentMessage) + ReceivedDMConnection conn -> + case agentMessage of + CONF confId connInfo -> do + withStore $ \st -> updateConnectionStatus st conn ConnRequested + saveConnInfo conn connInfo + acceptAgentConnection conn confId $ XInfo profile + INFO connInfo -> do + withStore $ \st -> updateConnectionStatus st conn ConnSndReady + saveConnInfo conn connInfo + CON -> + withStore $ \st -> updateConnectionStatus st conn ConnReady + _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMessage) + ReceivedGroupMessage conn gName m -> + case agentMessage of + CONF confId connInfo -> do + withStore $ \st -> updateConnectionStatus st conn ConnRequested + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case memberCategory m of + GCInviteeMember -> + case chatMsgEvent of + XGrpAcpt memId + | memId == memberId m -> do + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemAccepted + acceptAgentConnection conn confId XOk + | otherwise -> messageError "x.grp.acpt: memberId is different from expected" + _ -> messageError "CONF from invited member must have x.grp.acpt" + _ -> + case chatMsgEvent of + XGrpMemInfo memId _memProfile + | memId == memberId m -> do + -- TODO update member profile + Group {membership} <- withStore $ \st -> getGroup st user gName + acceptAgentConnection conn confId $ XGrpMemInfo (memberId membership) profile + | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" + _ -> messageError "CONF from member must have x.grp.mem.info" + INFO connInfo -> do + withStore $ \st -> updateConnectionStatus st conn ConnSndReady + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case chatMsgEvent of + XGrpMemInfo memId _memProfile + | memId == memberId m -> do + -- TODO update member profile + pure () + | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" + XOk -> pure () + _ -> messageError "INFO from member must have x.grp.mem.info" + pure () + CON -> do + group@Group {members, membership} <- withStore $ \st -> getGroup st user gName + withStore $ \st -> do + updateConnectionStatus st conn ConnReady + updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected + updateGroupMemberStatus st userId (groupMemberId membership) GSMemConnected + -- TODO forward any pending (GMIntroInvReceived) introductions + case memberCategory m of + GCHostMember -> do + showUserJoinedGroup gName + setActive $ ActiveG gName + showToast ("#" <> gName) "you are connected to group" + GCInviteeMember -> do + showJoinedGroupMember gName m + setActive $ ActiveG gName + showToast ("#" <> gName) $ "member " <> localDisplayName (m :: GroupMember) <> " is connected" + intros <- withStore $ \st -> createIntroductions st group m + sendGroupMessage members . XGrpMemNew $ memberInfo m + forM_ intros $ \intro -> do + sendDirectMessage agentConnId . XGrpMemIntro . memberInfo $ reMember intro + withStore $ \st -> updateIntroStatus st intro GMIntroSent _ -> do - if Just (invitedBy membership) == (IBContact <$> memberContactId m) - then do - -- sender invited the current user - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected - showUserConnectedToGroup gName - pure () - else do - showConnectedGroupMember gName $ displayName (memberProfile m :: Profile) + -- TODO send probe and decide whether to use existing contact connection or the new contact connection + -- TODO notify member who forwarded introduction - question - where it is stored? There is via_contact but probably there should be via_member in group_members table + withStore (\st -> getViaGroupContact st user m) >>= \case + Nothing -> do + notifyMemberConnected gName m + messageError "implementation error: connected member does not have contact" + Just ct -> + when (contactIsReady ct) $ notifyMemberConnected gName m MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of - XGrpMemNew memId memRole memProfile -> do - Group {membership, members} <- withStore $ \st -> getGroup st user gName - when (memberId membership /= memId && isNothing (find ((== memId) . memberId . fst) members)) $ - withStore $ \st -> pure () -- add new member as GSMemAccepted - _ -> pure () - _ -> pure () + XMsgNew (MsgContent MTText [] body) -> + newGroupTextMessage gName m meta $ find (isSimplexContentType XCText) body + XGrpMemNew memInfo@(MemberInfo memId _ _) -> do + group@Group {membership} <- withStore $ \st -> getGroup st user gName + when (memberId membership /= memId) $ + if isMember memId group + then messageError "x.grp.mem.new error: member already exists" + else do + newMember <- withStore $ \st -> createNewGroupMember st user group memInfo GCPostMember GSMemAnnounced + showJoinedGroupMemberConnecting gName m newMember + XGrpMemIntro memInfo@(MemberInfo memId _ _) -> + case memberCategory m of + GCHostMember -> do + group <- withStore $ \st -> getGroup st user gName + if isMember memId group + then messageWarning "x.grp.mem.intro ignored: member already exists" + else do + (groupConnId, groupQInfo) <- withAgent createConnection + (directConnId, directQInfo) <- withAgent createConnection + newMember <- withStore $ \st -> createIntroReMember st user group m memInfo groupConnId directConnId + let msg = XGrpMemInv memId IntroInvitation {groupQInfo, directQInfo} + sendDirectMessage agentConnId msg + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId newMember) GSMemIntroInvited + _ -> messageError "x.grp.mem.intro can be only sent by host member" + XGrpMemInv memId introInv -> + case memberCategory m of + GCInviteeMember -> do + group <- withStore $ \st -> getGroup st user gName + case find ((== memId) . memberId) $ members group of + Nothing -> messageError "x.grp.mem.inv error: referenced member does not exists" + Just reMember -> do + intro <- withStore $ \st -> saveIntroInvitation st reMember m introInv + case activeConn (reMember :: GroupMember) of + Nothing -> pure () -- this is not an error, introduction will be forwarded once the member is connected + Just Connection {agentConnId = reAgentConnId} -> do + sendDirectMessage reAgentConnId $ XGrpMemFwd (memberInfo m) introInv + withStore $ \st -> updateIntroStatus st intro GMIntroInvForwarded + _ -> messageError "x.grp.mem.inv can be only sent by invitee member" + XGrpMemFwd memInfo@(MemberInfo memId _ _) introInv@IntroInvitation {groupQInfo, directQInfo} -> do + group@Group {membership} <- withStore $ \st -> getGroup st user gName + toMember <- case find ((== memId) . memberId) $ members group of + -- TODO if the missed messages are correctly sent as soon as there is connection before anything else is sent + -- the situation when member does not exist is an error + -- member receiving x.grp.mem.fwd should have also received x.grp.mem.new prior to that. + -- For now, this branch compensates for the lack of delayed message delivery. + Nothing -> withStore $ \st -> createNewGroupMember st user group memInfo GCPostMember GSMemAnnounced + Just m' -> pure m' + withStore $ \st -> saveMemberInvitation st toMember introInv + let msg = XGrpMemInfo (memberId membership) profile + groupConnId <- withAgent $ \a -> joinConnection a groupQInfo $ directMessage msg + directConnId <- withAgent $ \a -> joinConnection a directQInfo $ directMessage msg + withStore $ \st -> createIntroToMemberContact st userId m toMember groupConnId directConnId + _ -> messageError $ "unsupported message: " <> T.pack (show chatMsgEvent) + _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMessage) where - newTextMessage :: ContactName -> MsgMeta -> Maybe MsgBodyContent -> m () + isMember :: MemberId -> Group -> Bool + isMember memId Group {membership, members} = + memberId membership == memId || isJust (find ((== memId) . memberId) members) + + contactIsReady :: Contact -> Bool + contactIsReady Contact {activeConn} = connStatus activeConn == ConnReady + + memberIsReady :: GroupMember -> Bool + memberIsReady GroupMember {activeConn} = maybe False ((== ConnReady) . connStatus) activeConn + + notifyMemberConnected :: GroupName -> GroupMember -> m () + notifyMemberConnected gName m@GroupMember {localDisplayName} = do + showConnectedToGroupMember gName m + setActive $ ActiveG gName + showToast ("#" <> gName) $ "member " <> localDisplayName <> " is connected" + + messageWarning :: Text -> m () + messageWarning = liftIO . print + + messageError :: Text -> m () + messageError = liftIO . print + + newTextMessage :: ContactName -> MsgMeta -> Maybe MsgContentBody -> m () newTextMessage c meta = \case - Just MsgBodyContent {contentData = bs} -> do + Just MsgContentBody {contentData = bs} -> do let text = safeDecodeUtf8 bs showReceivedMessage c (snd $ broker meta) text (integrity meta) - showToast ("@" <> c) text + showToast (c <> "> ") text setActive $ ActiveC c - _ -> pure () + _ -> messageError "x.msg.new: no expected message body" - saveGroupInvitation :: Contact -> GroupInvitation -> m () - saveGroupInvitation ct@Contact {localDisplayName} inv@(GroupInvitation (fromMemId, fromRole) (memId, memRole) _ _) = do - when (fromRole < GRAdmin || fromRole < memRole) $ throwError $ ChatError CEGroupRole + newGroupTextMessage :: GroupName -> GroupMember -> MsgMeta -> Maybe MsgContentBody -> m () + newGroupTextMessage gName GroupMember {localDisplayName = c} meta = \case + Just MsgContentBody {contentData = bs} -> do + let text = safeDecodeUtf8 bs + showReceivedGroupMessage gName c (snd $ broker meta) text (integrity meta) + showToast ("#" <> gName <> " " <> c <> "> ") text + setActive $ ActiveG gName + _ -> messageError "x.msg.new: no expected message body" + + processGroupInvitation :: Contact -> GroupInvitation -> m () + processGroupInvitation ct@Contact {localDisplayName} inv@(GroupInvitation (fromMemId, fromRole) (memId, memRole) _ _) = do + when (fromRole < GRAdmin || fromRole < memRole) . throwError . ChatError $ CEGroupContactRole localDisplayName when (fromMemId == memId) $ throwError $ ChatError CEGroupDuplicateMemberId group <- withStore $ \st -> createGroupInvitation st user ct inv - showReceivedGroupInvitation group localDisplayName + showReceivedGroupInvitation group localDisplayName memRole parseChatMessage :: ByteString -> Either ChatError ChatMessage parseChatMessage msgBody = first ChatErrorMessage (parseAll rawChatMessageP msgBody >>= toChatMessage) @@ -306,8 +455,18 @@ directMessage chatMsgEvent = serializeRawChatMessage $ rawChatMessage ChatMessage {chatMsgId = Nothing, chatMsgEvent, chatDAG = Nothing} -sendGroupMessage :: ChatMonad m => [(GroupMember, Maybe Connection)] -> ChatMsgEvent -> m () -sendGroupMessage _members _chatMsgEvent = pure () +sendGroupMessage :: ChatMonad m => [GroupMember] -> ChatMsgEvent -> m () +sendGroupMessage members chatMsgEvent = do + let msg = directMessage chatMsgEvent + -- TODO once scheduled delivery is implemented memberActive should be changed to memberCurrent + withAgent $ \a -> + forM_ (filter memberActive members) $ + traverse (\connId -> sendMessage a connId msg) . memberConnId + +acceptAgentConnection :: ChatMonad m => Connection -> ConfirmationId -> ChatMsgEvent -> m () +acceptAgentConnection conn@Connection {agentConnId} confId msg = do + withAgent $ \a -> allowConnection a agentConnId confId $ directMessage msg + withStore $ \st -> updateConnectionStatus st conn ConnAccepted getCreateActiveUser :: SQLiteStore -> IO User getCreateActiveUser st = do @@ -393,9 +552,9 @@ withStore action = chatCommandP :: Parser ChatCommand chatCommandP = ("/help" <|> "/h") $> ChatHelp - <|> ("/group #" <|> "/g #") *> (NewGroup <$> groupProfile) - <|> ("/add #" <|> "/a #") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) - <|> ("/join #" <|> "/j #") *> (JoinGroup <$> displayName) + <|> ("/group #" <|> "/group " <|> "/g #" <|> "/g ") *> (NewGroup <$> groupProfile) + <|> ("/add #" <|> "/add " <|> "/a #" <|> "/a ") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) + <|> ("/join #" <|> "/join " <|> "/j #" <|> "/j ") *> (JoinGroup <$> displayName) <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> displayName <* A.space <*> displayName) <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> displayName) <|> ("/members #" <|> "/ms #") *> (ListMembers <$> displayName) @@ -416,4 +575,4 @@ chatCommandP = (" owner" $> GROwner) <|> (" admin" $> GRAdmin) <|> (" normal" $> GRMember) - <|> pure GRMember + <|> pure GRAdmin diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 4517a1b73f..813b3cf8eb 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -41,11 +41,12 @@ data ChatError deriving (Show, Exception) data ChatErrorType - = CEGroupRole + = CEGroupUserRole + | CEGroupContactRole ContactName | CEGroupDuplicateMember ContactName | CEGroupDuplicateMemberId | CEGroupNotJoined GroupName - | CEGroupMemberNotReady + | CEGroupMemberNotActive | CEGroupInternal String deriving (Show, Exception) diff --git a/src/Simplex/Chat/Input.hs b/src/Simplex/Chat/Input.hs index 7623c40d47..5369c3db9a 100644 --- a/src/Simplex/Chat/Input.hs +++ b/src/Simplex/Chat/Input.hs @@ -81,7 +81,7 @@ updateTermState ac tw (key, ms) ts@TerminalState {inputString = s, inputPosition contactPrefix = case ac of ActiveNone -> "" ActiveC c -> "@" <> T.unpack c <> " " - -- ActiveG (Group g) -> "#" <> B.unpack g <> " " + ActiveG g -> "#" <> T.unpack g <> " " backDeleteChar | p == 0 || null s = ts | p >= length s = ts' (init s, length s - 1) diff --git a/src/Simplex/Chat/Notification.hs b/src/Simplex/Chat/Notification.hs index 66d99137ba..bd80041dff 100644 --- a/src/Simplex/Chat/Notification.hs +++ b/src/Simplex/Chat/Notification.hs @@ -5,7 +5,6 @@ module Simplex.Chat.Notification (Notification (..), initializeNotifications) where import Control.Monad (void) -import Data.Char (toLower) import Data.List (isInfixOf) import Data.Text (Text) import qualified Data.Text as T diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 1fe10b4939..401120a6d0 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -29,33 +29,43 @@ import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow) data ChatDirection (p :: AParty) where - ReceivedDirectMessage :: ConnContact -> ChatDirection 'Agent + ReceivedDMConnection :: Connection -> ChatDirection 'Agent + ReceivedDMContact :: Contact -> ChatDirection 'Agent SentDirectMessage :: Contact -> ChatDirection 'Client - ReceivedGroupMessage :: GroupName -> GroupMember -> ChatDirection 'Agent + ReceivedGroupMessage :: Connection -> GroupName -> GroupMember -> ChatDirection 'Agent SentGroupMessage :: GroupName -> ChatDirection 'Client deriving instance Eq (ChatDirection p) deriving instance Show (ChatDirection p) -data ConnContact = CContact Contact | CConnection Connection - deriving (Eq, Show) - data ChatMsgEvent - = XMsgNew - { messageType :: MessageType, - files :: [(ContentType, Int)], - content :: [MsgBodyContent] - } + = XMsgNew MsgContent | XInfo Profile | XGrpInv GroupInvitation | XGrpAcpt MemberId - | XGrpMemNew MemberId GroupMemberRole Profile - | XGrpMemIntro MemberId GroupMemberRole Profile + | XGrpMemNew MemberInfo + | XGrpMemIntro MemberInfo + | XGrpMemInv MemberId IntroInvitation + | XGrpMemFwd MemberInfo IntroInvitation + | XGrpMemInfo MemberId Profile + | XGrpMemCon MemberId + | XGrpMemConAll MemberId + | XInfoProbe ByteString + | XInfoProbeCheck MemberId ByteString + | XInfoProbeOk MemberId ByteString + | XOk deriving (Eq, Show) data MessageType = MTText | MTImage deriving (Eq, Show) +data MsgContent = MsgContent + { messageType :: MessageType, + files :: [(ContentType, Int)], + content :: [MsgContentBody] + } + deriving (Eq, Show) + toMsgType :: ByteString -> Either String MessageType toMsgType = \case "c.text" -> Right MTText @@ -77,118 +87,148 @@ data ChatMessage = ChatMessage toChatMessage :: RawChatMessage -> Either String ChatMessage toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBody} = do (chatDAG, body) <- getDAG <$> mapM toMsgBodyContent chatMsgBody - case chatMsgEvent of - "x.msg.new" -> case chatMsgParams of - mt : rawFiles -> do - t <- toMsgType mt - files <- mapM (toContentInfo <=< parseAll contentInfoP) rawFiles - let msg = XMsgNew {messageType = t, files, content = body} - pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - [] -> Left "x.msg.new expects at least one parameter" - "x.info" -> case chatMsgParams of - [] -> do - profile <- getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = XInfo profile, chatDAG} - _ -> Left "x.info expects no parameters" - "x.grp.inv" -> case chatMsgParams of - [fromMemId, fromRole, memId, role, qInfo] -> do - fromMember <- (,) <$> B64.decode fromMemId <*> toMemberRole fromRole - invitedMember <- (,) <$> B64.decode memId <*> toMemberRole role - inv <- GroupInvitation fromMember invitedMember <$> parseAll smpQueueInfoP qInfo <*> getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = XGrpInv inv, chatDAG} - _ -> Left "x.grp.inv expects 5 parameters" - "x.grp.acpt" -> case chatMsgParams of - [memId] -> do - msg <- XGrpAcpt <$> B64.decode memId - pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - _ -> Left "x.grp.acpt expects one parameter" - "x.grp.mem.new" -> memberMessage chatMsgParams XGrpMemNew body chatDAG - "x.grp.mem.intro" -> memberMessage chatMsgParams XGrpMemIntro body chatDAG - _ -> Left $ "unsupported event " <> B.unpack chatMsgEvent + let chatMsg msg = pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} + case (chatMsgEvent, chatMsgParams) of + ("x.msg.new", mt : rawFiles) -> do + t <- toMsgType mt + files <- mapM (toContentInfo <=< parseAll contentInfoP) rawFiles + chatMsg . XMsgNew $ MsgContent {messageType = t, files, content = body} + ("x.info", []) -> do + profile <- getJSON body + chatMsg $ XInfo profile + ("x.grp.inv", [fromMemId, fromRole, memId, role, qInfo]) -> do + fromMem <- (,) <$> B64.decode fromMemId <*> toMemberRole fromRole + invitedMem <- (,) <$> B64.decode memId <*> toMemberRole role + groupQInfo <- parseAll smpQueueInfoP qInfo + profile <- getJSON body + chatMsg . XGrpInv $ GroupInvitation fromMem invitedMem groupQInfo profile + ("x.grp.acpt", [memId]) -> + chatMsg . XGrpAcpt =<< B64.decode memId + ("x.grp.mem.new", [memId, role]) -> do + chatMsg . XGrpMemNew =<< toMemberInfo memId role body + ("x.grp.mem.intro", [memId, role]) -> + chatMsg . XGrpMemIntro =<< toMemberInfo memId role body + ("x.grp.mem.inv", [memId, groupQInfo, directQInfo]) -> + chatMsg =<< (XGrpMemInv <$> B64.decode memId <*> toIntroInv groupQInfo directQInfo) + ("x.grp.mem.fwd", [memId, role, groupQInfo, directQInfo]) -> do + chatMsg =<< (XGrpMemFwd <$> toMemberInfo memId role body <*> toIntroInv groupQInfo directQInfo) + ("x.grp.mem.info", [memId]) -> + chatMsg =<< (XGrpMemInfo <$> B64.decode memId <*> getJSON body) + ("x.grp.mem.con", [memId]) -> + chatMsg . XGrpMemCon =<< B64.decode memId + ("x.grp.mem.con.all", [memId]) -> + chatMsg . XGrpMemConAll =<< B64.decode memId + ("x.info.probe", [probe]) -> do + chatMsg . XInfoProbe =<< B64.decode probe + ("x.info.probe.check", [memId, probeHash]) -> do + chatMsg =<< (XInfoProbeCheck <$> B64.decode memId <*> B64.decode probeHash) + ("x.info.probe.ok", [memId, probe]) -> do + chatMsg =<< (XInfoProbeOk <$> B64.decode memId <*> B64.decode probe) + ("x.ok", []) -> + chatMsg XOk + _ -> Left $ "bad syntax or unsupported event " <> B.unpack chatMsgEvent where - getDAG :: [MsgBodyContent] -> (Maybe ByteString, [MsgBodyContent]) + getDAG :: [MsgContentBody] -> (Maybe ByteString, [MsgContentBody]) getDAG body = case break (isContentType SimplexDAG) body of - (b, MsgBodyContent SimplexDAG dag : a) -> (Just dag, b <> a) + (b, MsgContentBody SimplexDAG dag : a) -> (Just dag, b <> a) _ -> (Nothing, body) - memberMessage :: - FromJSON a => [ByteString] -> (MemberId -> GroupMemberRole -> a -> ChatMsgEvent) -> [MsgBodyContent] -> Maybe ByteString -> Either String ChatMessage - memberMessage [memId, role] mkMsg body chatDAG = do - msg <- mkMsg <$> B64.decode memId <*> toMemberRole role <*> getJSON body - pure ChatMessage {chatMsgId, chatMsgEvent = msg, chatDAG} - memberMessage _ _ _ _ = Left "message expects 2 parameters" + toMemberInfo :: ByteString -> ByteString -> [MsgContentBody] -> Either String MemberInfo + toMemberInfo memId role body = MemberInfo <$> B64.decode memId <*> toMemberRole role <*> getJSON body + toIntroInv :: ByteString -> ByteString -> Either String IntroInvitation + toIntroInv groupQInfo directQInfo = IntroInvitation <$> parseAll smpQueueInfoP groupQInfo <*> parseAll smpQueueInfoP directQInfo toContentInfo :: (RawContentType, Int) -> Either String (ContentType, Int) toContentInfo (rawType, size) = (,size) <$> toContentType rawType - getJSON :: FromJSON a => [MsgBodyContent] -> Either String a + getJSON :: FromJSON a => [MsgContentBody] -> Either String a getJSON = J.eitherDecodeStrict' <=< getSimplexContentType XCJson -isContentType :: ContentType -> MsgBodyContent -> Bool -isContentType t MsgBodyContent {contentType = t'} = t == t' +isContentType :: ContentType -> MsgContentBody -> Bool +isContentType t MsgContentBody {contentType = t'} = t == t' -isSimplexContentType :: XContentType -> MsgBodyContent -> Bool +isSimplexContentType :: XContentType -> MsgContentBody -> Bool isSimplexContentType = isContentType . SimplexContentType -getContentType :: ContentType -> [MsgBodyContent] -> Either String ByteString +getContentType :: ContentType -> [MsgContentBody] -> Either String ByteString getContentType t body = case find (isContentType t) body of - Just MsgBodyContent {contentData} -> Right contentData + Just MsgContentBody {contentData} -> Right contentData Nothing -> Left "no required content type" -getSimplexContentType :: XContentType -> [MsgBodyContent] -> Either String ByteString +getSimplexContentType :: XContentType -> [MsgContentBody] -> Either String ByteString getSimplexContentType = getContentType . SimplexContentType rawChatMessage :: ChatMessage -> RawChatMessage rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = case chatMsgEvent of - XMsgNew {messageType = t, files, content} -> + XMsgNew MsgContent {messageType = t, files, content} -> let rawFiles = map (serializeContentInfo . rawContentInfo) files - chatMsgParams = rawMsgType t : rawFiles - chatMsgBody = rawWithDAG content - in RawChatMessage {chatMsgId, chatMsgEvent = "x.msg.new", chatMsgParams, chatMsgBody} + in rawMsg "x.msg.new" (rawMsgType t : rawFiles) content XInfo profile -> - let chatMsgBody = rawWithDAG [jsonBody profile] - in RawChatMessage {chatMsgId, chatMsgEvent = "x.info", chatMsgParams = [], chatMsgBody} + rawMsg "x.info" [] [jsonBody profile] XGrpInv (GroupInvitation (fromMemId, fromRole) (memId, role) qInfo groupProfile) -> - let chatMsgParams = + let params = [ B64.encode fromMemId, serializeMemberRole fromRole, B64.encode memId, serializeMemberRole role, serializeSmpQueueInfo qInfo ] - chatMsgBody = rawWithDAG [jsonBody groupProfile] - in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.inv", chatMsgParams, chatMsgBody} + in rawMsg "x.grp.inv" params [jsonBody groupProfile] XGrpAcpt memId -> - let chatMsgParams = [B64.encode memId] - in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.acpt", chatMsgParams, chatMsgBody = []} - XGrpMemNew memId role profile -> - let chatMsgParams = [B64.encode memId, serializeMemberRole role] - chatMsgBody = rawWithDAG [jsonBody profile] - in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.mem.new", chatMsgParams, chatMsgBody} - XGrpMemIntro memId role profile -> - let chatMsgParams = [B64.encode memId, serializeMemberRole role] - chatMsgBody = rawWithDAG [jsonBody profile] - in RawChatMessage {chatMsgId, chatMsgEvent = "x.grp.mem.intro", chatMsgParams, chatMsgBody} + rawMsg "x.grp.acpt" [B64.encode memId] [] + XGrpMemNew (MemberInfo memId role profile) -> + let params = [B64.encode memId, serializeMemberRole role] + in rawMsg "x.grp.mem.new" params [jsonBody profile] + XGrpMemIntro (MemberInfo memId role profile) -> + rawMsg "x.grp.mem.intro" [B64.encode memId, serializeMemberRole role] [jsonBody profile] + XGrpMemInv memId IntroInvitation {groupQInfo, directQInfo} -> + let params = [B64.encode memId, serializeSmpQueueInfo groupQInfo, serializeSmpQueueInfo directQInfo] + in rawMsg "x.grp.mem.inv" params [] + XGrpMemFwd (MemberInfo memId role profile) IntroInvitation {groupQInfo, directQInfo} -> + let params = + [ B64.encode memId, + serializeMemberRole role, + serializeSmpQueueInfo groupQInfo, + serializeSmpQueueInfo directQInfo + ] + in rawMsg "x.grp.mem.fwd" params [jsonBody profile] + XGrpMemInfo memId profile -> + rawMsg "x.grp.mem.info" [B64.encode memId] [jsonBody profile] + XGrpMemCon memId -> + rawMsg "x.grp.mem.con" [B64.encode memId] [] + XGrpMemConAll memId -> + rawMsg "x.grp.mem.con.all" [B64.encode memId] [] + XInfoProbe probe -> + rawMsg "x.info.probe" [B64.encode probe] [] + XInfoProbeCheck memId probeHash -> + rawMsg "x.info.probe.check" [B64.encode memId, B64.encode probeHash] [] + XInfoProbeOk memId probe -> + rawMsg "x.info.probe.ok" [B64.encode memId, B64.encode probe] [] + XOk -> + rawMsg "x.ok" [] [] where + rawMsg :: ByteString -> [ByteString] -> [MsgContentBody] -> RawChatMessage + rawMsg event chatMsgParams body = + RawChatMessage {chatMsgId, chatMsgEvent = event, chatMsgParams, chatMsgBody = rawWithDAG body} rawContentInfo :: (ContentType, Int) -> (RawContentType, Int) rawContentInfo (t, size) = (rawContentType t, size) - jsonBody :: ToJSON a => a -> MsgBodyContent + jsonBody :: ToJSON a => a -> MsgContentBody jsonBody x = let json = LB.toStrict $ J.encode x - in MsgBodyContent {contentType = SimplexContentType XCJson, contentData = json} - rawWithDAG :: [MsgBodyContent] -> [RawMsgBodyContent] + in MsgContentBody {contentType = SimplexContentType XCJson, contentData = json} + rawWithDAG :: [MsgContentBody] -> [RawMsgBodyContent] rawWithDAG body = map rawMsgBodyContent $ case chatDAG of Nothing -> body - Just dag -> MsgBodyContent {contentType = SimplexDAG, contentData = dag} : body + Just dag -> MsgContentBody {contentType = SimplexDAG, contentData = dag} : body -toMsgBodyContent :: RawMsgBodyContent -> Either String MsgBodyContent +toMsgBodyContent :: RawMsgBodyContent -> Either String MsgContentBody toMsgBodyContent RawMsgBodyContent {contentType, contentData} = do cType <- toContentType contentType - pure MsgBodyContent {contentType = cType, contentData} + pure MsgContentBody {contentType = cType, contentData} -rawMsgBodyContent :: MsgBodyContent -> RawMsgBodyContent -rawMsgBodyContent MsgBodyContent {contentType = t, contentData} = +rawMsgBodyContent :: MsgContentBody -> RawMsgBodyContent +rawMsgBodyContent MsgContentBody {contentType = t, contentData} = RawMsgBodyContent {contentType = rawContentType t, contentData} -data MsgBodyContent = MsgBodyContent +data MsgContentBody = MsgContentBody { contentType :: ContentType, contentData :: ByteString } diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index d902cd42c1..d8b183b1ef 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -25,13 +25,23 @@ module Simplex.Chat.Store getContact, getContactConnections, getConnectionChatDirection, + updateConnectionStatus, createNewGroup, createGroupInvitation, getGroup, getGroupInvitation, - createGroupMember, + createContactGroupMember, createMemberConnection, updateGroupMemberStatus, + createNewGroupMember, + createIntroductions, + updateIntroStatus, + saveIntroInvitation, + createIntroReMember, + createIntroToMemberContact, + saveMemberInvitation, + getViaGroupMember, + getViaGroupContact, ) where @@ -51,7 +61,7 @@ import Data.Maybe (listToMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (decodeUtf8) -import Data.Time.Clock (UTCTime) +import Data.Time.Clock (UTCTime, getCurrentTime) import Database.SQLite.Simple (NamedParam (..), Only (..), SQLError, (:.) (..)) import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) @@ -129,23 +139,36 @@ setActiveUser st userId = do createDirectConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> m () createDirectConnection st userId agentConnId = liftIO . withTransaction st $ \db -> - DB.execute - db - [sql| - INSERT INTO connections - (user_id, agent_conn_id, conn_status, conn_type) VALUES (?,?,?,?); - |] - (userId, agentConnId, ConnNew, ConnContact) + void $ createConnection_ db userId agentConnId Nothing 0 + +createConnection_ :: DB.Connection -> UserId -> ConnId -> Maybe Int64 -> Int -> IO Connection +createConnection_ db userId agentConnId viaContact connLevel = do + createdAt <- getCurrentTime + DB.execute + db + [sql| + INSERT INTO connections + (user_id, agent_conn_id, conn_status, conn_type, via_contact, conn_level, created_at) VALUES (?,?,?,?,?,?,?); + |] + (userId, agentConnId, ConnNew, ConnContact, viaContact, connLevel, createdAt) + connId <- insertedRowId db + pure Connection {connId, agentConnId, connType = ConnContact, entityId = Nothing, viaContact, connLevel, connStatus = ConnNew, createdAt} createDirectContact :: StoreMonad m => SQLiteStore -> UserId -> Connection -> Profile -> m () -createDirectContact st userId Connection {connId} Profile {displayName, fullName} = - liftIOEither . withTransaction st $ \db -> - withLocalDisplayName db userId displayName $ \localDisplayName' -> do - DB.execute db "INSERT INTO contact_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) - profileId <- insertedRowId db - DB.execute db "INSERT INTO contacts (contact_profile_id, local_display_name, user_id) VALUES (?, ?, ?)" (profileId, localDisplayName', userId) - contactId <- insertedRowId db - DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) +createDirectContact st userId Connection {connId} profile = + void $ + liftIOEither . withTransaction st $ \db -> + createContact_ db userId connId profile Nothing + +createContact_ :: DB.Connection -> UserId -> Int64 -> Profile -> Maybe Int64 -> IO (Either StoreError (Text, Int64, Int64)) +createContact_ db userId connId Profile {displayName, fullName} viaGroup = + withLocalDisplayName db userId displayName $ \ldn -> do + DB.execute db "INSERT INTO contact_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) + profileId <- insertedRowId db + DB.execute db "INSERT INTO contacts (contact_profile_id, local_display_name, user_id, via_group) VALUES (?,?,?,?)" (profileId, ldn, userId, viaGroup) + contactId <- insertedRowId db + DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) + pure (ldn, contactId, profileId) deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactName -> m () deleteContact st userId displayName = @@ -178,25 +201,27 @@ deleteContact st userId displayName = -- TODO return the last connection that is ready, not any last connection -- requires updating connection status -getContact :: - StoreMonad m => SQLiteStore -> UserId -> ContactName -> m Contact +getContact :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m Contact +-- TODO merge contact and connection? getContact st userId localDisplayName = liftIOEither . withTransaction st $ \db -> runExceptT $ do c@Contact {contactId} <- getContact_ db activeConn <- getConnection_ db contactId pure $ (c :: Contact) {activeConn} where + getContact_ :: DB.Connection -> ExceptT StoreError IO Contact getContact_ db = ExceptT $ do toContact <$> DB.queryNamed db [sql| - SELECT c.contact_id, p.display_name, p.full_name + SELECT c.contact_id, p.display_name, p.full_name, c.via_group FROM contacts c JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id WHERE c.user_id = :user_id AND c.local_display_name = :local_display_name AND c.is_user = :is_user |] [":user_id" := userId, ":local_display_name" := localDisplayName, ":is_user" := False] + getConnection_ :: DB.Connection -> Int64 -> ExceptT StoreError IO Connection getConnection_ db contactId = ExceptT $ do connection <$> DB.queryNamed @@ -210,10 +235,12 @@ getContact st userId localDisplayName = LIMIT 1 |] [":user_id" := userId, ":contact_id" := contactId] - toContact [(contactId, displayName, fullName)] = + toContact :: [(Int64, Text, Text, Maybe Int64)] -> Either StoreError Contact + toContact [(contactId, displayName, fullName, viaGroup)] = let profile = Profile {displayName, fullName} - in Right Contact {contactId, localDisplayName, profile, activeConn = undefined} + in Right Contact {contactId, localDisplayName, profile, activeConn = undefined, viaGroup} toContact _ = Left $ SEContactNotFound localDisplayName + connection :: [ConnectionRow] -> Either StoreError Connection connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEContactNotReady localDisplayName @@ -263,11 +290,11 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = ConnMember -> case entityId of Nothing -> throwError $ SEInternal "group member without connection" - Just groupMemberId -> uncurry ReceivedGroupMessage <$> getGroupAndMember_ db groupMemberId + Just groupMemberId -> uncurry (ReceivedGroupMessage c) <$> getGroupAndMember_ db groupMemberId c ConnContact -> - ReceivedDirectMessage <$> case entityId of - Nothing -> pure $ CConnection c - Just contactId -> getContact_ db contactId c + case entityId of + Nothing -> pure $ ReceivedDMConnection c + Just contactId -> ReceivedDMContact <$> getContact_ db contactId c where getConnection_ :: DB.Connection -> ExceptT StoreError IO Connection getConnection_ db = ExceptT $ do @@ -284,42 +311,49 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = connection :: [ConnectionRow] -> Either StoreError Connection connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEConnectionNotFound agentConnId - getContact_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO ConnContact + getContact_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO Contact getContact_ db contactId c = ExceptT $ do toContact contactId c <$> DB.query db [sql| - SELECT c.local_display_name, p.display_name, p.full_name + SELECT c.local_display_name, p.display_name, p.full_name, c.via_group FROM contacts c JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id WHERE c.user_id = ? AND c.contact_id = ? |] (userId, contactId) - toContact :: Int64 -> Connection -> [(ContactName, Text, Text)] -> Either StoreError ConnContact - toContact contactId c [(localDisplayName, displayName, fullName)] = + toContact :: Int64 -> Connection -> [(ContactName, Text, Text, Maybe Int64)] -> Either StoreError Contact + toContact contactId activeConn [(localDisplayName, displayName, fullName, viaGroup)] = let profile = Profile {displayName, fullName} - in Right $ CContact Contact {contactId, localDisplayName, profile, activeConn = c} + in Right $ Contact {contactId, localDisplayName, profile, activeConn, viaGroup} toContact _ _ _ = Left $ SEInternal "referenced contact not found" - getGroupAndMember_ :: DB.Connection -> Int64 -> ExceptT StoreError IO (GroupName, GroupMember) - getGroupAndMember_ db groupMemberId = ExceptT $ do - toGroupAndMember + getGroupAndMember_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO (GroupName, GroupMember) + getGroupAndMember_ db groupMemberId c = ExceptT $ do + toGroupAndMember c <$> DB.query db [sql| SELECT g.local_display_name, - m.group_member_id, m.member_id, m.member_role, m.member_status, - m.invited_by, m.contact_id, p.display_name, p.full_name + m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, + m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id JOIN groups g ON g.group_id = m.group_id WHERE m.group_member_id = ? |] (Only groupMemberId) - toGroupAndMember :: [Only GroupName :. GroupMemberRow] -> Either StoreError (GroupName, GroupMember) - toGroupAndMember [Only groupName :. memberRow] = Right (groupName, toGroupMember userContactId memberRow) - toGroupAndMember _ = Left $ SEInternal "referenced group member not found" + toGroupAndMember :: Connection -> [Only GroupName :. GroupMemberRow] -> Either StoreError (GroupName, GroupMember) + toGroupAndMember c [Only groupName :. memberRow] = + let member = toGroupMember userContactId memberRow + in Right (groupName, (member :: GroupMember) {activeConn = Just c}) + toGroupAndMember _ _ = Left $ SEInternal "referenced group member not found" + +updateConnectionStatus :: MonadUnliftIO m => SQLiteStore -> Connection -> ConnStatus -> m () +updateConnectionStatus st Connection {connId} connStatus = + liftIO . withTransaction st $ \db -> + DB.execute db "UPDATE connections SET conn_status = ? WHERE connection_id = ?" (connStatus, connId) -- | creates completely new group with a single member - the current user createNewGroup :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> User -> GroupProfile -> m Group @@ -333,7 +367,7 @@ createNewGroup st gVar user groupProfile = DB.execute db "INSERT INTO groups (local_display_name, user_id, group_profile_id) VALUES (?, ?, ?)" (displayName, uId, profileId) groupId <- insertedRowId db memberId <- randomId gVar 12 - membership <- createContactMember_ db user groupId user (memberId, GROwner) GSMemFull IBUser + membership <- createContactMember_ db user groupId user (memberId, GROwner) GCUserMember GSMemCreator IBUser pure $ Right Group {groupId, localDisplayName = displayName, groupProfile, members = [], membership} -- | creates a new group record for the group the current user was invited to @@ -348,9 +382,9 @@ createGroupInvitation st user contact GroupInvitation {fromMember, invitedMember profileId <- insertedRowId db DB.execute db "INSERT INTO groups (group_profile_id, local_display_name, inv_queue_info, user_id) VALUES (?, ?, ?, ?)" (profileId, localDisplayName, queueInfo, uId) groupId <- insertedRowId db - member <- createContactMember_ db user groupId contact fromMember GSMemFull IBUnknown - membership <- createContactMember_ db user groupId user invitedMember GSMemInvited (IBContact $ contactId contact) - pure Group {groupId, localDisplayName, groupProfile, members = [(member, Nothing)], membership} + member <- createContactMember_ db user groupId contact fromMember GCHostMember GSMemInvited IBUnknown + membership <- createContactMember_ db user groupId user invitedMember GCUserMember GSMemInvited (IBContact $ contactId contact) + pure Group {groupId, localDisplayName, groupProfile, members = [member], membership} -- TODO return the last connection that is ready, not any last connection -- requires updating connection status @@ -382,15 +416,15 @@ getGroup_ db User {userId, userContactId} localDisplayName = do let groupProfile = GroupProfile {displayName, fullName} in Right (Group {groupId, localDisplayName, groupProfile, members = undefined, membership = undefined}, qInfo) toGroup _ = Left $ SEGroupNotFound localDisplayName - getMembers_ :: Int64 -> ExceptT StoreError IO [(GroupMember, Maybe Connection)] + getMembers_ :: Int64 -> ExceptT StoreError IO [GroupMember] getMembers_ groupId = ExceptT $ do Right . map toContactMember <$> DB.query db [sql| SELECT - m.group_member_id, m.member_id, m.member_role, m.member_status, - m.invited_by, m.contact_id, p.display_name, p.full_name, + m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, + m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at FROM group_members m @@ -398,19 +432,20 @@ getGroup_ db User {userId, userContactId} localDisplayName = do LEFT JOIN connections c ON c.connection_id = ( SELECT max(cc.connection_id) FROM connections cc - where cc.group_member_id = c.group_member_id + where cc.group_member_id = m.group_member_id ) - WHERE m.group_id = ? + WHERE m.group_id = ? AND m.user_id = ? |] - (Only groupId) - toContactMember :: (GroupMemberRow :. MaybeConnectionRow) -> (GroupMember, Maybe Connection) - toContactMember (memberRow :. connRow) = (toGroupMember userContactId memberRow, toMaybeConnection connRow) - splitUserMember_ :: [(GroupMember, Maybe Connection)] -> Either StoreError ([(GroupMember, Maybe Connection)], GroupMember) + (groupId, userId) + toContactMember :: (GroupMemberRow :. MaybeConnectionRow) -> GroupMember + toContactMember (memberRow :. connRow) = + (toGroupMember userContactId memberRow) {activeConn = toMaybeConnection connRow} + splitUserMember_ :: [GroupMember] -> Either StoreError ([GroupMember], GroupMember) splitUserMember_ allMembers = - let (b, a) = break ((== Just userContactId) . memberContactId . fst) allMembers + let (b, a) = break ((== Just userContactId) . memberContactId) allMembers in case a of [] -> Left SEGroupWithoutUser - u : ms -> Right (b <> ms, fst u) + u : ms -> Right (b <> ms, u) getGroupInvitation :: StoreMonad m => SQLiteStore -> User -> GroupName -> m ReceivedGroupInvitation getGroupInvitation st user localDisplayName = @@ -418,76 +453,364 @@ getGroupInvitation st user localDisplayName = (Group {membership, members, groupProfile}, qInfo) <- getGroup_ db user localDisplayName when (memberStatus membership /= GSMemInvited) $ throwError SEGroupAlreadyJoined case (qInfo, findFromContact (invitedBy membership) members) of - (Just queueInfo, Just (fromMember, Nothing)) -> - pure ReceivedGroupInvitation {fromMember, invitedMember = membership, queueInfo, groupProfile} - _ -> throwError SENoGroupInvitation + (Just queueInfo, Just fromMember) -> + pure ReceivedGroupInvitation {fromMember, userMember = membership, queueInfo, groupProfile} + _ -> throwError SEGroupInvitationNotFound where - findFromContact :: InvitedBy -> [(GroupMember, Maybe Connection)] -> Maybe (GroupMember, Maybe Connection) - findFromContact (IBContact contactId) = find (\(m, _) -> memberContactId m == Just contactId) + findFromContact :: InvitedBy -> [GroupMember] -> Maybe GroupMember + findFromContact (IBContact contactId) = find ((== Just contactId) . memberContactId) findFromContact _ = const Nothing -type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberStatus, Maybe Int64, Maybe Int64, ContactName, Text) +type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, Maybe Int64, ContactName, Maybe Int64, ContactName, Text) toGroupMember :: Int64 -> GroupMemberRow -> GroupMember -toGroupMember userContactId (groupMemberId, memberId, memberRole, memberStatus, invitedById, memberContactId, displayName, fullName) = +toGroupMember userContactId (groupMemberId, memberId, memberRole, memberCategory, memberStatus, invitedById, localDisplayName, memberContactId, displayName, fullName) = let memberProfile = Profile {displayName, fullName} invitedBy = toInvitedBy userContactId invitedById - in GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} + in GroupMember {groupMemberId, memberId, memberRole, memberCategory, memberStatus, invitedBy, localDisplayName, memberProfile, memberContactId, activeConn = Nothing} -createGroupMember :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Contact -> GroupMemberRole -> ConnId -> m GroupMember -createGroupMember st gVar user groupId contact memberRole agentConnId = +createContactGroupMember :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Contact -> GroupMemberRole -> ConnId -> m GroupMember +createContactGroupMember st gVar user groupId contact memberRole agentConnId = liftIOEither . withTransaction st $ \db -> createWithRandomId gVar $ \memId -> do - member <- createContactMember_ db user groupId contact (memId, memberRole) GSMemInvited IBUser + member <- createContactMember_ db user groupId contact (memId, memberRole) GCInviteeMember GSMemInvited IBUser groupMemberId <- insertedRowId db - createMemberConnection_ db (userId user) groupMemberId agentConnId + void $ createMemberConnection_ db (userId user) groupMemberId agentConnId Nothing 0 pure member createMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> ConnId -> m () createMemberConnection st userId groupMemberId agentConnId = - liftIO . withTransaction st $ \db -> createMemberConnection_ db userId groupMemberId agentConnId + liftIO . withTransaction st $ \db -> + void $ createMemberConnection_ db userId groupMemberId agentConnId Nothing 0 updateGroupMemberStatus :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> GroupMemberStatus -> m () -updateGroupMemberStatus _st _userId _groupMemberId _memberStatus = pure () +updateGroupMemberStatus st userId groupMemberId memberStatus = + liftIO . withTransaction st $ \db -> + DB.executeNamed + db + [sql| + UPDATE group_members + SET member_status = :member_status + WHERE user_id = :user_id AND group_member_id = :group_member_id + |] + [ ":user_id" := userId, + ":group_member_id" := groupMemberId, + ":member_status" := memberStatus + ] -createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> IO () -createMemberConnection_ db userId groupMemberId agentConnId = +-- | add new member with profile +createNewGroupMember :: StoreMonad m => SQLiteStore -> User -> Group -> MemberInfo -> GroupMemberCategory -> GroupMemberStatus -> m GroupMember +createNewGroupMember st user@User {userId} group memInfo@(MemberInfo _ _ Profile {displayName, fullName}) memCategory memStatus = + liftIOEither . withTransaction st $ \db -> + withLocalDisplayName db userId displayName $ \localDisplayName -> do + DB.execute db "INSERT INTO contact_profiles (display_name, full_name) VALUES (?, ?)" (displayName, fullName) + memProfileId <- insertedRowId db + let newMember = + NewGroupMember + { memInfo, + memCategory, + memStatus, + memInvitedBy = IBUnknown, + localDisplayName, + memContactId = Nothing, + memProfileId + } + createNewMember_ db user group newMember + +createNewMember_ :: DB.Connection -> User -> Group -> NewGroupMember -> IO GroupMember +createNewMember_ + db + User {userId, userContactId} + Group {groupId} + NewGroupMember + { memInfo = MemberInfo memberId memberRole memberProfile, + memCategory = memberCategory, + memStatus = memberStatus, + memInvitedBy = invitedBy, + localDisplayName, + memContactId = memberContactId, + memProfileId + } = do + let invitedById = fromInvitedBy userContactId invitedBy + DB.execute + db + [sql| + INSERT INTO group_members + (group_id, member_id, member_role, member_category, member_status, + invited_by, user_id, local_display_name, contact_profile_id, contact_id) VALUES (?,?,?,?,?,?,?,?,?,?) + |] + (groupId, memberId, memberRole, memberCategory, memberStatus, invitedById, userId, localDisplayName, memProfileId, memberContactId) + groupMemberId <- insertedRowId db + pure $ + GroupMember + { groupMemberId, + memberId, + memberRole, + memberStatus, + memberCategory, + invitedBy, + memberProfile, + localDisplayName, + memberContactId, + activeConn = Nothing + } + +createIntroductions :: MonadUnliftIO m => SQLiteStore -> Group -> GroupMember -> m [GroupMemberIntro] +createIntroductions st Group {members} toMember = do + let reMembers = filter (\m -> memberCurrent m && groupMemberId m /= groupMemberId toMember) members + if null reMembers + then pure [] + else liftIO . withTransaction st $ \db -> + mapM (insertIntro_ db) reMembers + where + insertIntro_ :: DB.Connection -> GroupMember -> IO GroupMemberIntro + insertIntro_ db reMember = do + DB.execute + db + [sql| + INSERT INTO group_member_intros + (re_group_member_id, to_group_member_id, intro_status) VALUES (?,?,?) + |] + (groupMemberId reMember, groupMemberId toMember, GMIntroPending) + introId <- insertedRowId db + pure GroupMemberIntro {introId, reMember, toMember, introStatus = GMIntroPending, introInvitation = Nothing} + +updateIntroStatus :: MonadUnliftIO m => SQLiteStore -> GroupMemberIntro -> GroupMemberIntroStatus -> m () +updateIntroStatus st GroupMemberIntro {introId} introStatus' = + liftIO . withTransaction st $ \db -> + DB.executeNamed + db + [sql| + UPDATE group_member_intros + SET intro_status = :intro_status + WHERE group_member_intro_id = :intro_id + |] + [":intro_status" := introStatus', ":intro_id" := introId] + +saveIntroInvitation :: StoreMonad m => SQLiteStore -> GroupMember -> GroupMember -> IntroInvitation -> m GroupMemberIntro +saveIntroInvitation st reMember toMember introInv = do + liftIOEither . withTransaction st $ \db -> runExceptT $ do + intro <- getIntroduction_ db reMember toMember + liftIO $ + DB.executeNamed + db + [sql| + UPDATE group_member_intros + SET intro_status = :intro_status, + group_queue_info = :group_queue_info, + direct_queue_info = :direct_queue_info + WHERE group_member_intro_id = :intro_id + |] + [ ":intro_status" := GMIntroInvReceived, + ":group_queue_info" := groupQInfo introInv, + ":direct_queue_info" := directQInfo introInv, + ":intro_id" := introId intro + ] + pure intro {introInvitation = Just introInv, introStatus = GMIntroInvReceived} + +saveMemberInvitation :: StoreMonad m => SQLiteStore -> GroupMember -> IntroInvitation -> m () +saveMemberInvitation st GroupMember {groupMemberId} IntroInvitation {groupQInfo, directQInfo} = + liftIO . withTransaction st $ \db -> + DB.executeNamed + db + [sql| + UPDATE group_members + SET member_status = :member_status, + group_queue_info = :group_queue_info, + direct_queue_info = :direct_queue_info + WHERE group_member_id = :group_member_id + |] + [ ":member_status" := GSMemIntroInvited, + ":group_queue_info" := groupQInfo, + ":direct_queue_info" := directQInfo, + ":group_member_id" := groupMemberId + ] + +getIntroduction_ :: DB.Connection -> GroupMember -> GroupMember -> ExceptT StoreError IO GroupMemberIntro +getIntroduction_ db reMember toMember = ExceptT $ do + toIntro + <$> DB.query + db + [sql| + SELECT group_member_intro_id, group_queue_info, direct_queue_info, intro_status + FROM group_member_intros + WHERE re_group_member_id = ? AND to_group_member_id = ? + |] + (groupMemberId reMember, groupMemberId toMember) + where + toIntro :: [(Int64, Maybe SMPQueueInfo, Maybe SMPQueueInfo, GroupMemberIntroStatus)] -> Either StoreError GroupMemberIntro + toIntro [(introId, groupQInfo, directQInfo, introStatus)] = + let introInvitation = IntroInvitation <$> groupQInfo <*> directQInfo + in Right GroupMemberIntro {introId, reMember, toMember, introStatus, introInvitation} + toIntro _ = Left SEIntroNotFound + +createIntroReMember :: StoreMonad m => SQLiteStore -> User -> Group -> GroupMember -> MemberInfo -> ConnId -> ConnId -> m GroupMember +createIntroReMember st user@User {userId} group _host@GroupMember {memberContactId, activeConn} memInfo@(MemberInfo _ _ memberProfile) groupAgentConnId directAgentConnId = + liftIOEither . withTransaction st $ \db -> runExceptT $ do + let cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn + Connection {connId = directConnId} <- liftIO $ createConnection_ db userId directAgentConnId memberContactId cLevel + (localDisplayName, contactId, memProfileId) <- ExceptT $ createContact_ db userId directConnId memberProfile (Just $ groupId group) + liftIO $ do + let newMember = + NewGroupMember + { memInfo, + memCategory = GCPreMember, + memStatus = GSMemIntroduced, + memInvitedBy = IBUnknown, + localDisplayName, + memContactId = Just contactId, + memProfileId + } + member <- createNewMember_ db user group newMember + conn <- createMemberConnection_ db userId (groupMemberId member) groupAgentConnId memberContactId cLevel + pure (member :: GroupMember) {activeConn = Just conn} + +createIntroToMemberContact :: StoreMonad m => SQLiteStore -> UserId -> GroupMember -> GroupMember -> ConnId -> ConnId -> m () +createIntroToMemberContact st userId GroupMember {memberContactId = viaContactId, activeConn} _to@GroupMember {groupMemberId, localDisplayName} groupAgentConnId directAgentConnId = + liftIO . withTransaction st $ \db -> do + let cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn + void $ createMemberConnection_ db userId groupMemberId groupAgentConnId viaContactId cLevel + Connection {connId = directConnId} <- createConnection_ db userId directAgentConnId viaContactId cLevel + contactId <- createMemberContact_ db directConnId + updateMember_ db contactId + where + createMemberContact_ :: DB.Connection -> Int64 -> IO Int64 + createMemberContact_ db connId = do + DB.executeNamed + db + [sql| + INSERT INTO contacts (contact_profile_id, via_group, local_display_name, user_id) + SELECT contact_profile_id, group_id, :local_display_name, :user_id + FROM group_members + WHERE group_member_id = :group_member_id + |] + [ ":group_member_id" := groupMemberId, + ":local_display_name" := localDisplayName, + ":user_id" := userId + ] + contactId <- insertedRowId db + DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) + pure contactId + updateMember_ :: DB.Connection -> Int64 -> IO () + updateMember_ db contactId = + DB.executeNamed + db + [sql| + UPDATE group_members + SET contact_id = :contact_id + WHERE group_member_id = :group_member_id + |] + [":contact_id" := contactId, ":group_member_id" := groupMemberId] + +createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> Maybe Int64 -> Int -> IO Connection +createMemberConnection_ db userId groupMemberId agentConnId viaContact connLevel = do + createdAt <- getCurrentTime DB.execute db [sql| INSERT INTO connections - (user_id, agent_conn_id, conn_status, conn_type, group_member_id) VALUES (?,?,?,?,?); + (user_id, agent_conn_id, conn_status, conn_type, group_member_id, via_contact, conn_level, created_at) VALUES (?,?,?,?,?,?,?,?); |] - (userId, agentConnId, ConnNew, ConnMember, groupMemberId) + (userId, agentConnId, ConnNew, ConnMember, groupMemberId, viaContact, connLevel, createdAt) + connId <- insertedRowId db + pure Connection {connId, agentConnId, connType = ConnMember, entityId = Just groupMemberId, viaContact, connLevel, connStatus = ConnNew, createdAt} -createContactMember_ :: IsContact a => DB.Connection -> User -> Int64 -> a -> MemberInfo -> GroupMemberStatus -> InvitedBy -> IO GroupMember -createContactMember_ db User {userContactId} groupId userOrContact (memberId, memberRole) memberStatus invitedBy = do +createContactMember_ :: IsContact a => DB.Connection -> User -> Int64 -> a -> (MemberId, GroupMemberRole) -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> IO GroupMember +createContactMember_ db User {userId, userContactId} groupId userOrContact (memberId, memberRole) memberCategory memberStatus invitedBy = do insertMember_ groupMemberId <- insertedRowId db let memberProfile = profile' userOrContact memberContactId = Just $ contactId' userOrContact - pure GroupMember {groupMemberId, memberId, memberRole, memberStatus, invitedBy, memberProfile, memberContactId} + localDisplayName = localDisplayName' userOrContact + pure GroupMember {groupMemberId, memberId, memberRole, memberCategory, memberStatus, invitedBy, localDisplayName, memberProfile, memberContactId, activeConn = Nothing} where insertMember_ = DB.executeNamed db [sql| INSERT INTO group_members - ( group_id, member_id, member_role, member_status, invited_by, - contact_profile_id, contact_id) + ( group_id, member_id, member_role, member_category, member_status, invited_by, + user_id, local_display_name, contact_profile_id, contact_id) VALUES - (:group_id,:member_id,:member_role,:member_status,:invited_by, + (:group_id,:member_id,:member_role,:member_category,:member_status,:invited_by, + :user_id,:local_display_name, (SELECT contact_profile_id FROM contacts WHERE contact_id = :contact_id), :contact_id) |] [ ":group_id" := groupId, ":member_id" := memberId, ":member_role" := memberRole, + ":member_category" := memberCategory, ":member_status" := memberStatus, ":invited_by" := fromInvitedBy userContactId invitedBy, + ":user_id" := userId, + ":local_display_name" := localDisplayName' userOrContact, ":contact_id" := contactId' userOrContact ] +getViaGroupMember :: MonadUnliftIO m => SQLiteStore -> User -> Contact -> m (Maybe (GroupName, GroupMember)) +getViaGroupMember st User {userId, userContactId} Contact {contactId} = + liftIO . withTransaction st $ \db -> + toGroupAndMember + <$> DB.query + db + [sql| + SELECT + g.local_display_name, + m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, + m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, + c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM group_members m + JOIN contacts ct ON ct.contact_id = m.contact_id + JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id + JOIN groups g ON g.group_id = m.group_id AND g.group_id = ct.via_group + LEFT JOIN connections c ON c.connection_id = ( + SELECT max(cc.connection_id) + FROM connections cc + where cc.group_member_id = m.group_member_id + ) + WHERE ct.user_id = ? AND ct.contact_id = ? + |] + (userId, contactId) + where + toGroupAndMember :: [Only GroupName :. GroupMemberRow :. MaybeConnectionRow] -> Maybe (GroupName, GroupMember) + toGroupAndMember [Only groupName :. memberRow :. connRow] = + let member = toGroupMember userContactId memberRow + in Just (groupName, (member :: GroupMember) {activeConn = toMaybeConnection connRow}) + toGroupAndMember _ = Nothing + +getViaGroupContact :: MonadUnliftIO m => SQLiteStore -> User -> GroupMember -> m (Maybe Contact) +getViaGroupContact st User {userId} GroupMember {groupMemberId} = + liftIO . withTransaction st $ \db -> + toContact + <$> DB.query + db + [sql| + SELECT + ct.contact_id, ct.local_display_name, p.display_name, p.full_name, ct.via_group, + c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM contacts ct + JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id + JOIN connections c ON c.connection_id = ( + SELECT max(cc.connection_id) + FROM connections cc + where cc.contact_id = ct.contact_id + ) + JOIN groups g ON g.group_id = ct.via_group + JOIN group_members m ON m.group_id = g.group_id AND m.contact_id = ct.contact_id + WHERE ct.user_id = ? AND m.group_member_id = ? + |] + (userId, groupMemberId) + where + toContact :: [(Int64, ContactName, Text, Text, Maybe Int64) :. ConnectionRow] -> Maybe Contact + toContact [(contactId, localDisplayName, displayName, fullName, viaGroup) :. connRow] = + let profile = Profile {displayName, fullName} + activeConn = toConnection connRow + in Just Contact {contactId, localDisplayName, profile, activeConn, viaGroup} + toContact _ = Nothing + -- | Saves unique local display name based on passed displayName, suffixed with _N if required. -- This function should be called inside transaction. withLocalDisplayName :: forall a. DB.Connection -> UserId -> Text -> (Text -> IO a) -> IO (Either StoreError a) @@ -548,8 +871,9 @@ data StoreError | SEGroupWithoutUser | SEDuplicateGroupMember | SEGroupAlreadyJoined - | SENoGroupInvitation + | SEGroupInvitationNotFound | SEConnectionNotFound ConnId + | SEIntroNotFound | SEUniqueID | SEInternal ByteString deriving (Show, Exception) diff --git a/src/Simplex/Chat/Terminal.hs b/src/Simplex/Chat/Terminal.hs index ea29e2b24c..24d613f486 100644 --- a/src/Simplex/Chat/Terminal.hs +++ b/src/Simplex/Chat/Terminal.hs @@ -15,7 +15,7 @@ import System.Terminal import System.Terminal.Internal (LocalTerminal, Terminal, VirtualTerminal) import UnliftIO.STM -data ActiveTo = ActiveNone | ActiveC ContactName +data ActiveTo = ActiveNone | ActiveC ContactName | ActiveG GroupName deriving (Eq) data ChatTerminal = ChatTerminal diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 605f315a17..95257a94d2 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -26,14 +26,17 @@ import Simplex.Messaging.Agent.Store.SQLite (fromTextField_) class IsContact a where contactId' :: a -> Int64 profile' :: a -> Profile + localDisplayName' :: a -> ContactName instance IsContact User where contactId' = userContactId profile' = profile + localDisplayName' = localDisplayName instance IsContact Contact where contactId' = contactId profile' = profile + localDisplayName' = localDisplayName data User = User { userId :: UserId, @@ -49,7 +52,8 @@ data Contact = Contact { contactId :: Int64, localDisplayName :: ContactName, profile :: Profile, - activeConn :: Connection + activeConn :: Connection, + viaGroup :: Maybe Int64 } deriving (Eq, Show) @@ -64,7 +68,7 @@ data Group = Group { groupId :: Int64, localDisplayName :: GroupName, groupProfile :: GroupProfile, - members :: [(GroupMember, Maybe Connection)], + members :: [GroupMember], membership :: GroupMember } deriving (Eq, Show) @@ -90,34 +94,62 @@ instance ToJSON GroupProfile where toEncoding = J.genericToEncoding J.defaultOpt instance FromJSON GroupProfile data GroupInvitation = GroupInvitation - { fromMember :: MemberInfo, - invitedMember :: MemberInfo, + { fromMember :: (MemberId, GroupMemberRole), + invitedMember :: (MemberId, GroupMemberRole), queueInfo :: SMPQueueInfo, groupProfile :: GroupProfile } deriving (Eq, Show) +data IntroInvitation = IntroInvitation + { groupQInfo :: SMPQueueInfo, + directQInfo :: SMPQueueInfo + } + deriving (Eq, Show) + +data MemberInfo = MemberInfo MemberId GroupMemberRole Profile + deriving (Eq, Show) + +memberInfo :: GroupMember -> MemberInfo +memberInfo m = MemberInfo (memberId m) (memberRole m) (memberProfile m) + data ReceivedGroupInvitation = ReceivedGroupInvitation { fromMember :: GroupMember, - invitedMember :: GroupMember, + userMember :: GroupMember, queueInfo :: SMPQueueInfo, groupProfile :: GroupProfile } deriving (Eq, Show) -type MemberInfo = (MemberId, GroupMemberRole) - data GroupMember = GroupMember { groupMemberId :: Int64, memberId :: MemberId, memberRole :: GroupMemberRole, + memberCategory :: GroupMemberCategory, memberStatus :: GroupMemberStatus, invitedBy :: InvitedBy, + localDisplayName :: ContactName, memberProfile :: Profile, - memberContactId :: Maybe Int64 + memberContactId :: Maybe Int64, + activeConn :: Maybe Connection } deriving (Eq, Show) +memberConnId :: GroupMember -> Maybe ConnId +memberConnId GroupMember {activeConn} = case activeConn of + Just Connection {agentConnId} -> Just agentConnId + Nothing -> Nothing + +data NewGroupMember = NewGroupMember + { memInfo :: MemberInfo, + memCategory :: GroupMemberCategory, + memStatus :: GroupMemberStatus, + memInvitedBy :: InvitedBy, + localDisplayName :: ContactName, + memProfileId :: Int64, + memContactId :: Maybe Int64 + } + type MemberId = ByteString data InvitedBy = IBContact Int64 | IBUser | IBUnknown @@ -160,37 +192,107 @@ fromBlobField_ p = \case f@(Field (SQLBlob b) _) -> case p b of Right k -> Ok k - Left e -> returnError ConversionFailed f ("couldn't parse field: " ++ e) + Left e -> returnError ConversionFailed f ("could not parse field: " ++ e) f -> returnError ConversionFailed f "expecting SQLBlob column type" +data GroupMemberCategory + = GCUserMember + | GCInviteeMember -- member invited by the user + | GCHostMember -- member who invited the user + | GCPreMember -- member who joined before the user and was introduced to the user (user receives x.grp.mem.intro about such members) + | GCPostMember -- member who joined after the user to whom the user was introduced (user receives x.grp.mem.new announcing these members and then x.grp.mem.fwd with invitation from these members) + deriving (Eq, Show) + +instance FromField GroupMemberCategory where fromField = fromTextField_ memberCategoryT + +instance ToField GroupMemberCategory where toField = toField . serializeMemberCategory + +memberCategoryT :: Text -> Maybe GroupMemberCategory +memberCategoryT = \case + "user" -> Just GCUserMember + "invitee" -> Just GCInviteeMember + "host" -> Just GCHostMember + "pre" -> Just GCPreMember + "post" -> Just GCPostMember + _ -> Nothing + +serializeMemberCategory :: GroupMemberCategory -> Text +serializeMemberCategory = \case + GCUserMember -> "user" + GCInviteeMember -> "invitee" + GCHostMember -> "host" + GCPreMember -> "pre" + GCPostMember -> "post" + data GroupMemberStatus - = GSMemInvited -- member received (or sent to) invitation - | GSMemAccepted -- member accepted invitation + = GSMemRemoved -- member who was removed from the group + | GSMemLeft -- member who left the group + | GSMemInvited -- member is sent to or received invitation to join the group + | GSMemIntroduced -- user received x.grp.mem.intro for this member (only with GCPreMember) + | GSMemIntroInvited -- member is sent to or received from intro invitation + | GSMemAccepted -- member accepted invitation (only User and Invitee) + | GSMemAnnounced -- host announced (x.grp.mem.new) a member (Invitee and PostMember) to the group - at this point this member can send messages and invite other members (if they have sufficient permissions) | GSMemConnected -- member created the group connection with the inviting member - | GSMemReady -- member connections are forwarded to all previous members - | GSMemFull -- member created group connections with all previous members + | GSMemComplete -- host confirmed (x.grp.mem.all) that a member (User, Invitee and PostMember) created group connections with all previous members + | GSMemCreator -- user member that created the group (only GCUserMember) deriving (Eq, Show, Ord) instance FromField GroupMemberStatus where fromField = fromTextField_ memberStatusT instance ToField GroupMemberStatus where toField = toField . serializeMemberStatus +memberActive :: GroupMember -> Bool +memberActive m = case memberStatus m of + GSMemRemoved -> False + GSMemLeft -> False + GSMemInvited -> False + GSMemIntroduced -> False + GSMemIntroInvited -> False + GSMemAccepted -> False + GSMemAnnounced -> False + GSMemConnected -> True + GSMemComplete -> True + GSMemCreator -> True + +memberCurrent :: GroupMember -> Bool +memberCurrent m = case memberStatus m of + GSMemRemoved -> False + GSMemLeft -> False + GSMemInvited -> False + GSMemIntroduced -> True + GSMemIntroInvited -> True + GSMemAccepted -> True + GSMemAnnounced -> True + GSMemConnected -> True + GSMemComplete -> True + GSMemCreator -> True + memberStatusT :: Text -> Maybe GroupMemberStatus memberStatusT = \case + "removed" -> Just GSMemRemoved + "left" -> Just GSMemLeft "invited" -> Just GSMemInvited + "introduced" -> Just GSMemIntroduced + "intro-inv" -> Just GSMemIntroInvited "accepted" -> Just GSMemAccepted + "announced" -> Just GSMemAnnounced "connected" -> Just GSMemConnected - "ready" -> Just GSMemReady - "full" -> Just GSMemFull + "complete" -> Just GSMemComplete + "creator" -> Just GSMemCreator _ -> Nothing serializeMemberStatus :: GroupMemberStatus -> Text serializeMemberStatus = \case + GSMemRemoved -> "removed" + GSMemLeft -> "left" GSMemInvited -> "invited" + GSMemIntroduced -> "introduced" + GSMemIntroInvited -> "intro-inv" GSMemAccepted -> "accepted" + GSMemAnnounced -> "announced" GSMemConnected -> "connected" - GSMemReady -> "ready" - GSMemFull -> "full" + GSMemComplete -> "complete" + GSMemCreator -> "creator" data Connection = Connection { connId :: Int64, @@ -204,7 +306,19 @@ data Connection = Connection } deriving (Eq, Show) -data ConnStatus = ConnNew | ConnConfirmed | ConnAccepted | ConnReady +data ConnStatus + = -- | connection is created by initiating party with agent NEW command (createConnection) + ConnNew + | -- | connection is joined by joining party with agent JOIN command (joinConnection) + ConnJoined + | -- | initiating party received CONF notification (to be renamed to REQ) + ConnRequested + | -- | initiating party accepted connection with agent LET command (to be renamed to ACPT) (allowConnection) + ConnAccepted + | -- | connection can be sent messages to (after joining party received INFO notification) + ConnSndReady + | -- | connection is ready for both parties to send and receive messages + ConnReady deriving (Eq, Show) instance FromField ConnStatus where fromField = fromTextField_ connStatusT @@ -214,16 +328,20 @@ instance ToField ConnStatus where toField = toField . serializeConnStatus connStatusT :: Text -> Maybe ConnStatus connStatusT = \case "new" -> Just ConnNew - "confirmed" -> Just ConnConfirmed + "joined" -> Just ConnJoined + "requested" -> Just ConnRequested "accepted" -> Just ConnAccepted + "snd-ready" -> Just ConnSndReady "ready" -> Just ConnReady _ -> Nothing serializeConnStatus :: ConnStatus -> Text serializeConnStatus = \case ConnNew -> "new" - ConnConfirmed -> "confirmed" + ConnJoined -> "joined" + ConnRequested -> "requested" ConnAccepted -> "accepted" + ConnSndReady -> "snd-ready" ConnReady -> "ready" data ConnType = ConnContact | ConnMember @@ -249,3 +367,45 @@ data NewConnection = NewConnection connLevel :: Int, viaConn :: Maybe Int64 } + +data GroupMemberIntro = GroupMemberIntro + { introId :: Int64, + reMember :: GroupMember, + toMember :: GroupMember, + introStatus :: GroupMemberIntroStatus, + introInvitation :: Maybe IntroInvitation + } + +data GroupMemberIntroStatus + = GMIntroPending + | GMIntroSent + | GMIntroInvReceived + | GMIntroInvForwarded + | GMIntroReConnected + | GMIntroToConnected + | GMIntroConnected + +instance FromField GroupMemberIntroStatus where fromField = fromTextField_ introStatusT + +instance ToField GroupMemberIntroStatus where toField = toField . serializeIntroStatus + +introStatusT :: Text -> Maybe GroupMemberIntroStatus +introStatusT = \case + "new" -> Just GMIntroPending + "sent" -> Just GMIntroSent + "rcv" -> Just GMIntroInvReceived + "fwd" -> Just GMIntroInvForwarded + "re-con" -> Just GMIntroReConnected + "to-con" -> Just GMIntroToConnected + "con" -> Just GMIntroConnected + _ -> Nothing + +serializeIntroStatus :: GroupMemberIntroStatus -> Text +serializeIntroStatus = \case + GMIntroPending -> "new" + GMIntroSent -> "sent" + GMIntroInvReceived -> "rcv" + GMIntroInvForwarded -> "fwd" + GMIntroReConnected -> "re-con" + GMIntroToConnected -> "to-con" + GMIntroConnected -> "con" diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index dee04bb327..16fbb3fca0 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -13,12 +13,16 @@ module Simplex.Chat.View showContactConnected, showContactDisconnected, showReceivedMessage, + showReceivedGroupMessage, showSentMessage, + showSentGroupMessage, showGroupCreated, showSentGroupInvitation, showReceivedGroupInvitation, - showConnectedGroupMember, - showUserConnectedToGroup, + showJoinedGroupMember, + showUserJoinedGroup, + showJoinedGroupMemberConnecting, + showConnectedToGroupMember, safeDecodeUtf8, ) where @@ -26,7 +30,7 @@ where import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.ByteString.Char8 (ByteString) -import Data.Composition ((.:)) +import Data.Composition ((.:), (.:.)) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (DiffTime, UTCTime) @@ -60,10 +64,22 @@ showContactDisconnected :: ChatReader m => ContactName -> m () showContactDisconnected = printToView . contactDisconnected showReceivedMessage :: ChatReader m => ContactName -> UTCTime -> Text -> MsgIntegrity -> m () -showReceivedMessage c utcTime msg mOk = printToView =<< liftIO (receivedMessage c utcTime msg mOk) +showReceivedMessage = showReceivedMessage_ . ttyFromContact + +showReceivedGroupMessage :: ChatReader m => GroupName -> ContactName -> UTCTime -> Text -> MsgIntegrity -> m () +showReceivedGroupMessage = showReceivedMessage_ .: ttyFromGroup + +showReceivedMessage_ :: ChatReader m => StyledString -> UTCTime -> Text -> MsgIntegrity -> m () +showReceivedMessage_ from utcTime msg mOk = printToView =<< liftIO (receivedMessage from utcTime msg mOk) showSentMessage :: ChatReader m => ContactName -> ByteString -> m () -showSentMessage c msg = printToView =<< liftIO (sentMessage c msg) +showSentMessage = showSentMessage_ . ttyToContact + +showSentGroupMessage :: ChatReader m => GroupName -> ByteString -> m () +showSentGroupMessage = showSentMessage_ . ttyToGroup + +showSentMessage_ :: ChatReader m => StyledString -> ByteString -> m () +showSentMessage_ to msg = printToView =<< liftIO (sentMessage to msg) showGroupCreated :: ChatReader m => Group -> m () showGroupCreated = printToView . groupCreated @@ -71,14 +87,20 @@ showGroupCreated = printToView . groupCreated showSentGroupInvitation :: ChatReader m => Group -> ContactName -> m () showSentGroupInvitation = printToView .: sentGroupInvitation -showReceivedGroupInvitation :: ChatReader m => Group -> ContactName -> m () -showReceivedGroupInvitation = printToView .: receivedGroupInvitation +showReceivedGroupInvitation :: ChatReader m => Group -> ContactName -> GroupMemberRole -> m () +showReceivedGroupInvitation = printToView .:. receivedGroupInvitation -showConnectedGroupMember :: ChatReader m => GroupName -> ContactName -> m () -showConnectedGroupMember = printToView .: connectedGroupMember +showJoinedGroupMember :: ChatReader m => GroupName -> GroupMember -> m () +showJoinedGroupMember = printToView .: joinedGroupMember -showUserConnectedToGroup :: ChatReader m => GroupName -> m () -showUserConnectedToGroup = printToView . userConnectedToGroup +showUserJoinedGroup :: ChatReader m => GroupName -> m () +showUserJoinedGroup = printToView . userJoinedGroup + +showJoinedGroupMemberConnecting :: ChatReader m => GroupName -> GroupMember -> GroupMember -> m () +showJoinedGroupMemberConnecting = printToView .:. joinedGroupMemberConnecting + +showConnectedToGroupMember :: ChatReader m => GroupName -> GroupMember -> m () +showConnectedToGroupMember = printToView .: connectedToGroupMember invitation :: SMPQueueInfo -> [StyledString] invitation qInfo = @@ -101,28 +123,40 @@ contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart cha groupCreated :: Group -> [StyledString] groupCreated g@Group {localDisplayName} = [ "group " <> ttyFullGroup g <> " is created", - "use " <> highlight ("/a #" <> localDisplayName <> " ") <> " to add members" + "use " <> highlight ("/a " <> localDisplayName <> " ") <> " to add members" ] sentGroupInvitation :: Group -> ContactName -> [StyledString] sentGroupInvitation g c = ["invitation to join the group " <> ttyFullGroup g <> " sent to " <> ttyContact c] -receivedGroupInvitation :: Group -> ContactName -> [StyledString] -receivedGroupInvitation g@Group {localDisplayName} c = - [ ttyContact c <> " invites you to join the group " <> ttyFullGroup g, - "use " <> highlight ("/j #" <> localDisplayName) <> " to accept" +receivedGroupInvitation :: Group -> ContactName -> GroupMemberRole -> [StyledString] +receivedGroupInvitation g@Group {localDisplayName} c role = + [ ttyFullGroup g <> ": " <> ttyContact c <> " invites you to join the group as " <> plain (serializeMemberRole role), + "use " <> highlight ("/j " <> localDisplayName) <> " to accept" ] -connectedGroupMember :: GroupName -> ContactName -> [StyledString] -connectedGroupMember g c = [ttyContact c <> " joined the group " <> ttyGroup g] +joinedGroupMember :: GroupName -> GroupMember -> [StyledString] +joinedGroupMember g m = [ttyGroup g <> ": " <> ttyMember m <> " joined the group "] -userConnectedToGroup :: GroupName -> [StyledString] -userConnectedToGroup g = ["you joined the group " <> ttyGroup g] +userJoinedGroup :: GroupName -> [StyledString] +userJoinedGroup g = [ttyGroup g <> ": you joined the group"] -receivedMessage :: ContactName -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] -receivedMessage c utcTime msg mOk = do +joinedGroupMemberConnecting :: GroupName -> GroupMember -> GroupMember -> [StyledString] +joinedGroupMemberConnecting g host m = [ttyGroup g <> ": " <> ttyMember host <> " added " <> ttyFullMember m <> " to the group (connecting...)"] + +connectedToGroupMember :: GroupName -> GroupMember -> [StyledString] +connectedToGroupMember g m = [ttyGroup g <> ": " <> connectedMember m <> " is connected"] + +connectedMember :: GroupMember -> StyledString +connectedMember m = case memberCategory m of + GCPreMember -> "member " <> ttyFullMember m + GCPostMember -> "new member " <> ttyMember m -- without fullName as as it was shown in joinedGroupMemberConnecting + _ -> "member " <> ttyMember m -- these case is not used + +receivedMessage :: StyledString -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] +receivedMessage from utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime - pure $ prependFirst (t <> " " <> ttyFromContact c) (msgPlain msg) ++ showIntegrity mOk + pure $ prependFirst (t <> " " <> from) (msgPlain msg) ++ showIntegrity mOk where formatUTCTime :: TimeZone -> ZonedTime -> StyledString formatUTCTime localTz currentTime = @@ -145,10 +179,10 @@ receivedMessage c utcTime msg mOk = do msgError :: String -> [StyledString] msgError s = [styled (Colored Red) s] -sentMessage :: ContactName -> ByteString -> IO [StyledString] -sentMessage c msg = do +sentMessage :: StyledString -> ByteString -> IO [StyledString] +sentMessage to msg = do time <- formatTime defaultTimeLocale "%H:%M" <$> getZonedTime - pure $ prependFirst (styleTime time <> " " <> ttyToContact c) (msgPlain $ safeDecodeUtf8 msg) + pure $ prependFirst (styleTime time <> " " <> to) (msgPlain $ safeDecodeUtf8 msg) prependFirst :: StyledString -> [StyledString] -> [StyledString] prependFirst s [] = [s] @@ -162,9 +196,10 @@ chatError = \case ChatError err -> case err of CEGroupDuplicateMember c -> ["contact " <> ttyContact c <> " is already in the group"] CEGroupDuplicateMemberId -> ["cannot add member - duplicate member ID"] - CEGroupRole -> ["insufficient role for this group command"] + CEGroupUserRole -> ["you have insufficient permissions for this group command"] + CEGroupContactRole c -> ["contact " <> ttyContact c <> " has insufficient permissions for this group action"] CEGroupNotJoined g -> ["you did not join this group, use " <> highlight ("/join #" <> g)] - CEGroupMemberNotReady -> ["you cannot invite other members yet, try later"] + CEGroupMemberNotActive -> ["you cannot invite other members yet, try later"] CEGroupInternal s -> ["chat group bug: " <> plain s] -- e -> ["chat error: " <> plain (show e)] ChatErrorStore err -> case err of @@ -191,7 +226,17 @@ ttyContact = styled (Colored Green) ttyFullContact :: Contact -> StyledString ttyFullContact Contact {localDisplayName, profile = Profile {fullName}} = - ttyContact localDisplayName <> optFullName localDisplayName fullName + ttyFullName localDisplayName fullName + +ttyMember :: GroupMember -> StyledString +ttyMember GroupMember {localDisplayName} = ttyContact localDisplayName + +ttyFullMember :: GroupMember -> StyledString +ttyFullMember GroupMember {localDisplayName, memberProfile = Profile {fullName}} = + ttyFullName localDisplayName fullName + +ttyFullName :: ContactName -> Text -> StyledString +ttyFullName c fullName = ttyContact c <> optFullName c fullName ttyToContact :: ContactName -> StyledString ttyToContact c = styled (Colored Cyan) $ "@" <> c <> " " @@ -206,9 +251,15 @@ ttyFullGroup :: Group -> StyledString ttyFullGroup Group {localDisplayName, groupProfile = GroupProfile {fullName}} = ttyGroup localDisplayName <> optFullName localDisplayName fullName -optFullName :: Text -> Text -> StyledString +ttyFromGroup :: GroupName -> ContactName -> StyledString +ttyFromGroup g c = styled (Colored Yellow) $ "#" <> g <> " " <> c <> "> " + +ttyToGroup :: GroupName -> StyledString +ttyToGroup g = styled (Colored Cyan) $ "#" <> g <> " " + +optFullName :: ContactName -> Text -> StyledString optFullName localDisplayName fullName - | localDisplayName == fullName = "" + | T.null fullName || localDisplayName == fullName = "" | otherwise = plain (" (" <> fullName <> ")") highlight :: StyledFormat a => a -> StyledString @@ -217,8 +268,5 @@ highlight = styled (Colored Cyan) highlight' :: String -> StyledString highlight' = highlight --- ttyFromGroup :: Group -> Contact -> StyledString --- ttyFromGroup (Group g) (Contact a) = styled (Colored Yellow) $ "#" <> g <> " " <> a <> "> " - styleTime :: String -> StyledString styleTime = Styled [SetColor Foreground Vivid Black] diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md index adec3f48f3..9e8629c90c 100644 --- a/src/Simplex/Chat/protocol.md +++ b/src/Simplex/Chat/protocol.md @@ -75,7 +75,12 @@ A -> B: invite to group - `MSG: x.grp.inv G_MEM_ID_A,G_MEM_ROLE_A,G_MEM_ID_B,G_M user B confirms B -> A: establish group connection (B: JOIN, A: LET) B -> Ag: join group - `in SMP confirmation: x.grp.acpt G_MEM_ID_B` -A -> group (including B)): announce group member: `MSG: N x.grp.mem.new G_MEM_ID_B,G_MEM_ROLE_B x.json:NNN ` +A -> group (including B)): announce group member: `MSG: N x.grp.mem.new G_MEM_ID_B,G_MEM_ROLE_B,G_MEM_ID_M,... x.json:NNN ` + +In the message `x.grp.mem.new` A sends the sorted list of all members to whom A is connected followed by the new member ID, role and profile. The following introductions will be sent about/to all members A "knows about" (includes members introduced to A and members who accepted group invitation but not connected yet), once they are connected, so it can be a bigger list than sent in `x.grp.mem.new`. + +All members who received `x.grp.mem.new` from A should check the list of connected members and if any connected members that recipients invited to the group are not in this list, they should introduce them to this new member (the last ID, role and profile in `x.grp.mem.new`). That might lead to double introductions that would provide a stronger consistency of group membership at a cost of extra connection between some members that will be unused. + subsequent messages between A and B are via group connection A -> Bg: intro member - `MSG: x.grp.mem.intro G_MEM_ID_M,G_MEM_ROLE_M x.json:NNN ` B -> Ag: inv for mem - `MSG: x.grp.mem.inv G_MEM_ID_M,,,` @@ -83,16 +88,17 @@ M is an existing member, messages are via group connection A -> Mg: fwd inv - `MSG: x.grp.mem.fwd G_MEM_ID_B,,,` M -> Bg: establish group connection (M: JOIN, B: LET) M -> B: establish direct connection (M: JOIN, B: LET) -M -> Bg: confirm profile and role - `MSG: x.grp.mem.info G_MEM_ID_M,G_MEM_ROLE x.json:NNN ` +M -> Bg: confirm profile and role - `CONF: x.grp.mem.info G_MEM_ID_M,G_MEM_ROLE x.json:NNN ` +B -> Mg: send profile probe - `MSG: x.info.probe ` - it should always be send, even when there is no profile match. if M is a known contact (profile match) send probe to M: - B -> M (via old DM conn): profile match probe: `MSG: x.grp.mem.probe G_MEM_ID_B,` - M -> B (via old DM conn): probe confirm: `MSG: x.grp.mem.probe.ok G_MEM_ID_M,` + B -> M (via old DM conn): profile match probe: `MSG: x.info.probe.check G_MEM_ID_B,` + M -> B (via old DM conn): probe confirm: `MSG: x.info.probe.ok G_MEM_ID_M,` link to the same contact B -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_M` M -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_B` once all members connected -A -> group: `MSG: N x.grp.mem.ok G_MEM_ID_B` +A -> group: `MSG: N x.grp.mem.con.all G_MEM_ID_B` #### Send group message diff --git a/stack.yaml b/stack.yaml index 732c3d6e25..b0136113dc 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 8ba3e3e45a6006d173738db9eac1068edad74df7 + commit: 53aadca635a6953bd18a305423783b0d71a13cb6 # this commit is in PR #164 # # extra-deps: [] diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index be249edbeb..5b35ce3dac 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -7,6 +7,7 @@ module ChatClient where import Control.Concurrent.Async import Control.Concurrent.STM (retry) +import Control.Exception (bracket_) import Control.Monad.Except import Simplex.Chat import Simplex.Chat.Controller (ChatController (..)) @@ -17,11 +18,8 @@ import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import qualified System.Terminal as C import System.Terminal.Internal (VirtualTerminal, VirtualTerminalSettings (..), withVirtualTerminal) -testDB1 :: FilePath -testDB1 = "tests/tmp/test1" - -testDB2 :: FilePath -testDB2 = "tests/tmp/test2" +testDBPrefix :: FilePath +testDBPrefix = "tests/tmp/test" opts :: ChatOpts opts = @@ -50,10 +48,39 @@ virtualSimplexChat dbFile profile = do a <- async $ runSimplexChat cc pure (TestCC cc t a) +testChatN :: [Profile] -> ([TestCC] -> IO ()) -> IO () +testChatN ps test = + bracket_ + (createDirectoryIfMissing False "tests/tmp") + (removeDirectoryRecursive "tests/tmp") + $ do + let envs = zip ps $ map ((testDBPrefix <>) . show) [(1 :: Int) ..] + tcs <- getTestCCs envs [] + test tcs + where + getTestCCs [] tcs = pure tcs + getTestCCs ((p, db) : envs') tcs = (:) <$> virtualSimplexChat db p <*> getTestCCs envs' tcs + testChat2 :: Profile -> Profile -> (TestCC -> TestCC -> IO ()) -> IO () -testChat2 p1 p2 test = do - createDirectoryIfMissing False "tests/tmp" - tc1 <- virtualSimplexChat testDB1 p1 - tc2 <- virtualSimplexChat testDB2 p2 - test tc1 tc2 - removeDirectoryRecursive "tests/tmp" +testChat2 p1 p2 test = testChatN [p1, p2] test_ + where + test_ :: [TestCC] -> IO () + test_ [tc1, tc2] = test tc1 tc2 + test_ _ = error "expected 2 chat clients" + +testChat3 :: Profile -> Profile -> Profile -> (TestCC -> TestCC -> TestCC -> IO ()) -> IO () +testChat3 p1 p2 p3 test = testChatN [p1, p2, p3] test_ + where + test_ :: [TestCC] -> IO () + test_ [tc1, tc2, tc3] = test tc1 tc2 tc3 + test_ _ = error "expected 3 chat clients" + +testChat4 :: Profile -> Profile -> Profile -> Profile -> (TestCC -> TestCC -> TestCC -> TestCC -> IO ()) -> IO () +testChat4 p1 p2 p3 p4 test = testChatN [p1, p2, p3, p4] test_ + where + test_ :: [TestCC] -> IO () + test_ [tc1, tc2, tc3, tc4] = test tc1 tc2 tc3 tc4 + test_ _ = error "expected 4 chat clients" + +concurrentlyN_ :: [IO a] -> IO () +concurrentlyN_ = mapConcurrently_ id diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 28c847621d..b37ace8793 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module ChatTests where @@ -7,8 +8,9 @@ import Control.Concurrent.Async (concurrently_) import Control.Concurrent.STM import Data.Char (isDigit) import Data.List (dropWhileEnd, isPrefixOf) +import qualified Data.Text as T import Simplex.Chat.Controller -import Simplex.Chat.Types (Profile (..)) +import Simplex.Chat.Types (Profile (..), User (..)) import System.Terminal.Internal (VirtualTerminal (..)) import Test.Hspec @@ -18,12 +20,19 @@ aliceProfile = Profile {displayName = "alice", fullName = "Alice"} bobProfile :: Profile bobProfile = Profile {displayName = "bob", fullName = "Bob"} +cathProfile :: Profile +cathProfile = Profile {displayName = "cath", fullName = "Catherine"} + +danProfile :: Profile +danProfile = Profile {displayName = "dan", fullName = "Daniel"} + chatTests :: Spec chatTests = do describe "direct messages" $ it "add contact and send/receive message" testAddContact - describe "chat groups" $ + describe "chat groups" $ do it "add contacts, create group and send/receive messages" testGroup + it "create and join group with 4 members" testGroup2 testAddContact :: IO () testAddContact = @@ -58,19 +67,133 @@ testAddContact = testGroup :: IO () testGroup = - testChat2 aliceProfile bobProfile $ - \alice bob -> do + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do connectUsers alice bob - alice #:> "/g #team" + connectUsers alice cath + alice #:> "/g team" -- TODO this occasionally fails in case getWindow is run before the command above is printed - alice <## "use /a #team to add members" - alice ##> "/a #team bob admin" + alice <## "use /a team to add members" + alice ##> "/a team bob" alice <## "invitation to join the group #team sent to bob" - bob <## "use /j #team to accept" - bob ##> "/j #team" + bob <## "use /j team to accept" + bob ##> "/j team" concurrently_ - (alice <## "bob joined the group #team") - (bob <## "you joined the group #team") + (alice <## "#team: bob joined the group") + (bob <## "#team: you joined the group") + alice ##> "/a team cath" + alice <## "invitation to join the group #team sent to cath" + cath <## "use /j team to accept" + cath ##> "/j team" + concurrentlyN_ + [ alice <## "#team: cath joined the group", + do + cath <## "#team: you joined the group" + cath <## "#team: member bob (Bob) is connected", + do + bob <## "#team: alice added cath (Catherine) to the group (connecting...)" + bob <## "#team: new member cath is connected" + ] + alice #> "#team hello" + concurrently_ + (bob <# "#team alice> hello") + (cath <# "#team alice> hello") + bob #> "#team hi there" + concurrently_ + (alice <# "#team bob> hi there") + (cath <# "#team bob> hi there") + cath #> "#team hey" + concurrently_ + (alice <# "#team cath> hey") + (bob <# "#team cath> hey") + bob #> "@cath hello cath" + cath <# "bob> hello cath" + cath #> "@bob hello bob" + bob <# "cath> hello bob" + +testGroup2 :: IO () +testGroup2 = + testChat4 aliceProfile bobProfile cathProfile danProfile $ + \alice bob cath dan -> do + connectUsers alice bob + connectUsers alice cath + connectUsers bob dan + connectUsers alice dan + alice #:> "/g club" + -- TODO this occasionally fails in case getWindow is run before the command above is printed + alice <## "use /a club to add members" + alice ##> "/a club bob" + alice <## "invitation to join the group #club sent to bob" + bob <## "use /j club to accept" + alice ##> "/a club cath" + alice <## "invitation to join the group #club sent to cath" + cath <## "use /j club to accept" + bob ##> "/j club" + concurrently_ + (alice <## "#club: bob joined the group") + (bob <## "#club: you joined the group") + cath ##> "/j club" + concurrentlyN_ + [ alice <## "#club: cath joined the group", + do + cath <## "#club: you joined the group" + cath <## "#club: member bob (Bob) is connected", + do + bob <## "#club: alice added cath (Catherine) to the group (connecting...)" + bob <## "#club: new member cath is connected" + ] + bob ##> "/a club dan" + bob <## "invitation to join the group #club sent to dan" + dan <## "use /j club to accept" + dan ##> "/j club" + concurrentlyN_ + [ bob <## "#club: dan joined the group", + do + dan <## "#club: you joined the group" + dan <### ["#club: member alice_1 (Alice) is connected", "#club: member cath (Catherine) is connected"], + do + alice <## "#club: bob added dan_1 (Daniel) to the group (connecting...)" + alice <## "#club: new member dan_1 is connected", + do + cath <## "#club: bob added dan (Daniel) to the group (connecting...)" + cath <## "#club: new member dan is connected" + ] + alice #> "#club hello" + concurrentlyN_ + [ bob <# "#club alice> hello", + cath <# "#club alice> hello", + dan <# "#club alice_1> hello" + ] + bob #> "#club hi there" + concurrentlyN_ + [ alice <# "#club bob> hi there", + cath <# "#club bob> hi there", + dan <# "#club bob> hi there" + ] + cath #> "#club hey" + concurrentlyN_ + [ alice <# "#club cath> hey", + bob <# "#club cath> hey", + dan <# "#club cath> hey" + ] + dan #> "#club how is it going?" + concurrentlyN_ + [ alice <# "#club dan_1> how is it going?", + bob <# "#club dan> how is it going?", + cath <# "#club dan> how is it going?" + ] + bob #> "@cath hi cath" + cath <# "bob> hi cath" + cath #> "@bob hi bob" + bob <# "cath> hi bob" + dan #> "@cath hey cath" + cath <# "dan> hey cath" + cath #> "@dan hey dan" + dan <# "cath> hey dan" + dan #> "@alice_1 hi alice" + alice <# "dan_1> hi alice" + alice #> "@dan_1 hello dan" + dan <# "alice_1> hello dan" connectUsers :: TestCC -> TestCC -> IO () connectUsers cc1 cc2 = do @@ -78,8 +201,12 @@ connectUsers cc1 cc2 = do Just inv <- invitation <$> getWindow cc1 cc2 ##> ("/c " <> inv) concurrently_ - (cc2 <## "alice (Alice) is connected") - (cc1 <## "bob (Bob) is connected") + (cc2 <## (showName cc1 <> " is connected")) + (cc1 <## (showName cc2 <> " is connected")) + where + showName :: TestCC -> String + showName (TestCC ChatController {currentUser = User {localDisplayName, profile = Profile {fullName}}} _ _) = + T.unpack $ localDisplayName <> " (" <> fullName <> ")" (##>) :: TestCC -> String -> IO () (##>) cc cmd = do @@ -97,6 +224,14 @@ connectUsers cc1 cc2 = do (<##) :: TestCC -> String -> Expectation cc <## line = (lastOutput <$> getWindow cc) `shouldReturn` line +(<###) :: TestCC -> [String] -> Expectation +_ <### [] = pure () +cc <### ls = do + line <- lastOutput <$> getWindow cc + if line `elem` ls + then cc <### filter (/= line) ls + else error $ "unexpected output: " <> line + (<#) :: TestCC -> String -> Expectation cc <# line = (dropTime . lastOutput <$> getWindow cc) `shouldReturn` line From 488df1aa3c237bf05322fdc1f897c775c6711e30 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 24 Jul 2021 18:11:04 +0100 Subject: [PATCH 14/29] refactor groups (#84) * refactor groups * disable chat test * remove comments --- src/Simplex/Chat.hs | 323 ++++++++++++++++++----------------- src/Simplex/Chat/Protocol.hs | 8 +- src/Simplex/Chat/Store.hs | 6 +- tests/ChatTests.hs | 25 +-- 4 files changed, 195 insertions(+), 167 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index d5b4e3a2ea..404f7eba0e 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -209,9 +209,42 @@ agentSubscriber = do processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () processAgentMessage user@User {userId, profile} agentConnId agentMessage = do chatDirection <- withStore $ \st -> getConnectionChatDirection st user agentConnId + forM_ (agentMsgConnStatus agentMessage) $ \status -> + withStore $ \st -> updateConnectionStatus st (fromConnection chatDirection) status case chatDirection of - ReceivedDMContact ct@Contact {localDisplayName = c, activeConn} -> - case agentMessage of + ReceivedDirectMessage conn maybeContact -> + processDirectMessage agentMessage conn maybeContact + ReceivedGroupMessage conn gName m -> + processGroupMessage agentMessage conn gName m + where + isMember :: MemberId -> Group -> Bool + isMember memId Group {membership, members} = + memberId membership == memId || isJust (find ((== memId) . memberId) members) + + contactIsReady :: Contact -> Bool + contactIsReady Contact {activeConn} = connStatus activeConn == ConnReady + + memberIsReady :: GroupMember -> Bool + memberIsReady GroupMember {activeConn} = maybe False ((== ConnReady) . connStatus) activeConn + + agentMsgConnStatus :: ACommand 'Agent -> Maybe ConnStatus + agentMsgConnStatus = \case + CONF _ _ -> Just ConnRequested + INFO _ -> Just ConnSndReady + CON -> Just ConnReady + _ -> Nothing + + processDirectMessage :: ACommand 'Agent -> Connection -> Maybe Contact -> m () + processDirectMessage agentMsg conn = \case + Nothing -> case agentMsg of + CONF confId connInfo -> do + saveConnInfo conn connInfo + acceptAgentConnection conn confId $ XInfo profile + INFO connInfo -> + saveConnInfo conn connInfo + CON -> pure () + _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMsg) + Just ct@Contact {localDisplayName = c} -> case agentMsg of MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of @@ -221,16 +254,14 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do _ -> pure () CONF confId connInfo -> do -- confirming direct connection with a member - withStore $ \st -> updateConnectionStatus st activeConn ConnRequested ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of XGrpMemInfo _memId _memProfile -> do -- TODO check member ID -- TODO update member profile - acceptAgentConnection activeConn confId XOk + acceptAgentConnection conn confId XOk _ -> messageError "CONF from member must have x.grp.mem.info" INFO connInfo -> do - withStore $ \st -> updateConnectionStatus st activeConn ConnSndReady ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of XGrpMemInfo _memId _memProfile -> do @@ -239,8 +270,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do pure () XOk -> pure () _ -> messageError "INFO from member must have x.grp.mem.info or x.ok" - CON -> do - withStore $ \st -> updateConnectionStatus st activeConn ConnReady + CON -> withStore (\st -> getViaGroupMember st user ct) >>= \case Nothing -> do showContactConnected ct @@ -252,151 +282,81 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do showContactDisconnected c showToast (c <> "> ") "disconnected" unsetActive $ ActiveC c - _ -> messageError $ "unexpected agent event: " <> T.pack (show agentMessage) - ReceivedDMConnection conn -> - case agentMessage of - CONF confId connInfo -> do - withStore $ \st -> updateConnectionStatus st conn ConnRequested - saveConnInfo conn connInfo - acceptAgentConnection conn confId $ XInfo profile - INFO connInfo -> do - withStore $ \st -> updateConnectionStatus st conn ConnSndReady - saveConnInfo conn connInfo - CON -> - withStore $ \st -> updateConnectionStatus st conn ConnReady - _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMessage) - ReceivedGroupMessage conn gName m -> - case agentMessage of - CONF confId connInfo -> do - withStore $ \st -> updateConnectionStatus st conn ConnRequested - ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo - case memberCategory m of - GCInviteeMember -> - case chatMsgEvent of - XGrpAcpt memId - | memId == memberId m -> do - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemAccepted - acceptAgentConnection conn confId XOk - | otherwise -> messageError "x.grp.acpt: memberId is different from expected" - _ -> messageError "CONF from invited member must have x.grp.acpt" - _ -> - case chatMsgEvent of - XGrpMemInfo memId _memProfile - | memId == memberId m -> do - -- TODO update member profile - Group {membership} <- withStore $ \st -> getGroup st user gName - acceptAgentConnection conn confId $ XGrpMemInfo (memberId membership) profile - | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" - _ -> messageError "CONF from member must have x.grp.mem.info" - INFO connInfo -> do - withStore $ \st -> updateConnectionStatus st conn ConnSndReady - ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo - case chatMsgEvent of - XGrpMemInfo memId _memProfile - | memId == memberId m -> do - -- TODO update member profile - pure () - | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" - XOk -> pure () - _ -> messageError "INFO from member must have x.grp.mem.info" - pure () - CON -> do - group@Group {members, membership} <- withStore $ \st -> getGroup st user gName - withStore $ \st -> do - updateConnectionStatus st conn ConnReady - updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected - updateGroupMemberStatus st userId (groupMemberId membership) GSMemConnected - -- TODO forward any pending (GMIntroInvReceived) introductions - case memberCategory m of - GCHostMember -> do - showUserJoinedGroup gName - setActive $ ActiveG gName - showToast ("#" <> gName) "you are connected to group" - GCInviteeMember -> do - showJoinedGroupMember gName m - setActive $ ActiveG gName - showToast ("#" <> gName) $ "member " <> localDisplayName (m :: GroupMember) <> " is connected" - intros <- withStore $ \st -> createIntroductions st group m - sendGroupMessage members . XGrpMemNew $ memberInfo m - forM_ intros $ \intro -> do - sendDirectMessage agentConnId . XGrpMemIntro . memberInfo $ reMember intro - withStore $ \st -> updateIntroStatus st intro GMIntroSent - _ -> do - -- TODO send probe and decide whether to use existing contact connection or the new contact connection - -- TODO notify member who forwarded introduction - question - where it is stored? There is via_contact but probably there should be via_member in group_members table - withStore (\st -> getViaGroupContact st user m) >>= \case - Nothing -> do - notifyMemberConnected gName m - messageError "implementation error: connected member does not have contact" - Just ct -> - when (contactIsReady ct) $ notifyMemberConnected gName m - MSG meta msgBody -> do - ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody - case chatMsgEvent of - XMsgNew (MsgContent MTText [] body) -> - newGroupTextMessage gName m meta $ find (isSimplexContentType XCText) body - XGrpMemNew memInfo@(MemberInfo memId _ _) -> do - group@Group {membership} <- withStore $ \st -> getGroup st user gName - when (memberId membership /= memId) $ - if isMember memId group - then messageError "x.grp.mem.new error: member already exists" - else do - newMember <- withStore $ \st -> createNewGroupMember st user group memInfo GCPostMember GSMemAnnounced - showJoinedGroupMemberConnecting gName m newMember - XGrpMemIntro memInfo@(MemberInfo memId _ _) -> - case memberCategory m of - GCHostMember -> do - group <- withStore $ \st -> getGroup st user gName - if isMember memId group - then messageWarning "x.grp.mem.intro ignored: member already exists" - else do - (groupConnId, groupQInfo) <- withAgent createConnection - (directConnId, directQInfo) <- withAgent createConnection - newMember <- withStore $ \st -> createIntroReMember st user group m memInfo groupConnId directConnId - let msg = XGrpMemInv memId IntroInvitation {groupQInfo, directQInfo} - sendDirectMessage agentConnId msg - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId newMember) GSMemIntroInvited - _ -> messageError "x.grp.mem.intro can be only sent by host member" - XGrpMemInv memId introInv -> - case memberCategory m of - GCInviteeMember -> do - group <- withStore $ \st -> getGroup st user gName - case find ((== memId) . memberId) $ members group of - Nothing -> messageError "x.grp.mem.inv error: referenced member does not exists" - Just reMember -> do - intro <- withStore $ \st -> saveIntroInvitation st reMember m introInv - case activeConn (reMember :: GroupMember) of - Nothing -> pure () -- this is not an error, introduction will be forwarded once the member is connected - Just Connection {agentConnId = reAgentConnId} -> do - sendDirectMessage reAgentConnId $ XGrpMemFwd (memberInfo m) introInv - withStore $ \st -> updateIntroStatus st intro GMIntroInvForwarded - _ -> messageError "x.grp.mem.inv can be only sent by invitee member" - XGrpMemFwd memInfo@(MemberInfo memId _ _) introInv@IntroInvitation {groupQInfo, directQInfo} -> do - group@Group {membership} <- withStore $ \st -> getGroup st user gName - toMember <- case find ((== memId) . memberId) $ members group of - -- TODO if the missed messages are correctly sent as soon as there is connection before anything else is sent - -- the situation when member does not exist is an error - -- member receiving x.grp.mem.fwd should have also received x.grp.mem.new prior to that. - -- For now, this branch compensates for the lack of delayed message delivery. - Nothing -> withStore $ \st -> createNewGroupMember st user group memInfo GCPostMember GSMemAnnounced - Just m' -> pure m' - withStore $ \st -> saveMemberInvitation st toMember introInv - let msg = XGrpMemInfo (memberId membership) profile - groupConnId <- withAgent $ \a -> joinConnection a groupQInfo $ directMessage msg - directConnId <- withAgent $ \a -> joinConnection a directQInfo $ directMessage msg - withStore $ \st -> createIntroToMemberContact st userId m toMember groupConnId directConnId - _ -> messageError $ "unsupported message: " <> T.pack (show chatMsgEvent) - _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMessage) - where - isMember :: MemberId -> Group -> Bool - isMember memId Group {membership, members} = - memberId membership == memId || isJust (find ((== memId) . memberId) members) + _ -> messageError $ "unexpected agent event: " <> T.pack (show agentMsg) - contactIsReady :: Contact -> Bool - contactIsReady Contact {activeConn} = connStatus activeConn == ConnReady - - memberIsReady :: GroupMember -> Bool - memberIsReady GroupMember {activeConn} = maybe False ((== ConnReady) . connStatus) activeConn + processGroupMessage :: ACommand 'Agent -> Connection -> GroupName -> GroupMember -> m () + processGroupMessage agentMsg conn gName m = case agentMsg of + CONF confId connInfo -> do + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case memberCategory m of + GCInviteeMember -> + case chatMsgEvent of + XGrpAcpt memId + | memId == memberId m -> do + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemAccepted + acceptAgentConnection conn confId XOk + | otherwise -> messageError "x.grp.acpt: memberId is different from expected" + _ -> messageError "CONF from invited member must have x.grp.acpt" + _ -> + case chatMsgEvent of + XGrpMemInfo memId _memProfile + | memId == memberId m -> do + -- TODO update member profile + Group {membership} <- withStore $ \st -> getGroup st user gName + acceptAgentConnection conn confId $ XGrpMemInfo (memberId membership) profile + | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" + _ -> messageError "CONF from member must have x.grp.mem.info" + INFO connInfo -> do + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case chatMsgEvent of + XGrpMemInfo memId _memProfile + | memId == memberId m -> do + -- TODO update member profile + pure () + | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" + XOk -> pure () + _ -> messageError "INFO from member must have x.grp.mem.info" + pure () + CON -> do + group@Group {members, membership} <- withStore $ \st -> getGroup st user gName + withStore $ \st -> do + updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected + updateGroupMemberStatus st userId (groupMemberId membership) GSMemConnected + -- TODO forward any pending (GMIntroInvReceived) introductions + case memberCategory m of + GCHostMember -> do + showUserJoinedGroup gName + setActive $ ActiveG gName + showToast ("#" <> gName) "you are connected to group" + GCInviteeMember -> do + showJoinedGroupMember gName m + setActive $ ActiveG gName + showToast ("#" <> gName) $ "member " <> localDisplayName (m :: GroupMember) <> " is connected" + intros <- withStore $ \st -> createIntroductions st group m + sendGroupMessage members . XGrpMemNew $ memberInfo m + forM_ intros $ \intro -> do + sendDirectMessage agentConnId . XGrpMemIntro . memberInfo $ reMember intro + withStore $ \st -> updateIntroStatus st intro GMIntroSent + _ -> do + -- TODO send probe and decide whether to use existing contact connection or the new contact connection + -- TODO notify member who forwarded introduction - question - where it is stored? There is via_contact but probably there should be via_member in group_members table + withStore (\st -> getViaGroupContact st user m) >>= \case + Nothing -> do + notifyMemberConnected gName m + messageError "implementation error: connected member does not have contact" + Just ct -> + when (contactIsReady ct) $ notifyMemberConnected gName m + MSG meta msgBody -> do + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody + case chatMsgEvent of + XMsgNew (MsgContent MTText [] body) -> + newGroupTextMessage gName m meta $ find (isSimplexContentType XCText) body + XGrpMemNew memInfo -> xGrpMemNew gName m memInfo + XGrpMemIntro memInfo -> xGrpMemIntro gName m memInfo + XGrpMemInv memId introInv -> xGrpMemInv gName m memId introInv + XGrpMemFwd memInfo introInv -> xGrpMemFwd gName m memInfo introInv + _ -> messageError $ "unsupported message: " <> T.pack (show chatMsgEvent) + _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMsg) notifyMemberConnected :: GroupName -> GroupMember -> m () notifyMemberConnected gName m@GroupMember {localDisplayName} = do @@ -444,7 +404,66 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do case chatMsgEvent of XInfo p -> withStore $ \st -> createDirectContact st userId activeConn p - _ -> pure () -- TODO show/log error, other events in SMP confirmation + -- TODO show/log error, other events in SMP confirmation + _ -> pure () + + xGrpMemNew :: GroupName -> GroupMember -> MemberInfo -> m () + xGrpMemNew gName m memInfo@(MemberInfo memId _ _) = do + group@Group {membership} <- withStore $ \st -> getGroup st user gName + when (memberId membership /= memId) $ + if isMember memId group + then messageError "x.grp.mem.new error: member already exists" + else do + newMember <- withStore $ \st -> createNewGroupMember st user group memInfo GCPostMember GSMemAnnounced + showJoinedGroupMemberConnecting gName m newMember + + xGrpMemIntro :: GroupName -> GroupMember -> MemberInfo -> m () + xGrpMemIntro gName m memInfo@(MemberInfo memId _ _) = + case memberCategory m of + GCHostMember -> do + group <- withStore $ \st -> getGroup st user gName + if isMember memId group + then messageWarning "x.grp.mem.intro ignored: member already exists" + else do + (groupConnId, groupQInfo) <- withAgent createConnection + (directConnId, directQInfo) <- withAgent createConnection + newMember <- withStore $ \st -> createIntroReMember st user group m memInfo groupConnId directConnId + let msg = XGrpMemInv memId IntroInvitation {groupQInfo, directQInfo} + sendDirectMessage agentConnId msg + withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId newMember) GSMemIntroInvited + _ -> messageError "x.grp.mem.intro can be only sent by host member" + + xGrpMemInv :: GroupName -> GroupMember -> MemberId -> IntroInvitation -> m () + xGrpMemInv gName m memId introInv = + case memberCategory m of + GCInviteeMember -> do + group <- withStore $ \st -> getGroup st user gName + case find ((== memId) . memberId) $ members group of + Nothing -> messageError "x.grp.mem.inv error: referenced member does not exists" + Just reMember -> do + intro <- withStore $ \st -> saveIntroInvitation st reMember m introInv + case activeConn (reMember :: GroupMember) of + Nothing -> pure () -- this is not an error, introduction will be forwarded once the member is connected + Just Connection {agentConnId = reAgentConnId} -> do + sendDirectMessage reAgentConnId $ XGrpMemFwd (memberInfo m) introInv + withStore $ \st -> updateIntroStatus st intro GMIntroInvForwarded + _ -> messageError "x.grp.mem.inv can be only sent by invitee member" + + xGrpMemFwd :: GroupName -> GroupMember -> MemberInfo -> IntroInvitation -> m () + xGrpMemFwd gName m memInfo@(MemberInfo memId _ _) introInv@IntroInvitation {groupQInfo, directQInfo} = do + group@Group {membership} <- withStore $ \st -> getGroup st user gName + toMember <- case find ((== memId) . memberId) $ members group of + -- TODO if the missed messages are correctly sent as soon as there is connection before anything else is sent + -- the situation when member does not exist is an error + -- member receiving x.grp.mem.fwd should have also received x.grp.mem.new prior to that. + -- For now, this branch compensates for the lack of delayed message delivery. + Nothing -> withStore $ \st -> createNewGroupMember st user group memInfo GCPostMember GSMemAnnounced + Just m' -> pure m' + withStore $ \st -> saveMemberInvitation st toMember introInv + let msg = XGrpMemInfo (memberId membership) profile + groupConnId <- withAgent $ \a -> joinConnection a groupQInfo $ directMessage msg + directConnId <- withAgent $ \a -> joinConnection a directQInfo $ directMessage msg + withStore $ \st -> createIntroToMemberContact st userId m toMember groupConnId directConnId sendDirectMessage :: ChatMonad m => ConnId -> ChatMsgEvent -> m () sendDirectMessage agentConnId chatMsgEvent = diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 401120a6d0..7ab92a3d74 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -29,8 +29,7 @@ import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow) data ChatDirection (p :: AParty) where - ReceivedDMConnection :: Connection -> ChatDirection 'Agent - ReceivedDMContact :: Contact -> ChatDirection 'Agent + ReceivedDirectMessage :: Connection -> Maybe Contact -> ChatDirection 'Agent SentDirectMessage :: Contact -> ChatDirection 'Client ReceivedGroupMessage :: Connection -> GroupName -> GroupMember -> ChatDirection 'Agent SentGroupMessage :: GroupName -> ChatDirection 'Client @@ -39,6 +38,11 @@ deriving instance Eq (ChatDirection p) deriving instance Show (ChatDirection p) +fromConnection :: ChatDirection 'Agent -> Connection +fromConnection = \case + ReceivedDirectMessage conn _ -> conn + ReceivedGroupMessage conn _ _ -> conn + data ChatMsgEvent = XMsgNew MsgContent | XInfo Profile diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index d8b183b1ef..98990911c9 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -292,9 +292,9 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = Nothing -> throwError $ SEInternal "group member without connection" Just groupMemberId -> uncurry (ReceivedGroupMessage c) <$> getGroupAndMember_ db groupMemberId c ConnContact -> - case entityId of - Nothing -> pure $ ReceivedDMConnection c - Just contactId -> ReceivedDMContact <$> getContact_ db contactId c + ReceivedDirectMessage c <$> case entityId of + Nothing -> pure Nothing + Just contactId -> Just <$> getContact_ db contactId c where getConnection_ :: DB.Connection -> ExceptT StoreError IO Connection getConnection_ db = ExceptT $ do diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index b37ace8793..70d360b784 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -75,15 +75,17 @@ testGroup = -- TODO this occasionally fails in case getWindow is run before the command above is printed alice <## "use /a team to add members" alice ##> "/a team bob" - alice <## "invitation to join the group #team sent to bob" - bob <## "use /j team to accept" + concurrently_ + (alice <## "invitation to join the group #team sent to bob") + (bob <## "use /j team to accept") bob ##> "/j team" concurrently_ (alice <## "#team: bob joined the group") (bob <## "#team: you joined the group") alice ##> "/a team cath" - alice <## "invitation to join the group #team sent to cath" - cath <## "use /j team to accept" + concurrently_ + (alice <## "invitation to join the group #team sent to cath") + (cath <## "use /j team to accept") cath ##> "/j team" concurrentlyN_ [ alice <## "#team: cath joined the group", @@ -123,11 +125,13 @@ testGroup2 = -- TODO this occasionally fails in case getWindow is run before the command above is printed alice <## "use /a club to add members" alice ##> "/a club bob" - alice <## "invitation to join the group #club sent to bob" - bob <## "use /j club to accept" + concurrently_ + (alice <## "invitation to join the group #club sent to bob") + (bob <## "use /j club to accept") alice ##> "/a club cath" - alice <## "invitation to join the group #club sent to cath" - cath <## "use /j club to accept" + concurrently_ + (alice <## "invitation to join the group #club sent to cath") + (cath <## "use /j club to accept") bob ##> "/j club" concurrently_ (alice <## "#club: bob joined the group") @@ -143,8 +147,9 @@ testGroup2 = bob <## "#club: new member cath is connected" ] bob ##> "/a club dan" - bob <## "invitation to join the group #club sent to dan" - dan <## "use /j club to accept" + concurrently_ + (bob <## "invitation to join the group #club sent to dan") + (dan <## "use /j club to accept") dan ##> "/j club" concurrentlyN_ [ bob <## "#club: dan joined the group", From cc4cb782090a86aac68da5107b8ed0f31d67dc2f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 25 Jul 2021 20:23:52 +0100 Subject: [PATCH 15/29] subscribe all user contacts and group members (#85) --- src/Simplex/Chat.hs | 23 +++++++++++++++++++- src/Simplex/Chat/Store.hs | 45 +++++++++++++++++++++++++++------------ src/Simplex/Chat/View.hs | 28 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 404f7eba0e..f954a02829 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -7,6 +7,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} module Simplex.Chat where @@ -23,7 +24,7 @@ import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.Functor (($>)) import Data.List (find) -import Data.Maybe (isJust) +import Data.Maybe (isJust, mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) @@ -200,12 +201,32 @@ processChatCommand user@User {userId, profile} = \case agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do q <- asks $ subQ . smpAgent + subscribeUserConnections forever $ do (_, connId, msg) <- atomically $ readTBQueue q user <- asks currentUser -- TODO handle errors properly void . runExceptT $ processAgentMessage user connId msg `catchError` (liftIO . print) +subscribeUserConnections :: (MonadUnliftIO m, MonadReader ChatController m) => m () +subscribeUserConnections = void . runExceptT $ do + user <- asks currentUser + subscribeContacts user + subscribeGroups user + where + subscribeContacts user = do + contacts <- withStore (`getUserContacts` user) + forM_ contacts $ \ct@Contact {localDisplayName = c} -> + (subscribe (contactConnId ct) >> showContactSubscribed c) `catchError` showContactSubError c + subscribeGroups user = do + groups <- filter (not . null . members) <$> withStore (`getUserGroups` user) + forM_ groups $ \Group {members, localDisplayName = g} -> do + let connectedMembers = mapMaybe (\m -> (m,) <$> memberConnId m) members + forM_ connectedMembers $ \(GroupMember {localDisplayName = c}, cId) -> + subscribe cId `catchError` showMemberSubError g c + showGroupSubscribed g + subscribe cId = withAgent (`subscribeConnection` cId) + processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () processAgentMessage user@User {userId, profile} agentConnId agentMessage = do chatDirection <- withStore $ \st -> getConnectionChatDirection st user agentConnId diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 98990911c9..fd576c0f30 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -23,12 +23,14 @@ module Simplex.Chat.Store createDirectContact, deleteContact, getContact, + getUserContacts, getContactConnections, getConnectionChatDirection, updateConnectionStatus, createNewGroup, createGroupInvitation, getGroup, + getUserGroups, getGroupInvitation, createContactGroupMember, createMemberConnection, @@ -53,6 +55,7 @@ import Control.Monad.IO.Unlift import Crypto.Random (ChaChaDRG, randomBytesGenerate) import qualified Data.ByteString.Base64 as B64 import Data.ByteString.Char8 (ByteString) +import Data.Either (rights) import Data.FileEmbed (embedDir, makeRelativeToProject) import Data.Function (on) import Data.Int (Int64) @@ -199,18 +202,20 @@ deleteContact st userId displayName = |] [":user_id" := userId, ":display_name" := displayName] +getContact :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m Contact +getContact st userId localDisplayName = + liftIOEither . withTransaction st $ \db -> runExceptT $ getContact_ db userId localDisplayName + -- TODO return the last connection that is ready, not any last connection -- requires updating connection status -getContact :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m Contact --- TODO merge contact and connection? -getContact st userId localDisplayName = - liftIOEither . withTransaction st $ \db -> runExceptT $ do - c@Contact {contactId} <- getContact_ db - activeConn <- getConnection_ db contactId - pure $ (c :: Contact) {activeConn} +getContact_ :: DB.Connection -> UserId -> ContactName -> ExceptT StoreError IO Contact +getContact_ db userId localDisplayName = do + c@Contact {contactId} <- getContactRec_ + activeConn <- getConnection_ contactId + pure $ (c :: Contact) {activeConn} where - getContact_ :: DB.Connection -> ExceptT StoreError IO Contact - getContact_ db = ExceptT $ do + getContactRec_ :: ExceptT StoreError IO Contact + getContactRec_ = ExceptT $ do toContact <$> DB.queryNamed db @@ -221,8 +226,8 @@ getContact st userId localDisplayName = WHERE c.user_id = :user_id AND c.local_display_name = :local_display_name AND c.is_user = :is_user |] [":user_id" := userId, ":local_display_name" := localDisplayName, ":is_user" := False] - getConnection_ :: DB.Connection -> Int64 -> ExceptT StoreError IO Connection - getConnection_ db contactId = ExceptT $ do + getConnection_ :: Int64 -> ExceptT StoreError IO Connection + getConnection_ contactId = ExceptT $ do connection <$> DB.queryNamed db @@ -244,6 +249,12 @@ getContact st userId localDisplayName = connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEContactNotReady localDisplayName +getUserContacts :: MonadUnliftIO m => SQLiteStore -> User -> m [Contact] +getUserContacts st User {userId} = + liftIO . withTransaction st $ \db -> do + contactNames <- liftIO $ map fromOnly <$> DB.query db "SELECT local_display_name FROM contacts WHERE user_id = ?" (Only userId) + rights <$> mapM (runExceptT . getContact_ db userId) contactNames + getContactConnections :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m [Connection] getContactConnections st userId displayName = liftIOEither . withTransaction st $ \db -> @@ -294,7 +305,7 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = ConnContact -> ReceivedDirectMessage c <$> case entityId of Nothing -> pure Nothing - Just contactId -> Just <$> getContact_ db contactId c + Just contactId -> Just <$> getContactRec_ db contactId c where getConnection_ :: DB.Connection -> ExceptT StoreError IO Connection getConnection_ db = ExceptT $ do @@ -311,8 +322,8 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = connection :: [ConnectionRow] -> Either StoreError Connection connection (connRow : _) = Right $ toConnection connRow connection _ = Left $ SEConnectionNotFound agentConnId - getContact_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO Contact - getContact_ db contactId c = ExceptT $ do + getContactRec_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO Contact + getContactRec_ db contactId c = ExceptT $ do toContact contactId c <$> DB.query db @@ -447,6 +458,12 @@ getGroup_ db User {userId, userContactId} localDisplayName = do [] -> Left SEGroupWithoutUser u : ms -> Right (b <> ms, u) +getUserGroups :: MonadUnliftIO m => SQLiteStore -> User -> m [Group] +getUserGroups st user = + liftIO . withTransaction st $ \db -> do + groupNames <- liftIO $ map fromOnly <$> DB.query db "SELECT local_display_name FROM groups WHERE user_id = ?" (Only $ userId user) + map fst . rights <$> mapM (runExceptT . getGroup_ db user) groupNames + getGroupInvitation :: StoreMonad m => SQLiteStore -> User -> GroupName -> m ReceivedGroupInvitation getGroupInvitation st user localDisplayName = liftIOEither . withTransaction st $ \db -> runExceptT $ do diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 16fbb3fca0..a2c1a497ff 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -12,6 +12,10 @@ module Simplex.Chat.View showContactDeleted, showContactConnected, showContactDisconnected, + showContactSubscribed, + showContactSubError, + showGroupSubscribed, + showMemberSubError, showReceivedMessage, showReceivedGroupMessage, showSentMessage, @@ -63,6 +67,18 @@ showContactConnected = printToView . contactConnected showContactDisconnected :: ChatReader m => ContactName -> m () showContactDisconnected = printToView . contactDisconnected +showContactSubscribed :: ChatReader m => ContactName -> m () +showContactSubscribed = printToView . contactSubscribed + +showContactSubError :: ChatReader m => ContactName -> ChatError -> m () +showContactSubError = printToView .: contactSubError + +showGroupSubscribed :: ChatReader m => GroupName -> m () +showGroupSubscribed = printToView . groupSubscribed + +showMemberSubError :: ChatReader m => GroupName -> ContactName -> ChatError -> m () +showMemberSubError = printToView .:. memberSubError + showReceivedMessage :: ChatReader m => ContactName -> UTCTime -> Text -> MsgIntegrity -> m () showReceivedMessage = showReceivedMessage_ . ttyFromContact @@ -120,6 +136,18 @@ contactConnected ct = [ttyFullContact ct <> " is connected"] contactDisconnected :: ContactName -> [StyledString] contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] +contactSubscribed :: ContactName -> [StyledString] +contactSubscribed c = [ttyContact c <> " is active"] + +contactSubError :: ContactName -> ChatError -> [StyledString] +contactSubError c e = ["contact " <> ttyContact c <> " error: " <> plain (show e)] + +groupSubscribed :: GroupName -> [StyledString] +groupSubscribed g = [ttyGroup g <> " is active"] + +memberSubError :: GroupName -> ContactName -> ChatError -> [StyledString] +memberSubError g c e = [ttyGroup g <> " member " <> ttyContact c <> " error: " <> plain (show e)] + groupCreated :: Group -> [StyledString] groupCreated g@Group {localDisplayName} = [ "group " <> ttyFullGroup g <> " is created", From b3af93e0ad83249c30b9ee7bc627673ebb9a16b9 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 27 Jul 2021 08:08:05 +0100 Subject: [PATCH 16/29] merge profiles using contact probe (#86) * chat commands to list members and to quit chat * merge profiles using probe * merge contacts connected to the same user based on successful profile probe * delete display name after merging contacts * probe: rename "existing" contacts to "matching" --- migrations/20210612_initial.sql | 26 ++++++ src/Simplex/Chat.hs | 65 +++++++++++++-- src/Simplex/Chat/Protocol.hs | 20 ++--- src/Simplex/Chat/Store.hs | 142 ++++++++++++++++++++++++++++++-- src/Simplex/Chat/View.hs | 34 ++++++++ tests/ChatTests.hs | 21 +++-- 6 files changed, 279 insertions(+), 29 deletions(-) diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index ed10094cbe..a04e7db707 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -5,6 +5,8 @@ CREATE TABLE contact_profiles ( -- remote user profile properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties ); +CREATE INDEX contact_profiles_index ON contact_profiles (display_name, full_name); + CREATE TABLE users ( user_id INTEGER PRIMARY KEY, contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE @@ -43,6 +45,30 @@ CREATE TABLE contacts ( UNIQUE (user_id, contact_profile_id) ); +CREATE TABLE sent_probes ( + sent_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE, + probe BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users, + UNIQUE (user_id, probe) +); + +CREATE TABLE sent_probe_hashes ( + sent_probe_hash_id INTEGER PRIMARY KEY, + sent_probe_id INTEGER NOT NULL REFERENCES sent_probes ON DELETE CASCADE, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users, + UNIQUE (sent_probe_id, contact_id) +); + +CREATE TABLE received_probes ( + received_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, + probe BLOB, + probe_hash BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users +); + CREATE TABLE known_servers( server_id INTEGER PRIMARY KEY, host TEXT NOT NULL, diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index f954a02829..d84e06de2e 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -43,9 +43,10 @@ import Simplex.Messaging.Agent import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..)) import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Client (smpDefaultConfig) +import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (raceAny_) -import System.Exit (exitFailure) +import System.Exit (exitFailure, exitSuccess) import System.IO (hFlush, stdout) import Text.Read (readMaybe) import UnliftIO.Async (race_) @@ -67,6 +68,7 @@ data ChatCommand | DeleteGroup GroupName | ListMembers GroupName | SendGroupMessage GroupName ByteString + | QuitChat deriving (Show) cfg :: AgentConfig @@ -186,7 +188,9 @@ processChatCommand user@User {userId, profile} = \case RemoveMember _gName _cName -> pure () LeaveGroup _gName -> pure () DeleteGroup _gName -> pure () - ListMembers _gName -> pure () + ListMembers gName -> do + group <- withStore $ \st -> getGroup st user gName + showGroupMembers group SendGroupMessage gName msg -> do -- TODO save sent messages -- TODO save pending message delivery for members without connections @@ -194,6 +198,7 @@ processChatCommand user@User {userId, profile} = \case let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] sendGroupMessage members msgEvent setActive $ ActiveG gName + QuitChat -> liftIO exitSuccess where isMember :: Contact -> [GroupMember] -> Bool isMember Contact {contactId} members = isJust $ find ((== Just contactId) . memberContactId) members @@ -272,6 +277,9 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do XMsgNew (MsgContent MTText [] body) -> newTextMessage c meta $ find (isSimplexContentType XCText) body XInfo _ -> pure () -- TODO profile update XGrpInv gInv -> processGroupInvitation ct gInv + XInfoProbe probe -> xInfoProbe ct probe + XInfoProbeCheck probeHash -> xInfoProbeCheck ct probeHash + XInfoProbeOk probe -> xInfoProbeOk ct probe _ -> pure () CONF confId connInfo -> do -- confirming direct connection with a member @@ -298,7 +306,9 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do setActive $ ActiveC c showToast (c <> "> ") "connected" Just (gName, m) -> - when (memberIsReady m) $ notifyMemberConnected gName m + when (memberIsReady m) $ do + notifyMemberConnected gName m + when (memberCategory m == GCPreMember) $ probeMatchingContacts ct END -> do showContactDisconnected c showToast (c <> "> ") "disconnected" @@ -342,7 +352,8 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do group@Group {members, membership} <- withStore $ \st -> getGroup st user gName withStore $ \st -> do updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected - updateGroupMemberStatus st userId (groupMemberId membership) GSMemConnected + unless (memberActive membership) $ + updateGroupMemberStatus st userId (groupMemberId membership) GSMemConnected -- TODO forward any pending (GMIntroInvReceived) introductions case memberCategory m of GCHostMember -> do @@ -366,7 +377,9 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do notifyMemberConnected gName m messageError "implementation error: connected member does not have contact" Just ct -> - when (contactIsReady ct) $ notifyMemberConnected gName m + when (contactIsReady ct) $ do + notifyMemberConnected gName m + when (memberCategory m == GCPreMember) $ probeMatchingContacts ct MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of @@ -385,6 +398,19 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do setActive $ ActiveG gName showToast ("#" <> gName) $ "member " <> localDisplayName <> " is connected" + probeMatchingContacts :: Contact -> m () + probeMatchingContacts ct = do + gVar <- asks idsDrg + (probe, probeId) <- withStore $ \st -> createSentProbe st gVar userId ct + sendDirectMessage (contactConnId ct) $ XInfoProbe probe + cs <- withStore (\st -> getMatchingContacts st userId ct) + let probeHash = C.sha256Hash probe + forM_ cs $ \c -> sendProbeHash c probeHash probeId `catchError` const (pure ()) + where + sendProbeHash c probeHash probeId = do + sendDirectMessage (contactConnId c) $ XInfoProbeCheck probeHash + withStore $ \st -> createSentProbeHash st userId probeId c + messageWarning :: Text -> m () messageWarning = liftIO . print @@ -416,6 +442,32 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do group <- withStore $ \st -> createGroupInvitation st user ct inv showReceivedGroupInvitation group localDisplayName memRole + xInfoProbe :: Contact -> ByteString -> m () + xInfoProbe c2 probe = do + r <- withStore $ \st -> matchReceivedProbe st userId c2 probe + forM_ r $ \c1 -> probeMatch c1 c2 probe + + xInfoProbeCheck :: Contact -> ByteString -> m () + xInfoProbeCheck c1 probeHash = do + r <- withStore $ \st -> matchReceivedProbeHash st userId c1 probeHash + forM_ r $ \(c2, probe) -> probeMatch c1 c2 probe + + probeMatch :: Contact -> Contact -> ByteString -> m () + probeMatch c1@Contact {profile = p1} c2@Contact {profile = p2} probe = + when (p1 == p2) $ do + sendDirectMessage (contactConnId c1) $ XInfoProbeOk probe + mergeContacts c1 c2 + + xInfoProbeOk :: Contact -> ByteString -> m () + xInfoProbeOk c1 probe = do + r <- withStore $ \st -> matchSentProbe st userId c1 probe + forM_ r $ \c2 -> mergeContacts c1 c2 + + mergeContacts :: Contact -> Contact -> m () + mergeContacts to from = do + withStore $ \st -> mergeContactRecords st userId to from + showContactsMerged to from + parseChatMessage :: ByteString -> Either ChatError ChatMessage parseChatMessage msgBody = first ChatErrorMessage (parseAll rawChatMessageP msgBody >>= toChatMessage) @@ -597,13 +649,14 @@ chatCommandP = <|> ("/join #" <|> "/join " <|> "/j #" <|> "/j ") *> (JoinGroup <$> displayName) <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> displayName <* A.space <*> displayName) <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> displayName) - <|> ("/members #" <|> "/ms #") *> (ListMembers <$> displayName) + <|> ("/members #" <|> "/members " <|> "/ms #" <|> "/ms ") *> (ListMembers <$> displayName) <|> A.char '#' *> (SendGroupMessage <$> displayName <* A.space <*> A.takeByteString) <|> ("/add" <|> "/a") $> AddContact <|> ("/connect " <|> "/c ") *> (Connect <$> smpQueueInfoP) <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> displayName) <|> A.char '@' *> (SendMessage <$> displayName <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp + <|> ("/quit" <|> "/q") $> QuitChat where displayName = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) refChar c = c > ' ' && c /= '#' && c /= '@' diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 7ab92a3d74..35d8805107 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -56,8 +56,8 @@ data ChatMsgEvent | XGrpMemCon MemberId | XGrpMemConAll MemberId | XInfoProbe ByteString - | XInfoProbeCheck MemberId ByteString - | XInfoProbeOk MemberId ByteString + | XInfoProbeCheck ByteString + | XInfoProbeOk ByteString | XOk deriving (Eq, Show) @@ -124,10 +124,10 @@ toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBod chatMsg . XGrpMemConAll =<< B64.decode memId ("x.info.probe", [probe]) -> do chatMsg . XInfoProbe =<< B64.decode probe - ("x.info.probe.check", [memId, probeHash]) -> do - chatMsg =<< (XInfoProbeCheck <$> B64.decode memId <*> B64.decode probeHash) - ("x.info.probe.ok", [memId, probe]) -> do - chatMsg =<< (XInfoProbeOk <$> B64.decode memId <*> B64.decode probe) + ("x.info.probe.check", [probeHash]) -> do + chatMsg =<< (XInfoProbeCheck <$> B64.decode probeHash) + ("x.info.probe.ok", [probe]) -> do + chatMsg =<< (XInfoProbeOk <$> B64.decode probe) ("x.ok", []) -> chatMsg XOk _ -> Left $ "bad syntax or unsupported event " <> B.unpack chatMsgEvent @@ -202,10 +202,10 @@ rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = rawMsg "x.grp.mem.con.all" [B64.encode memId] [] XInfoProbe probe -> rawMsg "x.info.probe" [B64.encode probe] [] - XInfoProbeCheck memId probeHash -> - rawMsg "x.info.probe.check" [B64.encode memId, B64.encode probeHash] [] - XInfoProbeOk memId probe -> - rawMsg "x.info.probe.ok" [B64.encode memId, B64.encode probe] [] + XInfoProbeCheck probeHash -> + rawMsg "x.info.probe.check" [B64.encode probeHash] [] + XInfoProbeOk probe -> + rawMsg "x.info.probe.ok" [B64.encode probe] [] XOk -> rawMsg "x.ok" [] [] where diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index fd576c0f30..041ac06460 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -44,6 +44,14 @@ module Simplex.Chat.Store saveMemberInvitation, getViaGroupMember, getViaGroupContact, + getMatchingContacts, + randomBytes, + createSentProbe, + createSentProbeHash, + matchReceivedProbe, + matchReceivedProbeHash, + matchSentProbe, + mergeContactRecords, ) where @@ -73,6 +81,7 @@ import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol (AParty (..), ConnId, SMPQueueInfo) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore, withTransaction) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) +import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Util (bshow, liftIOEither) import System.FilePath (takeBaseName, takeExtension) import UnliftIO.STM @@ -293,6 +302,126 @@ toMaybeConnection (Just connId, Just agentConnId, Just connLevel, viaContact, Ju Just $ toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) toMaybeConnection _ = Nothing +getMatchingContacts :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> m [Contact] +getMatchingContacts st userId Contact {contactId, profile = Profile {displayName, fullName}} = + liftIO . withTransaction st $ \db -> do + contactNames <- + map fromOnly + <$> DB.queryNamed + db + [sql| + SELECT ct.local_display_name + FROM contacts ct + JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id + WHERE ct.user_id = :user_id AND ct.contact_id != :contact_id + AND p.display_name = :display_name AND p.full_name = :full_name + |] + [ ":user_id" := userId, + ":contact_id" := contactId, + ":display_name" := displayName, + ":full_name" := fullName + ] + rights <$> mapM (runExceptT . getContact_ db userId) contactNames + +createSentProbe :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> UserId -> Contact -> m (ByteString, Int64) +createSentProbe st gVar userId _to@Contact {contactId} = + liftIOEither . withTransaction st $ \db -> + createWithRandomBytes 32 gVar $ \probe -> do + DB.execute db "INSERT INTO sent_probes (contact_id, probe, user_id) VALUES (?,?,?)" (contactId, probe, userId) + (probe,) <$> insertedRowId db + +createSentProbeHash :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> Contact -> m () +createSentProbeHash st userId probeId _to@Contact {contactId} = + liftIO . withTransaction st $ \db -> + DB.execute db "INSERT INTO sent_probe_hashes (sent_probe_id, contact_id, user_id) VALUES (?,?,?)" (probeId, contactId, userId) + +matchReceivedProbe :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> ByteString -> m (Maybe Contact) +matchReceivedProbe st userId _from@Contact {contactId} probe = + liftIO . withTransaction st $ \db -> do + let probeHash = C.sha256Hash probe + contactNames <- + map fromOnly + <$> DB.query + db + [sql| + SELECT c.local_display_name + FROM contacts c + JOIN received_probes r ON r.contact_id = c.contact_id + WHERE c.user_id = ? AND r.probe_hash = ? AND r.probe IS NULL + |] + (userId, probeHash) + DB.execute db "INSERT INTO received_probes (contact_id, probe, probe_hash, user_id) VALUES (?,?,?,?)" (contactId, probe, probeHash, userId) + case contactNames of + [] -> pure Nothing + cName : _ -> + either (const Nothing) Just + <$> runExceptT (getContact_ db userId cName) + +matchReceivedProbeHash :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> ByteString -> m (Maybe (Contact, ByteString)) +matchReceivedProbeHash st userId _from@Contact {contactId} probeHash = + liftIO . withTransaction st $ \db -> do + namesAndProbes <- + DB.query + db + [sql| + SELECT c.local_display_name, r.probe + FROM contacts c + JOIN received_probes r ON r.contact_id = c.contact_id + WHERE c.user_id = ? AND r.probe_hash = ? AND r.probe IS NOT NULL + |] + (userId, probeHash) + DB.execute db "INSERT INTO received_probes (contact_id, probe_hash, user_id) VALUES (?,?,?)" (contactId, probeHash, userId) + case namesAndProbes of + [] -> pure Nothing + (cName, probe) : _ -> + either (const Nothing) (Just . (,probe)) + <$> runExceptT (getContact_ db userId cName) + +matchSentProbe :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> ByteString -> m (Maybe Contact) +matchSentProbe st userId _from@Contact {contactId} probe = + liftIO . withTransaction st $ \db -> do + contactNames <- + map fromOnly + <$> DB.query + db + [sql| + SELECT c.local_display_name + FROM contacts c + JOIN sent_probes s ON s.contact_id = c.contact_id + JOIN sent_probe_hashes h ON h.sent_probe_id = s.sent_probe_id + WHERE c.user_id = ? AND s.probe = ? AND h.contact_id = ? + |] + (userId, probe, contactId) + case contactNames of + [] -> pure Nothing + cName : _ -> + either (const Nothing) Just + <$> runExceptT (getContact_ db userId cName) + +mergeContactRecords :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> Contact -> m () +mergeContactRecords st userId Contact {contactId = toContactId} Contact {contactId = fromContactId, localDisplayName} = + liftIO . withTransaction st $ \db -> do + DB.execute db "UPDATE connections SET contact_id = ? WHERE contact_id = ? AND user_id = ?" (toContactId, fromContactId, userId) + DB.execute db "UPDATE connections SET via_contact = ? WHERE via_contact = ? AND user_id = ?" (toContactId, fromContactId, userId) + DB.execute db "UPDATE group_members SET invited_by = ? WHERE invited_by = ? AND user_id = ?" (toContactId, fromContactId, userId) + DB.execute db "UPDATE messages SET contact_id = ? WHERE contact_id = ?" (toContactId, fromContactId) + DB.executeNamed + db + [sql| + UPDATE group_members + SET contact_id = :to_contact_id, + local_display_name = (SELECT local_display_name FROM contacts WHERE contact_id = :to_contact_id), + contact_profile_id = (SELECT contact_profile_id FROM contacts WHERE contact_id = :to_contact_id) + WHERE contact_id = :from_contact_id + AND user_id = :user_id + |] + [ ":to_contact_id" := toContactId, + ":from_contact_id" := fromContactId, + ":user_id" := userId + ] + DB.execute db "DELETE FROM contacts WHERE contact_id = ? AND user_id = ?" (fromContactId, userId) + DB.execute db "DELETE FROM display_names WHERE local_display_name = ? AND user_id = ?" (localDisplayName, userId) + getConnectionChatDirection :: StoreMonad m => SQLiteStore -> User -> ConnId -> m (ChatDirection 'Agent) getConnectionChatDirection st User {userId, userContactId} agentConnId = liftIOEither . withTransaction st $ \db -> runExceptT $ do @@ -377,7 +506,7 @@ createNewGroup st gVar user groupProfile = profileId <- insertedRowId db DB.execute db "INSERT INTO groups (local_display_name, user_id, group_profile_id) VALUES (?, ?, ?)" (displayName, uId, profileId) groupId <- insertedRowId db - memberId <- randomId gVar 12 + memberId <- randomBytes gVar 12 membership <- createContactMember_ db user groupId user (memberId, GROwner) GCUserMember GSMemCreator IBUser pure $ Right Group {groupId, localDisplayName = displayName, groupProfile, members = [], membership} @@ -865,20 +994,23 @@ withLocalDisplayName db userId displayName action = getLdnSuffix >>= (`tryCreate (ldn, displayName, ldnSuffix, userId) createWithRandomId :: forall a. TVar ChaChaDRG -> (ByteString -> IO a) -> IO (Either StoreError a) -createWithRandomId gVar create = tryCreate 3 +createWithRandomId = createWithRandomBytes 12 + +createWithRandomBytes :: forall a. Int -> TVar ChaChaDRG -> (ByteString -> IO a) -> IO (Either StoreError a) +createWithRandomBytes size gVar create = tryCreate 3 where tryCreate :: Int -> IO (Either StoreError a) tryCreate 0 = pure $ Left SEUniqueID tryCreate n = do - id' <- randomId gVar 12 + id' <- randomBytes gVar size E.try (create id') >>= \case Right x -> pure $ Right x Left e | DB.sqlError e == DB.ErrorConstraint -> tryCreate (n - 1) | otherwise -> pure . Left . SEInternal $ bshow e -randomId :: TVar ChaChaDRG -> Int -> IO ByteString -randomId gVar n = B64.encode <$> (atomically . stateTVar gVar $ randomBytesGenerate n) +randomBytes :: TVar ChaChaDRG -> Int -> IO ByteString +randomBytes gVar n = B64.encode <$> (atomically . stateTVar gVar $ randomBytesGenerate n) data StoreError = SEDuplicateName diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index a2c1a497ff..77322b8850 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -27,6 +27,8 @@ module Simplex.Chat.View showUserJoinedGroup, showJoinedGroupMemberConnecting, showConnectedToGroupMember, + showGroupMembers, + showContactsMerged, safeDecodeUtf8, ) where @@ -118,6 +120,12 @@ showJoinedGroupMemberConnecting = printToView .:. joinedGroupMemberConnecting showConnectedToGroupMember :: ChatReader m => GroupName -> GroupMember -> m () showConnectedToGroupMember = printToView .: connectedToGroupMember +showGroupMembers :: ChatReader m => Group -> m () +showGroupMembers = printToView . groupMembers + +showContactsMerged :: ChatReader m => Contact -> Contact -> m () +showContactsMerged = printToView .: contactsMerged + invitation :: SMPQueueInfo -> [StyledString] invitation qInfo = [ "pass this invitation to your contact (via another channel): ", @@ -181,6 +189,32 @@ connectedMember m = case memberCategory m of GCPostMember -> "new member " <> ttyMember m -- without fullName as as it was shown in joinedGroupMemberConnecting _ -> "member " <> ttyMember m -- these case is not used +groupMembers :: Group -> [StyledString] +groupMembers Group {membership, members} = map groupMember . filter (not . removedOrLeft) $ membership : members + where + removedOrLeft m = let s = memberStatus m in s == GSMemRemoved || s == GSMemLeft + groupMember m = ttyFullMember m <> ": " <> role m <> ", " <> category m <> status m + role = plain . serializeMemberRole . memberRole + category m = case memberCategory m of + GCUserMember -> "you, " + GCInviteeMember -> "invited, " + GCHostMember -> "host, " + _ -> "" + status m = case memberStatus m of + GSMemRemoved -> "removed" + GSMemLeft -> "left" + GSMemInvited -> "not yet joined" + GSMemConnected -> "connected" + GSMemComplete -> "connected" + GSMemCreator -> "created group" + _ -> "" + +contactsMerged :: Contact -> Contact -> [StyledString] +contactsMerged _to@Contact {localDisplayName = c1} _from@Contact {localDisplayName = c2} = + [ "contact " <> ttyContact c2 <> " is merged into " <> ttyContact c1, + "use " <> ttyToContact c1 <> highlight' "" <> " to send messages" + ] + receivedMessage :: StyledString -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage from utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 70d360b784..dce2c3f666 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -155,10 +155,15 @@ testGroup2 = [ bob <## "#club: dan joined the group", do dan <## "#club: you joined the group" - dan <### ["#club: member alice_1 (Alice) is connected", "#club: member cath (Catherine) is connected"], + dan + <### [ "#club: member alice_1 (Alice) is connected", + "#club: member cath (Catherine) is connected", + "use @alice to send messages" + ], do alice <## "#club: bob added dan_1 (Daniel) to the group (connecting...)" - alice <## "#club: new member dan_1 is connected", + alice <## "#club: new member dan_1 is connected" + alice <## "use @dan to send messages", do cath <## "#club: bob added dan (Daniel) to the group (connecting...)" cath <## "#club: new member dan is connected" @@ -167,7 +172,7 @@ testGroup2 = concurrentlyN_ [ bob <# "#club alice> hello", cath <# "#club alice> hello", - dan <# "#club alice_1> hello" + dan <# "#club alice> hello" ] bob #> "#club hi there" concurrentlyN_ @@ -183,7 +188,7 @@ testGroup2 = ] dan #> "#club how is it going?" concurrentlyN_ - [ alice <# "#club dan_1> how is it going?", + [ alice <# "#club dan> how is it going?", bob <# "#club dan> how is it going?", cath <# "#club dan> how is it going?" ] @@ -195,10 +200,10 @@ testGroup2 = cath <# "dan> hey cath" cath #> "@dan hey dan" dan <# "cath> hey dan" - dan #> "@alice_1 hi alice" - alice <# "dan_1> hi alice" - alice #> "@dan_1 hello dan" - dan <# "alice_1> hello dan" + dan #> "@alice hi alice" + alice <# "dan> hi alice" + alice #> "@dan hello dan" + dan <# "alice> hello dan" connectUsers :: TestCC -> TestCC -> IO () connectUsers cc1 cc2 = do From b798342c6197178fb0295809a5faf810a664454c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 2 Aug 2021 20:10:24 +0100 Subject: [PATCH 17/29] group commands (remove member, leave group, delete group) (#87) * remove group member * leave group, fix remove member, tests for leave group/remove member * delete group with test * prevent contact deletion error when it is a group member * support inviting the group member who left or was removed * use small retry interval in the tests * test multiline outputs --- .github/workflows/build.yml | 1 + apps/simplex-chat/Main.hs | 2 +- migrations/20210612_initial.sql | 4 +- src/Simplex/Chat.hs | 202 +++++++++++++++++------- src/Simplex/Chat/Controller.hs | 2 + src/Simplex/Chat/Help.hs | 2 +- src/Simplex/Chat/Protocol.hs | 15 ++ src/Simplex/Chat/Store.hs | 43 ++++- src/Simplex/Chat/Types.hs | 5 + src/Simplex/Chat/View.hs | 108 +++++++++++-- src/Simplex/Chat/protocol.md | 8 +- stack.yaml | 2 +- tests/ChatClient.hs | 13 +- tests/ChatTests.hs | 268 +++++++++++++++++++++++++------- 14 files changed, 537 insertions(+), 138 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd30dbb1db..336656be7c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - v4 tags: - "v*" pull_request: diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index e618a0c3de..952f77fd10 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -14,7 +14,7 @@ main :: IO () main = do opts <- welcomeGetOpts t <- withTerminal pure - simplexChat opts t + simplexChat defaultChatConfig opts t welcomeGetOpts :: IO ChatOpts welcomeGetOpts = do diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index a04e7db707..86abb601c6 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -117,9 +117,7 @@ CREATE TABLE group_members ( -- group members, excluding the local user REFERENCES display_names (user_id, local_display_name) ON DELETE RESTRICT ON UPDATE CASCADE, - UNIQUE (group_id, member_id), - UNIQUE (group_id, contact_id), - UNIQUE (group_id, local_display_name) + UNIQUE (group_id, member_id) ); CREATE TABLE group_member_intros ( diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index d84e06de2e..ef5e75176c 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -28,6 +28,7 @@ import Data.Maybe (isJust, mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) +import Numeric.Natural import Simplex.Chat.Controller import Simplex.Chat.Help import Simplex.Chat.Input @@ -40,9 +41,8 @@ import Simplex.Chat.Terminal import Simplex.Chat.Types import Simplex.Chat.View import Simplex.Messaging.Agent -import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..)) +import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), defaultAgentConfig) import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Client (smpDefaultConfig) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (raceAny_) @@ -71,39 +71,46 @@ data ChatCommand | QuitChat deriving (Show) -cfg :: AgentConfig -cfg = - AgentConfig - { tcpPort = undefined, -- agent does not listen to TCP - smpServers = undefined, -- filled in from options - rsaKeySize = 2048 `div` 8, - connIdBytes = 12, - tbqSize = 16, - dbFile = undefined, -- filled in from options +data ChatConfig = ChatConfig + { agentConfig :: AgentConfig, + dbPoolSize :: Int, + tbqSize :: Natural + } + +defaultChatConfig :: ChatConfig +defaultChatConfig = + ChatConfig + { agentConfig = + defaultAgentConfig + { tcpPort = undefined, -- agent does not listen to TCP + smpServers = undefined, -- filled in from options + dbFile = undefined, -- filled in from options + dbPoolSize = 1 + }, dbPoolSize = 1, - smpCfg = smpDefaultConfig + tbqSize = 16 } logCfg :: LogConfig logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} -simplexChat :: WithTerminal t => ChatOpts -> t -> IO () -simplexChat opts t = +simplexChat :: WithTerminal t => ChatConfig -> ChatOpts -> t -> IO () +simplexChat cfg opts t = -- setLogLevel LogInfo -- LogError -- withGlobalLogging logCfg $ do initializeNotifications - >>= newChatController opts t + >>= newChatController cfg opts t >>= runSimplexChat -newChatController :: WithTerminal t => ChatOpts -> t -> (Notification -> IO ()) -> IO ChatController -newChatController ChatOpts {dbFile, smpServers} t sendNotification = do - chatStore <- createStore (dbFile <> ".chat.db") 1 +newChatController :: WithTerminal t => ChatConfig -> ChatOpts -> t -> (Notification -> IO ()) -> IO ChatController +newChatController ChatConfig {agentConfig = cfg, dbPoolSize, tbqSize} ChatOpts {dbFile, smpServers} t sendNotification = do + chatStore <- createStore (dbFile <> ".chat.db") dbPoolSize currentUser <- getCreateActiveUser chatStore chatTerminal <- newChatTerminal t smpAgent <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} idsDrg <- newTVarIO =<< drgNew - inputQ <- newTBQueueIO $ tbqSize cfg - notifyQ <- newTBQueueIO $ tbqSize cfg + inputQ <- newTBQueueIO tbqSize + notifyQ <- newTBQueueIO tbqSize pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, idsDrg, inputQ, notifyQ, sendNotification} runSimplexChat :: ChatController -> IO () @@ -145,13 +152,16 @@ processChatCommand user@User {userId, profile} = \case Connect qInfo -> do connId <- withAgent $ \a -> joinConnection a qInfo . directMessage $ XInfo profile withStore $ \st -> createDirectConnection st userId connId - DeleteContact cName -> do - conns <- withStore $ \st -> getContactConnections st userId cName - withAgent $ \a -> forM_ conns $ \Connection {agentConnId} -> - deleteConnection a agentConnId `catchError` \(_ :: AgentErrorType) -> pure () - withStore $ \st -> deleteContact st userId cName - unsetActive $ ActiveC cName - showContactDeleted cName + DeleteContact cName -> + withStore (\st -> getContactGroupNames st userId cName) >>= \case + [] -> do + conns <- withStore $ \st -> getContactConnections st userId cName + withAgent $ \a -> forM_ conns $ \Connection {agentConnId} -> + deleteConnection a agentConnId `catchError` \(_ :: AgentErrorType) -> pure () + withStore $ \st -> deleteContact st userId cName + unsetActive $ ActiveC cName + showContactDeleted cName + gs -> showContactGroups cName gs SendMessage cName msg -> do contact <- withStore $ \st -> getContact st userId cName let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] @@ -166,42 +176,70 @@ processChatCommand user@User {userId, profile} = \case let Group {groupId, groupProfile, membership, members} = group userRole = memberRole membership userMemberId = memberId membership - when (userRole < GRAdmin || userRole < memRole) $ throwError $ ChatError CEGroupUserRole - when (isMember contact members) . throwError . ChatError $ CEGroupDuplicateMember cName - when (memberStatus membership == GSMemInvited) . throwError . ChatError $ CEGroupNotJoined gName - unless (memberActive membership) . throwError . ChatError $ CEGroupMemberNotActive + when (userRole < GRAdmin || userRole < memRole) $ chatError CEGroupUserRole + when (memberStatus membership == GSMemInvited) $ chatError (CEGroupNotJoined gName) + unless (memberActive membership) $ chatError CEGroupMemberNotActive + when (isJust $ contactMember contact members) $ chatError (CEGroupDuplicateMember cName) gVar <- asks idsDrg (agentConnId, qInfo) <- withAgent createConnection GroupMember {memberId} <- withStore $ \st -> createContactGroupMember st gVar user groupId contact memRole agentConnId let msg = XGrpInv $ GroupInvitation (userMemberId, userRole) (memberId, memRole) qInfo groupProfile sendDirectMessage (contactConnId contact) msg - showSentGroupInvitation group cName + showSentGroupInvitation gName cName setActive $ ActiveG gName JoinGroup gName -> do ReceivedGroupInvitation {fromMember, userMember, queueInfo} <- withStore $ \st -> getGroupInvitation st user gName agentConnId <- withAgent $ \a -> joinConnection a queueInfo . directMessage . XGrpAcpt $ memberId userMember withStore $ \st -> do - createMemberConnection st userId (groupMemberId fromMember) agentConnId - updateGroupMemberStatus st userId (groupMemberId fromMember) GSMemAccepted - updateGroupMemberStatus st userId (groupMemberId userMember) GSMemAccepted + createMemberConnection st userId fromMember agentConnId + updateGroupMemberStatus st userId fromMember GSMemAccepted + updateGroupMemberStatus st userId userMember GSMemAccepted MemberRole _gName _cName _mRole -> pure () - RemoveMember _gName _cName -> pure () - LeaveGroup _gName -> pure () - DeleteGroup _gName -> pure () + RemoveMember gName cName -> do + Group {membership, members} <- withStore $ \st -> getGroup st user gName + case find ((== cName) . (localDisplayName :: GroupMember -> ContactName)) members of + Nothing -> chatError $ CEGroupMemberNotFound cName + Just member -> do + let userRole = memberRole membership + when (userRole < GRAdmin || userRole < memberRole member) $ chatError CEGroupUserRole + sendGroupMessage members . XGrpMemDel $ memberId member + deleteMemberConnection member + withStore $ \st -> updateGroupMemberStatus st userId member GSMemRemoved + showDeletedMember gName Nothing (Just member) + LeaveGroup gName -> do + Group {membership, members} <- withStore $ \st -> getGroup st user gName + sendGroupMessage members XGrpLeave + mapM_ deleteMemberConnection members + withStore $ \st -> updateGroupMemberStatus st userId membership GSMemLeft + showLeftMemberUser gName + DeleteGroup gName -> do + g@Group {membership, members} <- withStore $ \st -> getGroup st user gName + let s = memberStatus membership + canDelete = + memberRole membership == GROwner + || (s == GSMemRemoved || s == GSMemLeft || s == GSMemGroupDeleted) + unless canDelete $ chatError CEGroupUserRole + when (memberActive membership) $ sendGroupMessage members XGrpDel + mapM_ deleteMemberConnection members + withStore $ \st -> deleteGroup st user g + showGroupDeletedUser gName ListMembers gName -> do group <- withStore $ \st -> getGroup st user gName showGroupMembers group SendGroupMessage gName msg -> do -- TODO save sent messages -- TODO save pending message delivery for members without connections - Group {members} <- withStore $ \st -> getGroup st user gName + Group {members, membership} <- withStore $ \st -> getGroup st user gName + unless (memberActive membership) $ chatError CEGroupMemberUserRemoved let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] sendGroupMessage members msgEvent setActive $ ActiveG gName QuitChat -> liftIO exitSuccess where - isMember :: Contact -> [GroupMember] -> Bool - isMember Contact {contactId} members = isJust $ find ((== Just contactId) . memberContactId) members + contactMember :: Contact -> [GroupMember] -> Maybe GroupMember + contactMember Contact {contactId} = + find $ \GroupMember {memberContactId = cId, memberStatus = s} -> + cId == Just contactId && s /= GSMemRemoved && s /= GSMemLeft agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do @@ -224,12 +262,18 @@ subscribeUserConnections = void . runExceptT $ do forM_ contacts $ \ct@Contact {localDisplayName = c} -> (subscribe (contactConnId ct) >> showContactSubscribed c) `catchError` showContactSubError c subscribeGroups user = do - groups <- filter (not . null . members) <$> withStore (`getUserGroups` user) - forM_ groups $ \Group {members, localDisplayName = g} -> do + groups <- withStore (`getUserGroups` user) + forM_ groups $ \Group {members, membership, localDisplayName = g} -> do let connectedMembers = mapMaybe (\m -> (m,) <$> memberConnId m) members - forM_ connectedMembers $ \(GroupMember {localDisplayName = c}, cId) -> - subscribe cId `catchError` showMemberSubError g c - showGroupSubscribed g + if null connectedMembers + then + if memberActive membership + then showGroupEmpty g + else showGroupRemoved g + else do + forM_ connectedMembers $ \(GroupMember {localDisplayName = c}, cId) -> + subscribe cId `catchError` showMemberSubError g c + showGroupSubscribed g subscribe cId = withAgent (`subscribeConnection` cId) processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () @@ -324,7 +368,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do case chatMsgEvent of XGrpAcpt memId | memId == memberId m -> do - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId m) GSMemAccepted + withStore $ \st -> updateGroupMemberStatus st userId m GSMemAccepted acceptAgentConnection conn confId XOk | otherwise -> messageError "x.grp.acpt: memberId is different from expected" _ -> messageError "CONF from invited member must have x.grp.acpt" @@ -351,9 +395,9 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do CON -> do group@Group {members, membership} <- withStore $ \st -> getGroup st user gName withStore $ \st -> do - updateGroupMemberStatus st userId (groupMemberId m) GSMemConnected + updateGroupMemberStatus st userId m GSMemConnected unless (memberActive membership) $ - updateGroupMemberStatus st userId (groupMemberId membership) GSMemConnected + updateGroupMemberStatus st userId membership GSMemConnected -- TODO forward any pending (GMIntroInvReceived) introductions case memberCategory m of GCHostMember -> do @@ -389,6 +433,9 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do XGrpMemIntro memInfo -> xGrpMemIntro gName m memInfo XGrpMemInv memId introInv -> xGrpMemInv gName m memId introInv XGrpMemFwd memInfo introInv -> xGrpMemFwd gName m memInfo introInv + XGrpMemDel memId -> xGrpMemDel gName m memId + XGrpLeave -> xGrpLeave gName m + XGrpDel -> xGrpDel gName m _ -> messageError $ "unsupported message: " <> T.pack (show chatMsgEvent) _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMsg) @@ -437,8 +484,8 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do processGroupInvitation :: Contact -> GroupInvitation -> m () processGroupInvitation ct@Contact {localDisplayName} inv@(GroupInvitation (fromMemId, fromRole) (memId, memRole) _ _) = do - when (fromRole < GRAdmin || fromRole < memRole) . throwError . ChatError $ CEGroupContactRole localDisplayName - when (fromMemId == memId) $ throwError $ ChatError CEGroupDuplicateMemberId + when (fromRole < GRAdmin || fromRole < memRole) $ chatError (CEGroupContactRole localDisplayName) + when (fromMemId == memId) $ chatError CEGroupDuplicateMemberId group <- withStore $ \st -> createGroupInvitation st user ct inv showReceivedGroupInvitation group localDisplayName memRole @@ -450,7 +497,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do xInfoProbeCheck :: Contact -> ByteString -> m () xInfoProbeCheck c1 probeHash = do r <- withStore $ \st -> matchReceivedProbeHash st userId c1 probeHash - forM_ r $ \(c2, probe) -> probeMatch c1 c2 probe + forM_ r . uncurry $ probeMatch c1 probeMatch :: Contact -> Contact -> ByteString -> m () probeMatch c1@Contact {profile = p1} c2@Contact {profile = p2} probe = @@ -503,7 +550,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do newMember <- withStore $ \st -> createIntroReMember st user group m memInfo groupConnId directConnId let msg = XGrpMemInv memId IntroInvitation {groupQInfo, directQInfo} sendDirectMessage agentConnId msg - withStore $ \st -> updateGroupMemberStatus st userId (groupMemberId newMember) GSMemIntroInvited + withStore $ \st -> updateGroupMemberStatus st userId newMember GSMemIntroInvited _ -> messageError "x.grp.mem.intro can be only sent by host member" xGrpMemInv :: GroupName -> GroupMember -> MemberId -> IntroInvitation -> m () @@ -538,6 +585,50 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do directConnId <- withAgent $ \a -> joinConnection a directQInfo $ directMessage msg withStore $ \st -> createIntroToMemberContact st userId m toMember groupConnId directConnId + xGrpMemDel :: GroupName -> GroupMember -> MemberId -> m () + xGrpMemDel gName m memId = do + Group {membership, members} <- withStore $ \st -> getGroup st user gName + if memberId membership == memId + then do + mapM_ deleteMemberConnection members + withStore $ \st -> updateGroupMemberStatus st userId membership GSMemRemoved + showDeletedMemberUser gName m + else case find ((== memId) . memberId) members of + Nothing -> messageError "x.grp.mem.del with unknown member ID" + Just member -> do + let mRole = memberRole m + if mRole < GRAdmin || mRole < memberRole member + then messageError "x.grp.mem.del with insufficient member permissions" + else do + deleteMemberConnection member + withStore $ \st -> updateGroupMemberStatus st userId member GSMemRemoved + showDeletedMember gName (Just m) (Just member) + + xGrpLeave :: GroupName -> GroupMember -> m () + xGrpLeave gName m = do + deleteMemberConnection m + withStore $ \st -> updateGroupMemberStatus st userId m GSMemLeft + showLeftMember gName m + + xGrpDel :: GroupName -> GroupMember -> m () + xGrpDel gName m = do + when (memberRole m /= GROwner) $ chatError CEGroupUserRole + ms <- withStore $ \st -> do + Group {members, membership} <- getGroup st user gName + updateGroupMemberStatus st userId membership GSMemGroupDeleted + pure members + mapM_ deleteMemberConnection ms + showGroupDeleted gName m + +chatError :: ChatMonad m => ChatErrorType -> m () +chatError = throwError . ChatError + +deleteMemberConnection :: ChatMonad m => GroupMember -> m () +deleteMemberConnection m = do + User {userId} <- asks currentUser + withAgent $ forM_ (memberConnId m) . deleteConnection + withStore $ \st -> deleteGroupMemberConnection st userId m + sendDirectMessage :: ChatMonad m => ConnId -> ChatMsgEvent -> m () sendDirectMessage agentConnId chatMsgEvent = void . withAgent $ \a -> sendMessage a agentConnId $ directMessage chatMsgEvent @@ -647,12 +738,13 @@ chatCommandP = <|> ("/group #" <|> "/group " <|> "/g #" <|> "/g ") *> (NewGroup <$> groupProfile) <|> ("/add #" <|> "/add " <|> "/a #" <|> "/a ") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) <|> ("/join #" <|> "/join " <|> "/j #" <|> "/j ") *> (JoinGroup <$> displayName) - <|> ("/remove #" <|> "/rm #") *> (RemoveMember <$> displayName <* A.space <*> displayName) + <|> ("/remove #" <|> "/remove " <|> "/rm #" <|> "/rm ") *> (RemoveMember <$> displayName <* A.space <*> displayName) + <|> ("/leave #" <|> "/leave " <|> "/l #" <|> "/l ") *> (LeaveGroup <$> displayName) <|> ("/delete #" <|> "/d #") *> (DeleteGroup <$> displayName) <|> ("/members #" <|> "/members " <|> "/ms #" <|> "/ms ") *> (ListMembers <$> displayName) <|> A.char '#' *> (SendGroupMessage <$> displayName <* A.space <*> A.takeByteString) - <|> ("/add" <|> "/a") $> AddContact <|> ("/connect " <|> "/c ") *> (Connect <$> smpQueueInfoP) + <|> ("/connect" <|> "/c") $> AddContact <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> displayName) <|> A.char '@' *> (SendMessage <$> displayName <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 813b3cf8eb..d07331ed6c 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -47,6 +47,8 @@ data ChatErrorType | CEGroupDuplicateMemberId | CEGroupNotJoined GroupName | CEGroupMemberNotActive + | CEGroupMemberUserRemoved + | CEGroupMemberNotFound ContactName | CEGroupInternal String deriving (Show, Exception) diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index d120864c36..997931e3fe 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -14,7 +14,7 @@ chatHelpInfo = [ highlight "Using Simplex chat prototype.", "Follow these steps to set up a connection:", "", - Markdown (Colored Green) "Step 1: " <> highlight "/add" <> " -- Alice adds a contact.", + Markdown (Colored Green) "Step 1: " <> highlight "/connect" <> " -- Alice adds a contact.", indent <> "Alice should send the invitation printed by the /add command", indent <> "to her contact, Bob, out-of-band, via any trusted channel.", "", diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 35d8805107..ee79c40aa7 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -55,6 +55,9 @@ data ChatMsgEvent | XGrpMemInfo MemberId Profile | XGrpMemCon MemberId | XGrpMemConAll MemberId + | XGrpMemDel MemberId + | XGrpLeave + | XGrpDel | XInfoProbe ByteString | XInfoProbeCheck ByteString | XInfoProbeOk ByteString @@ -122,6 +125,12 @@ toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBod chatMsg . XGrpMemCon =<< B64.decode memId ("x.grp.mem.con.all", [memId]) -> chatMsg . XGrpMemConAll =<< B64.decode memId + ("x.grp.mem.del", [memId]) -> + chatMsg . XGrpMemDel =<< B64.decode memId + ("x.grp.leave", []) -> + chatMsg XGrpLeave + ("x.grp.del", []) -> + chatMsg XGrpDel ("x.info.probe", [probe]) -> do chatMsg . XInfoProbe =<< B64.decode probe ("x.info.probe.check", [probeHash]) -> do @@ -200,6 +209,12 @@ rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = rawMsg "x.grp.mem.con" [B64.encode memId] [] XGrpMemConAll memId -> rawMsg "x.grp.mem.con.all" [B64.encode memId] [] + XGrpMemDel memId -> + rawMsg "x.grp.mem.del" [B64.encode memId] [] + XGrpLeave -> + rawMsg "x.grp.leave" [] [] + XGrpDel -> + rawMsg "x.grp.del" [] [] XInfoProbe probe -> rawMsg "x.info.probe" [B64.encode probe] [] XInfoProbeCheck probeHash -> diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 041ac06460..607e07ba1e 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -21,6 +21,7 @@ module Simplex.Chat.Store setActiveUser, createDirectConnection, createDirectContact, + getContactGroupNames, deleteContact, getContact, getUserContacts, @@ -30,12 +31,14 @@ module Simplex.Chat.Store createNewGroup, createGroupInvitation, getGroup, + deleteGroup, getUserGroups, getGroupInvitation, createContactGroupMember, createMemberConnection, updateGroupMemberStatus, createNewGroupMember, + deleteGroupMemberConnection, createIntroductions, updateIntroStatus, saveIntroInvitation, @@ -182,6 +185,20 @@ createContact_ db userId connId Profile {displayName, fullName} viaGroup = DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, connId) pure (ldn, contactId, profileId) +getContactGroupNames :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactName -> m [GroupName] +getContactGroupNames st userId displayName = + liftIO . withTransaction st $ \db -> do + map fromOnly + <$> DB.query + db + [sql| + SELECT DISTINCT g.local_display_name + FROM groups g + JOIN group_members m ON m.group_id = g.group_id + WHERE g.user_id = ? AND m.local_display_name = ? + |] + (userId, displayName) + deleteContact :: MonadUnliftIO m => SQLiteStore -> UserId -> ContactName -> m () deleteContact st userId displayName = liftIO . withTransaction st $ \db -> do @@ -587,6 +604,14 @@ getGroup_ db User {userId, userContactId} localDisplayName = do [] -> Left SEGroupWithoutUser u : ms -> Right (b <> ms, u) +deleteGroup :: MonadUnliftIO m => SQLiteStore -> User -> Group -> m () +deleteGroup st User {userId} Group {groupId, members, localDisplayName} = + liftIO . withTransaction st $ \db -> do + forM_ members $ \m -> DB.execute db "DELETE FROM connections WHERE user_id = ? AND group_member_id = ?" (userId, groupMemberId m) + DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_id = ?" (userId, groupId) + DB.execute db "DELETE FROM groups WHERE user_id = ? AND group_id = ?" (userId, groupId) + DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName) + getUserGroups :: MonadUnliftIO m => SQLiteStore -> User -> m [Group] getUserGroups st user = liftIO . withTransaction st $ \db -> do @@ -624,13 +649,13 @@ createContactGroupMember st gVar user groupId contact memberRole agentConnId = void $ createMemberConnection_ db (userId user) groupMemberId agentConnId Nothing 0 pure member -createMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> ConnId -> m () -createMemberConnection st userId groupMemberId agentConnId = +createMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> GroupMember -> ConnId -> m () +createMemberConnection st userId GroupMember {groupMemberId} agentConnId = liftIO . withTransaction st $ \db -> void $ createMemberConnection_ db userId groupMemberId agentConnId Nothing 0 -updateGroupMemberStatus :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> GroupMemberStatus -> m () -updateGroupMemberStatus st userId groupMemberId memberStatus = +updateGroupMemberStatus :: MonadUnliftIO m => SQLiteStore -> UserId -> GroupMember -> GroupMemberStatus -> m () +updateGroupMemberStatus st userId GroupMember {groupMemberId} memStatus = liftIO . withTransaction st $ \db -> DB.executeNamed db @@ -641,7 +666,7 @@ updateGroupMemberStatus st userId groupMemberId memberStatus = |] [ ":user_id" := userId, ":group_member_id" := groupMemberId, - ":member_status" := memberStatus + ":member_status" := memStatus ] -- | add new member with profile @@ -701,6 +726,14 @@ createNewMember_ activeConn = Nothing } +deleteGroupMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> GroupMember -> m () +deleteGroupMemberConnection st userId m = + liftIO . withTransaction st $ \db -> deleteGroupMemberConnection_ db userId m + +deleteGroupMemberConnection_ :: DB.Connection -> UserId -> GroupMember -> IO () +deleteGroupMemberConnection_ db userId GroupMember {groupMemberId} = + DB.execute db "DELETE FROM connections WHERE user_id = ? AND group_member_id = ?" (userId, groupMemberId) + createIntroductions :: MonadUnliftIO m => SQLiteStore -> Group -> GroupMember -> m [GroupMemberIntro] createIntroductions st Group {members} toMember = do let reMembers = filter (\m -> memberCurrent m && groupMemberId m /= groupMemberId toMember) members diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 95257a94d2..ba00415101 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -227,6 +227,7 @@ serializeMemberCategory = \case data GroupMemberStatus = GSMemRemoved -- member who was removed from the group | GSMemLeft -- member who left the group + | GSMemGroupDeleted -- user member of the deleted group | GSMemInvited -- member is sent to or received invitation to join the group | GSMemIntroduced -- user received x.grp.mem.intro for this member (only with GCPreMember) | GSMemIntroInvited -- member is sent to or received from intro invitation @@ -245,6 +246,7 @@ memberActive :: GroupMember -> Bool memberActive m = case memberStatus m of GSMemRemoved -> False GSMemLeft -> False + GSMemGroupDeleted -> False GSMemInvited -> False GSMemIntroduced -> False GSMemIntroInvited -> False @@ -258,6 +260,7 @@ memberCurrent :: GroupMember -> Bool memberCurrent m = case memberStatus m of GSMemRemoved -> False GSMemLeft -> False + GSMemGroupDeleted -> False GSMemInvited -> False GSMemIntroduced -> True GSMemIntroInvited -> True @@ -271,6 +274,7 @@ memberStatusT :: Text -> Maybe GroupMemberStatus memberStatusT = \case "removed" -> Just GSMemRemoved "left" -> Just GSMemLeft + "deleted" -> Just GSMemGroupDeleted "invited" -> Just GSMemInvited "introduced" -> Just GSMemIntroduced "intro-inv" -> Just GSMemIntroInvited @@ -285,6 +289,7 @@ serializeMemberStatus :: GroupMemberStatus -> Text serializeMemberStatus = \case GSMemRemoved -> "removed" GSMemLeft -> "left" + GSMemGroupDeleted -> "deleted" GSMemInvited -> "invited" GSMemIntroduced -> "introduced" GSMemIntroInvited -> "intro-inv" diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 77322b8850..f54a0936f0 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -10,23 +10,32 @@ module Simplex.Chat.View showInvitation, showChatError, showContactDeleted, + showContactGroups, showContactConnected, showContactDisconnected, showContactSubscribed, showContactSubError, showGroupSubscribed, + showGroupEmpty, + showGroupRemoved, showMemberSubError, showReceivedMessage, showReceivedGroupMessage, showSentMessage, showSentGroupMessage, showGroupCreated, + showGroupDeletedUser, + showGroupDeleted, showSentGroupInvitation, showReceivedGroupInvitation, showJoinedGroupMember, showUserJoinedGroup, showJoinedGroupMemberConnecting, showConnectedToGroupMember, + showDeletedMember, + showDeletedMemberUser, + showLeftMemberUser, + showLeftMember, showGroupMembers, showContactsMerged, safeDecodeUtf8, @@ -63,6 +72,9 @@ showChatError = printToView . chatError showContactDeleted :: ChatReader m => ContactName -> m () showContactDeleted = printToView . contactDeleted +showContactGroups :: ChatReader m => ContactName -> [GroupName] -> m () +showContactGroups = printToView .: contactGroups + showContactConnected :: ChatReader m => Contact -> m () showContactConnected = printToView . contactConnected @@ -78,6 +90,12 @@ showContactSubError = printToView .: contactSubError showGroupSubscribed :: ChatReader m => GroupName -> m () showGroupSubscribed = printToView . groupSubscribed +showGroupEmpty :: ChatReader m => GroupName -> m () +showGroupEmpty = printToView . groupEmpty + +showGroupRemoved :: ChatReader m => GroupName -> m () +showGroupRemoved = printToView . groupRemoved + showMemberSubError :: ChatReader m => GroupName -> ContactName -> ChatError -> m () showMemberSubError = printToView .:. memberSubError @@ -102,7 +120,13 @@ showSentMessage_ to msg = printToView =<< liftIO (sentMessage to msg) showGroupCreated :: ChatReader m => Group -> m () showGroupCreated = printToView . groupCreated -showSentGroupInvitation :: ChatReader m => Group -> ContactName -> m () +showGroupDeletedUser :: ChatReader m => GroupName -> m () +showGroupDeletedUser = printToView . groupDeletedUser + +showGroupDeleted :: ChatReader m => GroupName -> GroupMember -> m () +showGroupDeleted = printToView .: groupDeleted + +showSentGroupInvitation :: ChatReader m => GroupName -> ContactName -> m () showSentGroupInvitation = printToView .: sentGroupInvitation showReceivedGroupInvitation :: ChatReader m => Group -> ContactName -> GroupMemberRole -> m () @@ -120,6 +144,18 @@ showJoinedGroupMemberConnecting = printToView .:. joinedGroupMemberConnecting showConnectedToGroupMember :: ChatReader m => GroupName -> GroupMember -> m () showConnectedToGroupMember = printToView .: connectedToGroupMember +showDeletedMember :: ChatReader m => GroupName -> Maybe GroupMember -> Maybe GroupMember -> m () +showDeletedMember = printToView .:. deletedMember + +showDeletedMemberUser :: ChatReader m => GroupName -> GroupMember -> m () +showDeletedMemberUser = printToView .: deletedMemberUser + +showLeftMemberUser :: ChatReader m => GroupName -> m () +showLeftMemberUser = printToView . leftMemberUser + +showLeftMember :: ChatReader m => GroupName -> GroupMember -> m () +showLeftMember = printToView .: leftMember + showGroupMembers :: ChatReader m => Group -> m () showGroupMembers = printToView . groupMembers @@ -136,22 +172,36 @@ invitation qInfo = ] contactDeleted :: ContactName -> [StyledString] -contactDeleted c = [ttyContact c <> " is deleted"] +contactDeleted c = [ttyContact c <> ": contact is deleted"] + +contactGroups :: ContactName -> [GroupName] -> [StyledString] +contactGroups c gNames = [ttyContact c <> ": contact cannot be deleted, it is a member of the group(s) " <> ttyGroups gNames] + where + ttyGroups :: [GroupName] -> StyledString + ttyGroups [] = "" + ttyGroups [g] = ttyGroup g + ttyGroups (g : gs) = ttyGroup g <> ", " <> ttyGroups gs contactConnected :: Contact -> [StyledString] -contactConnected ct = [ttyFullContact ct <> " is connected"] +contactConnected ct = [ttyFullContact ct <> ": contact is connected"] contactDisconnected :: ContactName -> [StyledString] -contactDisconnected c = ["disconnected from " <> ttyContact c <> " - restart chat"] +contactDisconnected c = [ttyContact c <> ": contact is disconnected - restart chat"] contactSubscribed :: ContactName -> [StyledString] -contactSubscribed c = [ttyContact c <> " is active"] +contactSubscribed c = [ttyContact c <> ": contact is active"] contactSubError :: ContactName -> ChatError -> [StyledString] -contactSubError c e = ["contact " <> ttyContact c <> " error: " <> plain (show e)] +contactSubError c e = [ttyContact c <> ": contact error " <> plain (show e)] groupSubscribed :: GroupName -> [StyledString] -groupSubscribed g = [ttyGroup g <> " is active"] +groupSubscribed g = [ttyGroup g <> ": group is active"] + +groupEmpty :: GroupName -> [StyledString] +groupEmpty g = [ttyGroup g <> ": group is empty"] + +groupRemoved :: GroupName -> [StyledString] +groupRemoved g = [ttyGroup g <> ": you are no longer a member or group deleted"] memberSubError :: GroupName -> ContactName -> ChatError -> [StyledString] memberSubError g c e = [ttyGroup g <> " member " <> ttyContact c <> " error: " <> plain (show e)] @@ -162,8 +212,17 @@ groupCreated g@Group {localDisplayName} = "use " <> highlight ("/a " <> localDisplayName <> " ") <> " to add members" ] -sentGroupInvitation :: Group -> ContactName -> [StyledString] -sentGroupInvitation g c = ["invitation to join the group " <> ttyFullGroup g <> " sent to " <> ttyContact c] +groupDeletedUser :: GroupName -> [StyledString] +groupDeletedUser g = groupDeleted_ g Nothing + +groupDeleted :: GroupName -> GroupMember -> [StyledString] +groupDeleted g m = groupDeleted_ g (Just m) <> ["use " <> highlight ("/d #" <> g) <> " to delete the local copy of the group"] + +groupDeleted_ :: GroupName -> Maybe GroupMember -> [StyledString] +groupDeleted_ g m = [ttyGroup g <> ": " <> memberOrUser m <> " deleted the group"] + +sentGroupInvitation :: GroupName -> ContactName -> [StyledString] +sentGroupInvitation g c = ["invitation to join the group " <> ttyGroup g <> " sent to " <> ttyContact c] receivedGroupInvitation :: Group -> ContactName -> GroupMemberRole -> [StyledString] receivedGroupInvitation g@Group {localDisplayName} c role = @@ -183,6 +242,27 @@ joinedGroupMemberConnecting g host m = [ttyGroup g <> ": " <> ttyMember host <> connectedToGroupMember :: GroupName -> GroupMember -> [StyledString] connectedToGroupMember g m = [ttyGroup g <> ": " <> connectedMember m <> " is connected"] +deletedMember :: GroupName -> Maybe GroupMember -> Maybe GroupMember -> [StyledString] +deletedMember g by m = [ttyGroup g <> ": " <> memberOrUser by <> " removed " <> memberOrUser m <> " from the group"] + +deletedMemberUser :: GroupName -> GroupMember -> [StyledString] +deletedMemberUser g by = deletedMember g (Just by) Nothing <> groupPreserved g + +leftMemberUser :: GroupName -> [StyledString] +leftMemberUser g = leftMember_ g Nothing <> groupPreserved g + +leftMember :: GroupName -> GroupMember -> [StyledString] +leftMember g m = leftMember_ g (Just m) + +leftMember_ :: GroupName -> Maybe GroupMember -> [StyledString] +leftMember_ g m = [ttyGroup g <> ": " <> memberOrUser m <> " left the group"] + +groupPreserved :: GroupName -> [StyledString] +groupPreserved g = ["use " <> highlight ("/d #" <> g) <> " to delete the group"] + +memberOrUser :: Maybe GroupMember -> StyledString +memberOrUser = maybe "you" ttyMember + connectedMember :: GroupMember -> StyledString connectedMember m = case memberCategory m of GCPreMember -> "member " <> ttyFullMember m @@ -262,6 +342,8 @@ chatError = \case CEGroupContactRole c -> ["contact " <> ttyContact c <> " has insufficient permissions for this group action"] CEGroupNotJoined g -> ["you did not join this group, use " <> highlight ("/join #" <> g)] CEGroupMemberNotActive -> ["you cannot invite other members yet, try later"] + CEGroupMemberUserRemoved -> ["you are no longer the member of the group"] + CEGroupMemberNotFound c -> ["contact " <> ttyContact c <> " is not a group member"] CEGroupInternal s -> ["chat group bug: " <> plain s] -- e -> ["chat error: " <> plain (show e)] ChatErrorStore err -> case err of @@ -271,13 +353,7 @@ chatError = \case SEGroupNotFound g -> ["no group " <> ttyGroup g] SEGroupAlreadyJoined -> ["you already joined this group"] e -> ["chat db error: " <> plain (show e)] - ChatErrorAgent err -> case err of - -- CONN e -> case e of - -- -- TODO replace with ChatErrorContact errors, these errors should never happen - -- NOT_FOUND -> ["no contact " <> ttyContact c] - -- DUPLICATE -> ["contact " <> ttyContact c <> " already exists"] - -- SIMPLEX -> ["contact " <> ttyContact c <> " did not accept invitation yet"] - e -> ["smp agent error: " <> plain (show e)] + ChatErrorAgent e -> ["smp agent error: " <> plain (show e)] ChatErrorMessage e -> ["chat message error: " <> plain (show e)] printToView :: (MonadUnliftIO m, MonadReader ChatController m) => [StyledString] -> m () diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md index 9e8629c90c..bd761ec46c 100644 --- a/src/Simplex/Chat/protocol.md +++ b/src/Simplex/Chat/protocol.md @@ -91,8 +91,8 @@ M -> B: establish direct connection (M: JOIN, B: LET) M -> Bg: confirm profile and role - `CONF: x.grp.mem.info G_MEM_ID_M,G_MEM_ROLE x.json:NNN ` B -> Mg: send profile probe - `MSG: x.info.probe ` - it should always be send, even when there is no profile match. if M is a known contact (profile match) send probe to M: - B -> M (via old DM conn): profile match probe: `MSG: x.info.probe.check G_MEM_ID_B,` - M -> B (via old DM conn): probe confirm: `MSG: x.info.probe.ok G_MEM_ID_M,` + B -> M (via old DM conn): profile match probe: `MSG: x.info.probe.check ` + M -> B (via old DM conn): probe confirm: `MSG: x.info.probe.ok ` link to the same contact B -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_M` M -> Ag: connected to M: `MSG: x.grp.mem.con G_MEM_ID_B` @@ -102,7 +102,9 @@ A -> group: `MSG: N x.grp.mem.con.all G_MEM_ID_B` #### Send group message -`MSG: N x.msg.new G_MEM_ROLE, x.json:NNN ` +Example: + +`MSG: N x.msg.new c.text x.text:5 hello ` #### Group member statuses diff --git a/stack.yaml b/stack.yaml index b0136113dc..3e8e8b2c75 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 53aadca635a6953bd18a305423783b0d71a13cb6 + commit: 8a4bced56972363c073c05bf81ecc0a3b1c2cd8d # this commit is in PR #164 # # extra-deps: [] diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 5b35ce3dac..e009ef8f4b 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -14,6 +14,7 @@ import Simplex.Chat.Controller (ChatController (..)) import Simplex.Chat.Options import Simplex.Chat.Store import Simplex.Chat.Types (Profile) +import Simplex.Messaging.Agent.Env.SQLite import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import qualified System.Terminal as C import System.Terminal.Internal (VirtualTerminal, VirtualTerminalSettings (..), withVirtualTerminal) @@ -39,12 +40,22 @@ termSettings = data TestCC = TestCC ChatController VirtualTerminal (Async ()) +aCfg :: AgentConfig +aCfg = agentConfig defaultChatConfig + +cfg :: ChatConfig +cfg = + defaultChatConfig + { agentConfig = + aCfg {retryInterval = (retryInterval aCfg) {initialInterval = 50000}} + } + virtualSimplexChat :: FilePath -> Profile -> IO TestCC virtualSimplexChat dbFile profile = do st <- createStore (dbFile <> ".chat.db") 1 void . runExceptT $ createUser st profile True t <- withVirtualTerminal termSettings pure - cc <- newChatController opts {dbFile} t . const $ pure () -- no notifications + cc <- newChatController cfg opts {dbFile} t . const $ pure () -- no notifications a <- async $ runSimplexChat cc pure (TestCC cc t a) diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index dce2c3f666..affe3d68fe 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PostfixOperators #-} module ChatTests where @@ -7,11 +8,12 @@ import ChatClient import Control.Concurrent.Async (concurrently_) import Control.Concurrent.STM import Data.Char (isDigit) -import Data.List (dropWhileEnd, isPrefixOf) +import Data.List (dropWhileEnd, intercalate, isPrefixOf) import qualified Data.Text as T import Simplex.Chat.Controller import Simplex.Chat.Types (Profile (..), User (..)) import System.Terminal.Internal (VirtualTerminal (..)) +import System.Timeout (timeout) import Test.Hspec aliceProfile :: Profile @@ -33,35 +35,37 @@ chatTests = do describe "chat groups" $ do it "add contacts, create group and send/receive messages" testGroup it "create and join group with 4 members" testGroup2 + it "create and delete group" testGroupDelete + fit "remove contact from group and add again" testGroupRemoveAdd testAddContact :: IO () testAddContact = testChat2 aliceProfile bobProfile $ \alice bob -> do - alice ##> "/a" + alice ##> "/c" Just inv <- invitation <$> getWindow alice bob ##> ("/c " <> inv) concurrently_ - (bob <## "alice (Alice) is connected") - (alice <## "bob (Bob) is connected") + (bob <## "alice (Alice): contact is connected") + (alice <## "bob (Bob): contact is connected") alice #> "@bob hello" bob <# "alice> hello" bob #> "@alice hi" alice <# "bob> hi" -- test adding the same contact one more time - local name will be different - alice ##> "/a" + alice ##> "/c" Just inv' <- invitation <$> getWindow alice bob ##> ("/c " <> inv') concurrently_ - (bob <## "alice_1 (Alice) is connected") - (alice <## "bob_1 (Bob) is connected") + (bob <## "alice_1 (Alice): contact is connected") + (alice <## "bob_1 (Bob): contact is connected") alice #> "@bob_1 hello" bob <# "alice_1> hello" bob #> "@alice_1 hi" alice <# "bob_1> hi" -- test deleting contact alice ##> "/d bob_1" - alice <## "bob_1 is deleted" + alice <## "bob_1: contact is deleted" alice #:> "@bob_1 hey" alice <## "no contact bob_1" @@ -73,11 +77,11 @@ testGroup = connectUsers alice cath alice #:> "/g team" -- TODO this occasionally fails in case getWindow is run before the command above is printed - alice <## "use /a team to add members" + alice <## "group #team is created\nuse /a team to add members" alice ##> "/a team bob" concurrently_ (alice <## "invitation to join the group #team sent to bob") - (bob <## "use /j team to accept") + (bob <## "#team: alice invites you to join the group as admin\nuse /j team to accept") bob ##> "/j team" concurrently_ (alice <## "#team: bob joined the group") @@ -85,7 +89,7 @@ testGroup = alice ##> "/a team cath" concurrently_ (alice <## "invitation to join the group #team sent to cath") - (cath <## "use /j team to accept") + (cath <## "#team: alice invites you to join the group as admin\nuse /j team to accept") cath ##> "/j team" concurrentlyN_ [ alice <## "#team: cath joined the group", @@ -108,10 +112,25 @@ testGroup = concurrently_ (alice <# "#team cath> hey") (bob <# "#team cath> hey") - bob #> "@cath hello cath" - cath <# "bob> hello cath" - cath #> "@bob hello bob" - bob <# "cath> hello bob" + bob <##> cath + -- remove member + bob ##> "/rm team cath" + concurrentlyN_ + [ bob <## "#team: you removed cath from the group", + alice <## "#team: bob removed cath from the group", + cath <## "#team: bob removed you from the group\nuse /d #team to delete the group" + ] + bob #> "#team hi" + concurrently_ + (alice <# "#team bob> hi") + (cath "#team hello" + concurrently_ + (bob <# "#team alice> hello") + (cath "#team hello" + cath <## "you are no longer the member of the group" + bob <##> cath testGroup2 :: IO () testGroup2 = @@ -123,15 +142,15 @@ testGroup2 = connectUsers alice dan alice #:> "/g club" -- TODO this occasionally fails in case getWindow is run before the command above is printed - alice <## "use /a club to add members" + alice <## "group #club is created\nuse /a club to add members" alice ##> "/a club bob" concurrently_ (alice <## "invitation to join the group #club sent to bob") - (bob <## "use /j club to accept") + (bob <## "#club: alice invites you to join the group as admin\nuse /j club to accept") alice ##> "/a club cath" concurrently_ (alice <## "invitation to join the group #club sent to cath") - (cath <## "use /j club to accept") + (cath <## "#club: alice invites you to join the group as admin\nuse /j club to accept") bob ##> "/j club" concurrently_ (alice <## "#club: bob joined the group") @@ -149,21 +168,18 @@ testGroup2 = bob ##> "/a club dan" concurrently_ (bob <## "invitation to join the group #club sent to dan") - (dan <## "use /j club to accept") + (dan <## "#club: bob invites you to join the group as admin\nuse /j club to accept") dan ##> "/j club" concurrentlyN_ [ bob <## "#club: dan joined the group", do dan <## "#club: you joined the group" - dan - <### [ "#club: member alice_1 (Alice) is connected", - "#club: member cath (Catherine) is connected", - "use @alice to send messages" - ], + dan <### ["#club: member alice_1 (Alice) is connected", "#club: member cath (Catherine) is connected"] + dan <## "contact alice_1 is merged into alice\nuse @alice to send messages", do alice <## "#club: bob added dan_1 (Daniel) to the group (connecting...)" alice <## "#club: new member dan_1 is connected" - alice <## "use @dan to send messages", + alice <## "contact dan_1 is merged into dan\nuse @dan to send messages", do cath <## "#club: bob added dan (Daniel) to the group (connecting...)" cath <## "#club: new member dan is connected" @@ -192,58 +208,203 @@ testGroup2 = bob <# "#club dan> how is it going?", cath <# "#club dan> how is it going?" ] - bob #> "@cath hi cath" - cath <# "bob> hi cath" - cath #> "@bob hi bob" - bob <# "cath> hi bob" - dan #> "@cath hey cath" - cath <# "dan> hey cath" - cath #> "@dan hey dan" - dan <# "cath> hey dan" - dan #> "@alice hi alice" - alice <# "dan> hi alice" - alice #> "@dan hello dan" - dan <# "alice> hello dan" + bob <##> cath + dan <##> cath + dan <##> alice + -- remove member + cath ##> "/rm club dan" + concurrentlyN_ + [ cath <## "#club: you removed dan from the group", + alice <## "#club: cath removed dan from the group", + bob <## "#club: cath removed dan from the group", + dan <## "#club: cath removed you from the group\nuse /d #club to delete the group" + ] + alice #> "#club hello" + concurrentlyN_ + [ bob <# "#club alice> hello", + cath <# "#club alice> hello", + (dan "#club hi there" + concurrentlyN_ + [ alice <# "#club bob> hi there", + cath <# "#club bob> hi there", + (dan "#club hey" + concurrentlyN_ + [ alice <# "#club cath> hey", + bob <# "#club cath> hey", + (dan "#club how is it going?" + dan <## "you are no longer the member of the group" + dan <##> cath + dan <##> alice + -- member leaves + bob ##> "/l club" + concurrentlyN_ + [ bob <## "#club: you left the group\nuse /d #club to delete the group", + alice <## "#club: bob left the group", + cath <## "#club: bob left the group" + ] + alice #> "#club hello" + concurrently_ + (cath <# "#club alice> hello") + (bob "#club hey" + concurrently_ + (alice <# "#club cath> hey") + (bob "#club how is it going?" + bob <## "you are no longer the member of the group" + bob <##> cath + bob <##> alice + +testGroupDelete :: IO () +testGroupDelete = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + alice ##> "/d #team" + concurrentlyN_ + [ alice <## "#team: you deleted the group", + bob <## "#team: alice deleted the group\nuse /d #team to delete the local copy of the group", + cath <## "#team: alice deleted the group\nuse /d #team to delete the local copy of the group" + ] + bob #:> "/d #team" + bob <## "#team: you deleted the group" + cath #:> "#team hi" + cath <## "you are no longer the member of the group" + +testGroupRemoveAdd :: IO () +testGroupRemoveAdd = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + -- remove member + alice ##> "/rm team bob" + concurrentlyN_ + [ alice <## "#team: you removed bob from the group", + bob <## "#team: alice removed you from the group\nuse /d #team to delete the group", + cath <## "#team: alice removed bob from the group" + ] + alice ##> "/a team bob" + bob <## "#team_1 (team): alice invites you to join the group as admin\nuse /j team_1 to accept" + bob ##> "/j team_1" + concurrentlyN_ + [ alice <## "#team: bob joined the group", + do + bob <## "#team_1: you joined the group" + bob <## "#team_1: member cath_1 (Catherine) is connected" + bob <## "contact cath_1 is merged into cath\nuse @cath to send messages", + do + cath <## "#team: alice added bob_1 (Bob) to the group (connecting...)" + cath <## "#team: new member bob_1 is connected" + cath <## "contact bob_1 is merged into bob\nuse @bob to send messages" + ] + alice #> "#team hi" + concurrently_ + (bob <# "#team_1 alice> hi") + (cath <# "#team alice> hi") + bob #> "#team_1 hey" + concurrently_ + (alice <# "#team bob> hey") + (cath <# "#team bob> hey") + cath #> "#team hello" + concurrently_ + (alice <# "#team cath> hello") + (bob <# "#team_1 cath> hello") connectUsers :: TestCC -> TestCC -> IO () connectUsers cc1 cc2 = do - cc1 ##> "/a" + cc1 ##> "/c" Just inv <- invitation <$> getWindow cc1 cc2 ##> ("/c " <> inv) concurrently_ - (cc2 <## (showName cc1 <> " is connected")) - (cc1 <## (showName cc2 <> " is connected")) + (cc2 <## (showName cc1 <> ": contact is connected")) + (cc1 <## (showName cc2 <> ": contact is connected")) + +showName :: TestCC -> String +showName (TestCC ChatController {currentUser = User {localDisplayName, profile = Profile {fullName}}} _ _) = + T.unpack $ localDisplayName <> " (" <> fullName <> ")" + +createGroup3 :: String -> TestCC -> TestCC -> TestCC -> IO () +createGroup3 gName cc1 cc2 cc3 = do + connectUsers cc1 cc2 + connectUsers cc1 cc3 + cc1 #:> ("/g " <> gName) + cc1 <## ("use /a " <> gName <> " to add members") + cc1 ##> ("/a team " <> name cc2) + concurrently_ + (cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> name cc2)) + (cc2 <## ("use /j " <> gName <> " to accept")) + cc2 ##> ("/j " <> gName) + concurrently_ + (cc1 <## ("#" <> gName <> ": " <> name cc2 <> " joined the group")) + (cc2 <## ("#" <> gName <> ": you joined the group")) + cc1 ##> ("/a team " <> name cc3) + concurrently_ + (cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> name cc3)) + (cc3 <## ("use /j " <> gName <> " to accept")) + cc3 ##> ("/j " <> gName) + concurrentlyN_ + [ cc1 <## ("#" <> gName <> ": " <> name cc3 <> " joined the group"), + do + cc3 <## ("#" <> gName <> ": you joined the group") + cc3 <## ("#" <> gName <> ": member " <> showName cc2 <> " is connected"), + do + cc2 <## ("#" <> gName <> ": alice added " <> showName cc3 <> " to the group (connecting...)") + cc2 <## ("#" <> gName <> ": new member " <> name cc3 <> " is connected") + ] where - showName :: TestCC -> String - showName (TestCC ChatController {currentUser = User {localDisplayName, profile = Profile {fullName}}} _ _) = - T.unpack $ localDisplayName <> " (" <> fullName <> ")" + name :: TestCC -> String + name (TestCC ChatController {currentUser = User {localDisplayName}} _ _) = + T.unpack localDisplayName + +-- | test sending direct messages +(<##>) :: TestCC -> TestCC -> IO () +cc1 <##> cc2 = do + cc1 #> ("@" <> name cc2 <> " hi") + cc2 <# (name cc1 <> "> hi") + cc2 #> ("@" <> name cc1 <> " hey") + cc1 <# (name cc2 <> "> hey") + where + name (TestCC ChatController {currentUser = User {localDisplayName}} _ _) = T.unpack localDisplayName (##>) :: TestCC -> String -> IO () -(##>) cc cmd = do +cc ##> cmd = do cc #:> cmd cc <## cmd (#>) :: TestCC -> String -> IO () -(#>) cc cmd = do +cc #> cmd = do cc #:> cmd cc <# cmd (#:>) :: TestCC -> String -> IO () -(#:>) (TestCC cc _ _) cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd +(TestCC cc _ _) #:> cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd (<##) :: TestCC -> String -> Expectation -cc <## line = (lastOutput <$> getWindow cc) `shouldReturn` line +cc <## line = + let n = length $ lines line + in (lastOutput n <$> getWindow cc) `shouldReturn` line (<###) :: TestCC -> [String] -> Expectation _ <### [] = pure () cc <### ls = do - line <- lastOutput <$> getWindow cc + line <- lastOutput 1 <$> getWindow cc if line `elem` ls then cc <### filter (/= line) ls else error $ "unexpected output: " <> line (<#) :: TestCC -> String -> Expectation -cc <# line = (dropTime . lastOutput <$> getWindow cc) `shouldReturn` line +cc <# line = + let n = length $ lines line + in (dropTime . lastOutput n <$> getWindow cc) `shouldReturn` line + +( Expectation +( String dropTime msg = case splitAt 6 msg of @@ -255,15 +416,18 @@ getWindow :: TestCC -> IO [String] getWindow (TestCC _ t _) = do let w = virtualWindow t win <- readTVarIO w - atomically $ do + -- TODO to debug - putStrLn (lastOutput 1 win') - before returning it + r <- atomically $ do win' <- readTVar w if win' /= win then pure win' else retry + putStrLn $ lastOutput 1 r + pure r invitation :: [String] -> Maybe String invitation win = lastMaybe $ map (dropWhileEnd (== ' ')) $ filter ("smp::" `isPrefixOf`) win -lastOutput :: [String] -> String -lastOutput win = dropWhileEnd (== ' ') $ win !! (length win - 2) -- (- 2) to exclude prompt +lastOutput :: Int -> [String] -> String +lastOutput n win = intercalate "\n" $ map (dropWhileEnd (== ' ')) $ take n $ drop (length win - n - 1) win -- - 1 to exclude prompt lastMaybe :: [a] -> Maybe a lastMaybe [] = Nothing From a9d32db4048e4181ebf1b52fdde5f1bc75a27ef5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 5 Aug 2021 08:38:39 +0100 Subject: [PATCH 18/29] update for SMP agent protocol changes (#89) --- src/Simplex/Chat.hs | 16 ++++++++-------- stack.yaml | 2 +- tests/ChatTests.hs | 6 ++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index ef5e75176c..bd7ed16735 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -299,7 +299,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do agentMsgConnStatus :: ACommand 'Agent -> Maybe ConnStatus agentMsgConnStatus = \case - CONF _ _ -> Just ConnRequested + REQ _ _ -> Just ConnRequested INFO _ -> Just ConnSndReady CON -> Just ConnReady _ -> Nothing @@ -307,7 +307,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do processDirectMessage :: ACommand 'Agent -> Connection -> Maybe Contact -> m () processDirectMessage agentMsg conn = \case Nothing -> case agentMsg of - CONF confId connInfo -> do + REQ confId connInfo -> do saveConnInfo conn connInfo acceptAgentConnection conn confId $ XInfo profile INFO connInfo -> @@ -325,7 +325,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do XInfoProbeCheck probeHash -> xInfoProbeCheck ct probeHash XInfoProbeOk probe -> xInfoProbeOk ct probe _ -> pure () - CONF confId connInfo -> do + REQ confId connInfo -> do -- confirming direct connection with a member ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of @@ -333,7 +333,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do -- TODO check member ID -- TODO update member profile acceptAgentConnection conn confId XOk - _ -> messageError "CONF from member must have x.grp.mem.info" + _ -> messageError "REQ from member must have x.grp.mem.info" INFO connInfo -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of @@ -361,7 +361,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do processGroupMessage :: ACommand 'Agent -> Connection -> GroupName -> GroupMember -> m () processGroupMessage agentMsg conn gName m = case agentMsg of - CONF confId connInfo -> do + REQ confId connInfo -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case memberCategory m of GCInviteeMember -> @@ -371,7 +371,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do withStore $ \st -> updateGroupMemberStatus st userId m GSMemAccepted acceptAgentConnection conn confId XOk | otherwise -> messageError "x.grp.acpt: memberId is different from expected" - _ -> messageError "CONF from invited member must have x.grp.acpt" + _ -> messageError "REQ from invited member must have x.grp.acpt" _ -> case chatMsgEvent of XGrpMemInfo memId _memProfile @@ -380,7 +380,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do Group {membership} <- withStore $ \st -> getGroup st user gName acceptAgentConnection conn confId $ XGrpMemInfo (memberId membership) profile | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" - _ -> messageError "CONF from member must have x.grp.mem.info" + _ -> messageError "REQ from member must have x.grp.mem.info" INFO connInfo -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo case chatMsgEvent of @@ -648,7 +648,7 @@ sendGroupMessage members chatMsgEvent = do acceptAgentConnection :: ChatMonad m => Connection -> ConfirmationId -> ChatMsgEvent -> m () acceptAgentConnection conn@Connection {agentConnId} confId msg = do - withAgent $ \a -> allowConnection a agentConnId confId $ directMessage msg + withAgent $ \a -> acceptConnection a agentConnId confId $ directMessage msg withStore $ \st -> updateConnectionStatus st conn ConnAccepted getCreateActiveUser :: SQLiteStore -> IO User diff --git a/stack.yaml b/stack.yaml index 3e8e8b2c75..93b83465d7 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 8a4bced56972363c073c05bf81ecc0a3b1c2cd8d + commit: 703450d78e812a304e00fc59d09cc4316faf673d # this commit is in PR #164 # # extra-deps: [] diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index affe3d68fe..22ca888e53 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -36,7 +36,7 @@ chatTests = do it "add contacts, create group and send/receive messages" testGroup it "create and join group with 4 members" testGroup2 it "create and delete group" testGroupDelete - fit "remove contact from group and add again" testGroupRemoveAdd + it "remove contact from group and add again" testGroupRemoveAdd testAddContact :: IO () testAddContact = @@ -417,11 +417,9 @@ getWindow (TestCC _ t _) = do let w = virtualWindow t win <- readTVarIO w -- TODO to debug - putStrLn (lastOutput 1 win') - before returning it - r <- atomically $ do + atomically $ do win' <- readTVar w if win' /= win then pure win' else retry - putStrLn $ lastOutput 1 r - pure r invitation :: [String] -> Maybe String invitation win = lastMaybe $ map (dropWhileEnd (== ' ')) $ filter ("smp::" `isPrefixOf`) win From d23417596eaf6822396fabf992fd013fc024cce1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 5 Aug 2021 20:51:48 +0100 Subject: [PATCH 19/29] TMVar lock to avoid subscriber and client processing in parallel, fix the test (#90) * TMVar lock to avoid subscriber and client processing in parallel, fix the test * run SMP server as part of the test * stabilize tests * update simplexmq * test: stabilize getting invitation from terminal * remove unused import * simplify test --- package.yaml | 1 + src/Simplex/Chat.hs | 18 ++- src/Simplex/Chat/Controller.hs | 3 +- stack.yaml | 2 +- tests/ChatClient.hs | 113 +++++++++++++++++-- tests/ChatTests.hs | 198 +++++++++++++++++++-------------- tests/Test.hs | 10 +- 7 files changed, 243 insertions(+), 102 deletions(-) diff --git a/package.yaml b/package.yaml index 90dbff3df3..bfb1af7e4f 100644 --- a/package.yaml +++ b/package.yaml @@ -58,6 +58,7 @@ tests: - simplex-chat - async == 2.2.* - hspec == 2.7.* + - network == 3.1.* - stm == 2.5.* ghc-options: diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index bd7ed16735..21da972221 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -50,6 +50,7 @@ import System.Exit (exitFailure, exitSuccess) import System.IO (hFlush, stdout) import Text.Read (readMaybe) import UnliftIO.Async (race_) +import qualified UnliftIO.Exception as E import UnliftIO.STM data ChatCommand @@ -111,7 +112,8 @@ newChatController ChatConfig {agentConfig = cfg, dbPoolSize, tbqSize} ChatOpts { idsDrg <- newTVarIO =<< drgNew inputQ <- newTBQueueIO tbqSize notifyQ <- newTBQueueIO tbqSize - pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, idsDrg, inputQ, notifyQ, sendNotification} + chatLock <- newTMVarIO () + pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, idsDrg, inputQ, notifyQ, sendNotification, chatLock} runSimplexChat :: ChatController -> IO () runSimplexChat = runReaderT (race_ runTerminalInput runChatController) @@ -124,9 +126,16 @@ runChatController = notificationSubscriber ] +withLock :: MonadUnliftIO m => TMVar () -> m () -> m () +withLock lock = + E.bracket_ + (void . atomically $ takeTMVar lock) + (atomically $ putTMVar lock ()) + inputSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () inputSubscriber = do q <- asks inputQ + l <- asks chatLock forever $ atomically (readTBQueue q) >>= \case InputControl _ -> pure () @@ -139,7 +148,8 @@ inputSubscriber = do SendGroupMessage g msg -> showSentGroupMessage g msg _ -> printToView [plain s] user <- asks currentUser - void . runExceptT $ processChatCommand user cmd `catchError` showChatError + withLock l . void . runExceptT $ + processChatCommand user cmd `catchError` showChatError processChatCommand :: ChatMonad m => User -> ChatCommand -> m () processChatCommand user@User {userId, profile} = \case @@ -244,12 +254,14 @@ processChatCommand user@User {userId, profile} = \case agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do q <- asks $ subQ . smpAgent + l <- asks chatLock subscribeUserConnections forever $ do (_, connId, msg) <- atomically $ readTBQueue q user <- asks currentUser -- TODO handle errors properly - void . runExceptT $ processAgentMessage user connId msg `catchError` (liftIO . print) + withLock l . void . runExceptT $ + processAgentMessage user connId msg `catchError` (liftIO . print) subscribeUserConnections :: (MonadUnliftIO m, MonadReader ChatController m) => m () subscribeUserConnections = void . runExceptT $ do diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index d07331ed6c..395f62d963 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -28,7 +28,8 @@ data ChatController = ChatController idsDrg :: TVar ChaChaDRG, inputQ :: TBQueue InputEvent, notifyQ :: TBQueue Notification, - sendNotification :: Notification -> IO () + sendNotification :: Notification -> IO (), + chatLock :: TMVar () } data InputEvent = InputCommand String | InputControl Char diff --git a/stack.yaml b/stack.yaml index 93b83465d7..bb21b4f3d7 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 703450d78e812a304e00fc59d09cc4316faf673d + commit: d9084522afa939b2993d4c3e94db90cf145ffadf # this commit is in PR #164 # # extra-deps: [] diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index e009ef8f4b..22fd21be2d 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -1,32 +1,44 @@ {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} module ChatClient where +import Control.Concurrent (ThreadId, forkIOWithUnmask, killThread) import Control.Concurrent.Async -import Control.Concurrent.STM (retry) -import Control.Exception (bracket_) +import Control.Concurrent.STM +import Control.Exception (bracket, bracket_) import Control.Monad.Except +import Data.List (dropWhileEnd) +import Network.Socket import Simplex.Chat import Simplex.Chat.Controller (ChatController (..)) import Simplex.Chat.Options import Simplex.Chat.Store import Simplex.Chat.Types (Profile) import Simplex.Messaging.Agent.Env.SQLite +import Simplex.Messaging.Server (runSMPServerBlocking) +import Simplex.Messaging.Server.Env.STM +import Simplex.Messaging.Transport import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import qualified System.Terminal as C -import System.Terminal.Internal (VirtualTerminal, VirtualTerminalSettings (..), withVirtualTerminal) +import System.Terminal.Internal (VirtualTerminal (..), VirtualTerminalSettings (..), withVirtualTerminal) +import System.Timeout (timeout) testDBPrefix :: FilePath testDBPrefix = "tests/tmp/test" +serverPort :: ServiceName +serverPort = "5000" + opts :: ChatOpts opts = ChatOpts { dbFile = undefined, - smpServers = ["localhost:5223"] + smpServers = ["localhost:5000"] } termSettings :: VirtualTerminalSettings @@ -38,7 +50,13 @@ termSettings = virtualInterrupt = retry } -data TestCC = TestCC ChatController VirtualTerminal (Async ()) +data TestCC = TestCC + { chatController :: ChatController, + virtualTerminal :: VirtualTerminal, + chatAsync :: Async (), + termAsync :: Async (), + termQ :: TQueue String + } aCfg :: AgentConfig aCfg = agentConfig defaultChatConfig @@ -56,8 +74,33 @@ virtualSimplexChat dbFile profile = do void . runExceptT $ createUser st profile True t <- withVirtualTerminal termSettings pure cc <- newChatController cfg opts {dbFile} t . const $ pure () -- no notifications - a <- async $ runSimplexChat cc - pure (TestCC cc t a) + chatAsync <- async $ runSimplexChat cc + termQ <- newTQueueIO + termAsync <- async $ readTerminalOutput t termQ + pure TestCC {chatController = cc, virtualTerminal = t, chatAsync, termAsync, termQ} + +readTerminalOutput :: VirtualTerminal -> TQueue String -> IO () +readTerminalOutput t termQ = do + let w = virtualWindow t + winVar <- atomically $ newTVar . init =<< readTVar w + forever . atomically $ do + win <- readTVar winVar + win' <- init <$> readTVar w + if win' == win + then retry + else do + let diff = getDiff win' win + forM_ diff $ writeTQueue termQ + writeTVar winVar win' + where + getDiff :: [String] -> [String] -> [String] + getDiff win win' = getDiff_ 1 (length win) win win' + getDiff_ :: Int -> Int -> [String] -> [String] -> [String] + getDiff_ n len win' win = + let diff = drop (len - n) win' + in if drop n win <> diff == win' + then map (dropWhileEnd (== ' ')) diff + else getDiff_ (n + 1) len win' win testChatN :: [Profile] -> ([TestCC] -> IO ()) -> IO () testChatN ps test = @@ -95,3 +138,59 @@ testChat4 p1 p2 p3 p4 test = testChatN [p1, p2, p3, p4] test_ concurrentlyN_ :: [IO a] -> IO () concurrentlyN_ = mapConcurrently_ id + +serverCfg :: ServerConfig +serverCfg = + ServerConfig + { transports = [(serverPort, transport @TCP)], + tbqSize = 1, + queueIdBytes = 12, + msgIdBytes = 6, + storeLog = Nothing, + serverPrivateKey = + -- full RSA private key (only for tests) + "MIIFIwIBAAKCAQEArZyrri/NAwt5buvYjwu+B/MQeJUszDBpRgVqNddlI9kNwDXu\ + \kaJ8chEhrtaUgXeSWGooWwqjXEUQE6RVbCC6QVo9VEBSP4xFwVVd9Fj7OsgfcXXh\ + \AqWxfctDcBZQ5jTUiJpdBc+Vz2ZkumVNl0W+j9kWm9nfkMLQj8c0cVSDxz4OKpZb\ + \qFuj0uzHkis7e7wsrKSKWLPg3M5ZXPZM1m9qn7SfJzDRDfJifamxWI7uz9XK2+Dp\ + \NkUQlGQgFJEv1cKN88JAwIqZ1s+TAQMQiB+4QZ2aNfSqGEzRJN7FMCKRK7pM0A9A\ + \PCnijyuImvKFxTdk8Bx1q+XNJzsY6fBrLWJZ+QKBgQCySG4tzlcEm+tOVWRcwrWh\ + \6zsczGZp9mbf9c8itRx6dlldSYuDG1qnddL70wuAZF2AgS1JZgvcRZECoZRoWP5q\ + \Kq2wvpTIYjFPpC39lxgUoA/DXKVKZZdan+gwaVPAPT54my1CS32VrOiAY4gVJ3LJ\ + \Mn1/FqZXUFQA326pau3loQKCAQEAoljmJMp88EZoy3HlHUbOjl5UEhzzVsU1TnQi\ + \QmPm+aWRe2qelhjW4aTvSVE5mAUJsN6UWTeMf4uvM69Z9I5pfw2pEm8x4+GxRibY\ + \iiwF2QNaLxxmzEHm1zQQPTgb39o8mgklhzFPill0JsnL3f6IkVwjFJofWSmpqEGs\ + \dFSMRSXUTVXh1p/o7QZrhpwO/475iWKVS7o48N/0Xp513re3aXw+DRNuVnFEaBIe\ + \TLvWM9Czn16ndAu1HYiTBuMvtRbAWnGZxU8ewzF4wlWK5tdIL5PTJDd1VhZJAKtB\ + \npDvJpwxzKmjAhcTmjx0ckMIWtdVaOVm/2gWCXDty2FEdg7koQKBgQDOUUguJ/i7\ + \q0jldWYRnVkotKnpInPdcEaodrehfOqYEHnvro9xlS6OeAS4Vz5AdH45zQ/4J3bV\ + \2cH66tNr18ebM9nL//t5G69i89R9W7szyUxCI3LmAIdi3oSEbmz5GQBaw4l6h9Wi\ + \n4FmFQaAXZrjQfO2qJcAHvWRsMp2pmqAGwKBgQDXaza0DRsKWywWznsHcmHa0cx8\ + \I4jxqGaQmLO7wBJRP1NSFrywy1QfYrVX9CTLBK4V3F0PCgZ01Qv94751CzN43TgF\ + \ebd/O9r5NjNTnOXzdWqETbCffLGd6kLgCMwPQWpM9ySVjXHWCGZsRAnF2F6M1O32\ + \43StIifvwJQFqSM3ewKBgCaW6y7sRY90Ua7283RErezd9EyT22BWlDlACrPu3FNC\ + \LtBf1j43uxBWBQrMLsHe2GtTV0xt9m0MfwZsm2gSsXcm4Xi4DJgfN+Z7rIlyy9UY\ + \PCDSdZiU1qSr+NrffDrXlfiAM1cUmCdUX7eKjp/ltkUHNaOGfSn5Pdr3MkAiD/Hf\ + \AoGBAKIdKCuOwuYlwjS9J+IRGuSSM4o+OxQdwGmcJDTCpyWb5dEk68e7xKIna3zf\ + \jc+H+QdMXv1nkRK9bZgYheXczsXaNZUSTwpxaEldzVD3hNvsXSgJRy9fqHwA4PBq\ + \vqiBHoO3RNbqg+2rmTMfDuXreME3S955ZiPZm4Z+T8Hj52mPAoGAQm5QH/gLFtY5\ + \+znqU/0G8V6BKISCQMxbbmTQVcTgGySrP2gVd+e4MWvUttaZykhWqs8rpr7mgpIY\ + \hul7Swx0SHFN3WpXu8uj+B6MLpRcCbDHO65qU4kQLs+IaXXsuuTjMvJ5LwjkZVrQ\ + \TmKzSAw7iVWwEUZR/PeiEKazqrpp9VU=" + } + +withSmpServer :: IO a -> IO a +withSmpServer = serverBracket (`runSMPServerBlocking` serverCfg) (pure ()) . const + +serverBracket :: (TMVar Bool -> IO ()) -> IO () -> (ThreadId -> IO a) -> IO a +serverBracket process afterProcess f = do + started <- newEmptyTMVarIO + bracket + (forkIOWithUnmask ($ process started)) + (\t -> killThread t >> afterProcess >> waitFor started "stop") + (\t -> waitFor started "start" >> f t) + where + waitFor started s = + 5000000 `timeout` atomically (takeTMVar started) >>= \case + Nothing -> error $ "server did not " <> s + _ -> pure () diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 22ca888e53..f144dca213 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PostfixOperators #-} @@ -8,11 +9,9 @@ import ChatClient import Control.Concurrent.Async (concurrently_) import Control.Concurrent.STM import Data.Char (isDigit) -import Data.List (dropWhileEnd, intercalate, isPrefixOf) import qualified Data.Text as T import Simplex.Chat.Controller import Simplex.Chat.Types (Profile (..), User (..)) -import System.Terminal.Internal (VirtualTerminal (..)) import System.Timeout (timeout) import Test.Hspec @@ -43,7 +42,7 @@ testAddContact = testChat2 aliceProfile bobProfile $ \alice bob -> do alice ##> "/c" - Just inv <- invitation <$> getWindow alice + inv <- getInvitation alice bob ##> ("/c " <> inv) concurrently_ (bob <## "alice (Alice): contact is connected") @@ -54,7 +53,7 @@ testAddContact = alice <# "bob> hi" -- test adding the same contact one more time - local name will be different alice ##> "/c" - Just inv' <- invitation <$> getWindow alice + inv' <- getInvitation alice bob ##> ("/c " <> inv') concurrently_ (bob <## "alice_1 (Alice): contact is connected") @@ -66,7 +65,7 @@ testAddContact = -- test deleting contact alice ##> "/d bob_1" alice <## "bob_1: contact is deleted" - alice #:> "@bob_1 hey" + alice #> "@bob_1 hey" alice <## "no contact bob_1" testGroup :: IO () @@ -75,21 +74,27 @@ testGroup = \alice bob cath -> do connectUsers alice bob connectUsers alice cath - alice #:> "/g team" - -- TODO this occasionally fails in case getWindow is run before the command above is printed - alice <## "group #team is created\nuse /a team to add members" + alice ##> "/g team" + alice <## "group #team is created" + alice <## "use /a team to add members" alice ##> "/a team bob" - concurrently_ - (alice <## "invitation to join the group #team sent to bob") - (bob <## "#team: alice invites you to join the group as admin\nuse /j team to accept") + concurrentlyN_ + [ alice <## "invitation to join the group #team sent to bob", + do + bob <## "#team: alice invites you to join the group as admin" + bob <## "use /j team to accept" + ] bob ##> "/j team" concurrently_ (alice <## "#team: bob joined the group") (bob <## "#team: you joined the group") alice ##> "/a team cath" - concurrently_ - (alice <## "invitation to join the group #team sent to cath") - (cath <## "#team: alice invites you to join the group as admin\nuse /j team to accept") + concurrentlyN_ + [ alice <## "invitation to join the group #team sent to cath", + do + cath <## "#team: alice invites you to join the group as admin" + cath <## "use /j team to accept" + ] cath ##> "/j team" concurrentlyN_ [ alice <## "#team: cath joined the group", @@ -118,7 +123,9 @@ testGroup = concurrentlyN_ [ bob <## "#team: you removed cath from the group", alice <## "#team: bob removed cath from the group", - cath <## "#team: bob removed you from the group\nuse /d #team to delete the group" + do + cath <## "#team: bob removed you from the group" + cath <## "use /d #team to delete the group" ] bob #> "#team hi" concurrently_ @@ -128,7 +135,7 @@ testGroup = concurrently_ (bob <# "#team alice> hello") (cath "#team hello" + cath #> "#team hello" cath <## "you are no longer the member of the group" bob <##> cath @@ -140,17 +147,23 @@ testGroup2 = connectUsers alice cath connectUsers bob dan connectUsers alice dan - alice #:> "/g club" - -- TODO this occasionally fails in case getWindow is run before the command above is printed - alice <## "group #club is created\nuse /a club to add members" + alice ##> "/g club" + alice <## "group #club is created" + alice <## "use /a club to add members" alice ##> "/a club bob" - concurrently_ - (alice <## "invitation to join the group #club sent to bob") - (bob <## "#club: alice invites you to join the group as admin\nuse /j club to accept") + concurrentlyN_ + [ alice <## "invitation to join the group #club sent to bob", + do + bob <## "#club: alice invites you to join the group as admin" + bob <## "use /j club to accept" + ] alice ##> "/a club cath" - concurrently_ - (alice <## "invitation to join the group #club sent to cath") - (cath <## "#club: alice invites you to join the group as admin\nuse /j club to accept") + concurrentlyN_ + [ alice <## "invitation to join the group #club sent to cath", + do + cath <## "#club: alice invites you to join the group as admin" + cath <## "use /j club to accept" + ] bob ##> "/j club" concurrently_ (alice <## "#club: bob joined the group") @@ -166,20 +179,28 @@ testGroup2 = bob <## "#club: new member cath is connected" ] bob ##> "/a club dan" - concurrently_ - (bob <## "invitation to join the group #club sent to dan") - (dan <## "#club: bob invites you to join the group as admin\nuse /j club to accept") + concurrentlyN_ + [ bob <## "invitation to join the group #club sent to dan", + do + dan <## "#club: bob invites you to join the group as admin" + dan <## "use /j club to accept" + ] dan ##> "/j club" concurrentlyN_ [ bob <## "#club: dan joined the group", do dan <## "#club: you joined the group" - dan <### ["#club: member alice_1 (Alice) is connected", "#club: member cath (Catherine) is connected"] - dan <## "contact alice_1 is merged into alice\nuse @alice to send messages", + dan + <### [ "#club: member alice_1 (Alice) is connected", + "contact alice_1 is merged into alice", + "use @alice to send messages", + "#club: member cath (Catherine) is connected" + ], do alice <## "#club: bob added dan_1 (Daniel) to the group (connecting...)" alice <## "#club: new member dan_1 is connected" - alice <## "contact dan_1 is merged into dan\nuse @dan to send messages", + alice <## "contact dan_1 is merged into dan" + alice <## "use @dan to send messages", do cath <## "#club: bob added dan (Daniel) to the group (connecting...)" cath <## "#club: new member dan is connected" @@ -217,7 +238,9 @@ testGroup2 = [ cath <## "#club: you removed dan from the group", alice <## "#club: cath removed dan from the group", bob <## "#club: cath removed dan from the group", - dan <## "#club: cath removed you from the group\nuse /d #club to delete the group" + do + dan <## "#club: cath removed you from the group" + dan <## "use /d #club to delete the group" ] alice #> "#club hello" concurrentlyN_ @@ -237,14 +260,16 @@ testGroup2 = bob <# "#club cath> hey", (dan "#club how is it going?" + dan #> "#club how is it going?" dan <## "you are no longer the member of the group" dan <##> cath dan <##> alice -- member leaves bob ##> "/l club" concurrentlyN_ - [ bob <## "#club: you left the group\nuse /d #club to delete the group", + [ do + bob <## "#club: you left the group" + bob <## "use /d #club to delete the group", alice <## "#club: bob left the group", cath <## "#club: bob left the group" ] @@ -256,7 +281,7 @@ testGroup2 = concurrently_ (alice <# "#club cath> hey") (bob "#club how is it going?" + bob #> "#club how is it going?" bob <## "you are no longer the member of the group" bob <##> cath bob <##> alice @@ -269,12 +294,16 @@ testGroupDelete = alice ##> "/d #team" concurrentlyN_ [ alice <## "#team: you deleted the group", - bob <## "#team: alice deleted the group\nuse /d #team to delete the local copy of the group", - cath <## "#team: alice deleted the group\nuse /d #team to delete the local copy of the group" + do + bob <## "#team: alice deleted the group" + bob <## "use /d #team to delete the local copy of the group", + do + cath <## "#team: alice deleted the group" + cath <## "use /d #team to delete the local copy of the group" ] - bob #:> "/d #team" + bob ##> "/d #team" bob <## "#team: you deleted the group" - cath #:> "#team hi" + cath #> "#team hi" cath <## "you are no longer the member of the group" testGroupRemoveAdd :: IO () @@ -286,22 +315,28 @@ testGroupRemoveAdd = alice ##> "/rm team bob" concurrentlyN_ [ alice <## "#team: you removed bob from the group", - bob <## "#team: alice removed you from the group\nuse /d #team to delete the group", + do + bob <## "#team: alice removed you from the group" + bob <## "use /d #team to delete the group", cath <## "#team: alice removed bob from the group" ] alice ##> "/a team bob" - bob <## "#team_1 (team): alice invites you to join the group as admin\nuse /j team_1 to accept" + alice <## "invitation to join the group #team sent to bob" + bob <## "#team_1 (team): alice invites you to join the group as admin" + bob <## "use /j team_1 to accept" bob ##> "/j team_1" concurrentlyN_ [ alice <## "#team: bob joined the group", do bob <## "#team_1: you joined the group" bob <## "#team_1: member cath_1 (Catherine) is connected" - bob <## "contact cath_1 is merged into cath\nuse @cath to send messages", + bob <## "contact cath_1 is merged into cath" + bob <## "use @cath to send messages", do cath <## "#team: alice added bob_1 (Bob) to the group (connecting...)" cath <## "#team: new member bob_1 is connected" - cath <## "contact bob_1 is merged into bob\nuse @bob to send messages" + cath <## "contact bob_1 is merged into bob" + cath <## "use @bob to send messages" ] alice #> "#team hi" concurrently_ @@ -319,34 +354,29 @@ testGroupRemoveAdd = connectUsers :: TestCC -> TestCC -> IO () connectUsers cc1 cc2 = do cc1 ##> "/c" - Just inv <- invitation <$> getWindow cc1 + inv <- getInvitation cc1 cc2 ##> ("/c " <> inv) concurrently_ (cc2 <## (showName cc1 <> ": contact is connected")) (cc1 <## (showName cc2 <> ": contact is connected")) showName :: TestCC -> String -showName (TestCC ChatController {currentUser = User {localDisplayName, profile = Profile {fullName}}} _ _) = +showName (TestCC ChatController {currentUser = User {localDisplayName, profile = Profile {fullName}}} _ _ _ _) = T.unpack $ localDisplayName <> " (" <> fullName <> ")" createGroup3 :: String -> TestCC -> TestCC -> TestCC -> IO () createGroup3 gName cc1 cc2 cc3 = do connectUsers cc1 cc2 connectUsers cc1 cc3 - cc1 #:> ("/g " <> gName) + cc1 ##> ("/g " <> gName) + cc1 <## ("group #" <> gName <> " is created") cc1 <## ("use /a " <> gName <> " to add members") - cc1 ##> ("/a team " <> name cc2) - concurrently_ - (cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> name cc2)) - (cc2 <## ("use /j " <> gName <> " to accept")) + addMember cc2 cc2 ##> ("/j " <> gName) concurrently_ (cc1 <## ("#" <> gName <> ": " <> name cc2 <> " joined the group")) (cc2 <## ("#" <> gName <> ": you joined the group")) - cc1 ##> ("/a team " <> name cc3) - concurrently_ - (cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> name cc3)) - (cc3 <## ("use /j " <> gName <> " to accept")) + addMember cc3 cc3 ##> ("/j " <> gName) concurrentlyN_ [ cc1 <## ("#" <> gName <> ": " <> name cc3 <> " joined the group"), @@ -358,8 +388,17 @@ createGroup3 gName cc1 cc2 cc3 = do cc2 <## ("#" <> gName <> ": new member " <> name cc3 <> " is connected") ] where + addMember :: TestCC -> IO () + addMember mem = do + cc1 ##> ("/a " <> gName <> " " <> name mem) + concurrentlyN_ + [ cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> name mem), + do + mem <## ("#" <> gName <> ": " <> name cc1 <> " invites you to join the group as admin") + mem <## ("use /j " <> gName <> " to accept") + ] name :: TestCC -> String - name (TestCC ChatController {currentUser = User {localDisplayName}} _ _) = + name (TestCC ChatController {currentUser = User {localDisplayName}} _ _ _ _) = T.unpack localDisplayName -- | test sending direct messages @@ -370,41 +409,37 @@ cc1 <##> cc2 = do cc2 #> ("@" <> name cc1 <> " hey") cc1 <# (name cc2 <> "> hey") where - name (TestCC ChatController {currentUser = User {localDisplayName}} _ _) = T.unpack localDisplayName + name (TestCC ChatController {currentUser = User {localDisplayName}} _ _ _ _) = T.unpack localDisplayName (##>) :: TestCC -> String -> IO () cc ##> cmd = do - cc #:> cmd + cc `send` cmd cc <## cmd (#>) :: TestCC -> String -> IO () cc #> cmd = do - cc #:> cmd + cc `send` cmd cc <# cmd -(#:>) :: TestCC -> String -> IO () -(TestCC cc _ _) #:> cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd +send :: TestCC -> String -> IO () +send TestCC {chatController = cc} cmd = atomically $ writeTBQueue (inputQ cc) $ InputCommand cmd (<##) :: TestCC -> String -> Expectation -cc <## line = - let n = length $ lines line - in (lastOutput n <$> getWindow cc) `shouldReturn` line +cc <## line = getTermLine cc `shouldReturn` line (<###) :: TestCC -> [String] -> Expectation _ <### [] = pure () cc <### ls = do - line <- lastOutput 1 <$> getWindow cc + line <- getTermLine cc if line `elem` ls then cc <### filter (/= line) ls else error $ "unexpected output: " <> line (<#) :: TestCC -> String -> Expectation -cc <# line = - let n = length $ lines line - in (dropTime . lastOutput n <$> getWindow cc) `shouldReturn` line +cc <# line = (dropTime <$> getTermLine cc) `shouldReturn` line ( Expectation -( String dropTime msg = case splitAt 6 msg of @@ -412,21 +447,14 @@ dropTime msg = case splitAt 6 msg of if all isDigit [m, m', s, s'] then text else error "invalid time" _ -> error "invalid time" -getWindow :: TestCC -> IO [String] -getWindow (TestCC _ t _) = do - let w = virtualWindow t - win <- readTVarIO w - -- TODO to debug - putStrLn (lastOutput 1 win') - before returning it - atomically $ do - win' <- readTVar w - if win' /= win then pure win' else retry +getTermLine :: TestCC -> IO String +getTermLine = atomically . readTQueue . termQ -invitation :: [String] -> Maybe String -invitation win = lastMaybe $ map (dropWhileEnd (== ' ')) $ filter ("smp::" `isPrefixOf`) win - -lastOutput :: Int -> [String] -> String -lastOutput n win = intercalate "\n" $ map (dropWhileEnd (== ' ')) $ take n $ drop (length win - n - 1) win -- - 1 to exclude prompt - -lastMaybe :: [a] -> Maybe a -lastMaybe [] = Nothing -lastMaybe xs = Just $ last xs +getInvitation :: TestCC -> IO String +getInvitation cc = do + cc <## "pass this invitation to your contact (via another channel):" + cc <## "" + inv <- getTermLine cc + cc <## "" + cc <## "and ask them to connect: /c " + pure inv diff --git a/tests/Test.hs b/tests/Test.hs index ef5368a7ee..961475ab38 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -1,11 +1,11 @@ +import ChatClient import ChatTests import MarkdownTests import ProtocolTests import Test.Hspec main :: IO () -main = do - hspec $ do - describe "SimpleX chat markdown" markdownTests - describe "SimpleX chat protocol" protocolTests - xdescribe "SimpleX chat client" chatTests +main = withSmpServer . hspec $ do + describe "SimpleX chat markdown" markdownTests + describe "SimpleX chat protocol" protocolTests + describe "SimpleX chat client" chatTests From e5b9cdef9d51769db0251310bcaf1c0af29572e6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 14 Aug 2021 21:04:51 +0100 Subject: [PATCH 20/29] update for asynchronous message delivery (#92) --- src/Simplex/Chat.hs | 31 +++++++++++++++++++++---------- src/Simplex/Chat/Types.hs | 4 ++++ src/Simplex/Chat/View.hs | 9 ++++++++- stack.yaml | 2 +- tests/ChatClient.hs | 1 + tests/ChatTests.hs | 1 - 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 21da972221..89bc860b59 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -289,7 +289,7 @@ subscribeUserConnections = void . runExceptT $ do subscribe cId = withAgent (`subscribeConnection` cId) processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () -processAgentMessage user@User {userId, profile} agentConnId agentMessage = do +processAgentMessage user@User {userId, profile} agentConnId agentMessage = unless (sent agentMessage) $ do chatDirection <- withStore $ \st -> getConnectionChatDirection st user agentConnId forM_ (agentMsgConnStatus agentMessage) $ \status -> withStore $ \st -> updateConnectionStatus st (fromConnection chatDirection) status @@ -299,6 +299,10 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do ReceivedGroupMessage conn gName m -> processGroupMessage agentMessage conn gName m where + sent :: ACommand 'Agent -> Bool + sent SENT {} = True + sent _ = False + isMember :: MemberId -> Group -> Bool isMember memId Group {membership, members} = memberId membership == memId || isJust (find ((== memId) . memberId) members) @@ -324,8 +328,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do acceptAgentConnection conn confId $ XInfo profile INFO connInfo -> saveConnInfo conn connInfo - CON -> pure () - _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMsg) + _ -> pure () Just ct@Contact {localDisplayName = c} -> case agentMsg of MSG meta msgBody -> do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody @@ -366,10 +369,17 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do notifyMemberConnected gName m when (memberCategory m == GCPreMember) $ probeMatchingContacts ct END -> do + showContactAnotherClient c + showToast (c <> "> ") "connected to another client" + unsetActive $ ActiveC c + DOWN -> do showContactDisconnected c showToast (c <> "> ") "disconnected" - unsetActive $ ActiveC c - _ -> messageError $ "unexpected agent event: " <> T.pack (show agentMsg) + UP -> do + showContactSubscribed c + showToast (c <> "> ") "is active" + setActive $ ActiveC c + _ -> pure () processGroupMessage :: ACommand 'Agent -> Connection -> GroupName -> GroupMember -> m () processGroupMessage agentMsg conn gName m = case agentMsg of @@ -449,7 +459,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do XGrpLeave -> xGrpLeave gName m XGrpDel -> xGrpDel gName m _ -> messageError $ "unsupported message: " <> T.pack (show chatMsgEvent) - _ -> messageError $ "unsupported agent event: " <> T.pack (show agentMsg) + _ -> pure () notifyMemberConnected :: GroupName -> GroupMember -> m () notifyMemberConnected gName m@GroupMember {localDisplayName} = do @@ -636,10 +646,11 @@ chatError :: ChatMonad m => ChatErrorType -> m () chatError = throwError . ChatError deleteMemberConnection :: ChatMonad m => GroupMember -> m () -deleteMemberConnection m = do - User {userId} <- asks currentUser - withAgent $ forM_ (memberConnId m) . deleteConnection - withStore $ \st -> deleteGroupMemberConnection st userId m +deleteMemberConnection m@GroupMember {activeConn} = do + -- User {userId} <- asks currentUser + withAgent $ forM_ (memberConnId m) . suspendConnection + -- withStore $ \st -> deleteGroupMemberConnection st userId m + forM_ activeConn $ \conn -> withStore $ \st -> updateConnectionStatus st conn ConnDeleted sendDirectMessage :: ChatMonad m => ConnId -> ChatMsgEvent -> m () sendDirectMessage agentConnId chatMsgEvent = diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index ba00415101..94708cdf97 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -324,6 +324,8 @@ data ConnStatus ConnSndReady | -- | connection is ready for both parties to send and receive messages ConnReady + | -- | connection deleted + ConnDeleted deriving (Eq, Show) instance FromField ConnStatus where fromField = fromTextField_ connStatusT @@ -338,6 +340,7 @@ connStatusT = \case "accepted" -> Just ConnAccepted "snd-ready" -> Just ConnSndReady "ready" -> Just ConnReady + "deleted" -> Just ConnDeleted _ -> Nothing serializeConnStatus :: ConnStatus -> Text @@ -348,6 +351,7 @@ serializeConnStatus = \case ConnAccepted -> "accepted" ConnSndReady -> "snd-ready" ConnReady -> "ready" + ConnDeleted -> "deleted" data ConnType = ConnContact | ConnMember deriving (Eq, Show) diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index f54a0936f0..4af608f959 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -13,6 +13,7 @@ module Simplex.Chat.View showContactGroups, showContactConnected, showContactDisconnected, + showContactAnotherClient, showContactSubscribed, showContactSubError, showGroupSubscribed, @@ -81,6 +82,9 @@ showContactConnected = printToView . contactConnected showContactDisconnected :: ChatReader m => ContactName -> m () showContactDisconnected = printToView . contactDisconnected +showContactAnotherClient :: ChatReader m => ContactName -> m () +showContactAnotherClient = printToView . contactAnotherClient + showContactSubscribed :: ChatReader m => ContactName -> m () showContactSubscribed = printToView . contactSubscribed @@ -186,7 +190,10 @@ contactConnected :: Contact -> [StyledString] contactConnected ct = [ttyFullContact ct <> ": contact is connected"] contactDisconnected :: ContactName -> [StyledString] -contactDisconnected c = [ttyContact c <> ": contact is disconnected - restart chat"] +contactDisconnected c = [ttyContact c <> ": contact is disconnected (messages will be queued)"] + +contactAnotherClient :: ContactName -> [StyledString] +contactAnotherClient c = [ttyContact c <> ": contact is connected to another client"] contactSubscribed :: ContactName -> [StyledString] contactSubscribed c = [ttyContact c <> ": contact is active"] diff --git a/stack.yaml b/stack.yaml index bb21b4f3d7..2d17c5d3ae 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: d9084522afa939b2993d4c3e94db90cf145ffadf + commit: dd5137c336d5525c38b068d7212964b4ab196a33 # this commit is in PR #164 # # extra-deps: [] diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 22fd21be2d..5b266e8c77 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -20,6 +20,7 @@ import Simplex.Chat.Options import Simplex.Chat.Store import Simplex.Chat.Types (Profile) import Simplex.Messaging.Agent.Env.SQLite +import Simplex.Messaging.Agent.RetryInterval import Simplex.Messaging.Server (runSMPServerBlocking) import Simplex.Messaging.Server.Env.STM import Simplex.Messaging.Transport diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index f144dca213..3053d52763 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PostfixOperators #-} From 9cfca4ed3546943fd0d60a9a6cb02b61eee331d6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 22 Aug 2021 15:56:36 +0100 Subject: [PATCH 21/29] update user profile (and notify contacts) (#93) * update user profile (and notify contacts) * add concurrently to profile update test for better layout --- src/Simplex/Chat.hs | 42 +++++++++++---- src/Simplex/Chat/Controller.hs | 2 +- src/Simplex/Chat/Store.hs | 55 +++++++++++++++++++ src/Simplex/Chat/View.hs | 42 +++++++++++++++ tests/ChatTests.hs | 97 ++++++++++++++++++++++++++-------- 5 files changed, 206 insertions(+), 32 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 89bc860b59..68bb15f167 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -6,6 +6,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} @@ -69,6 +70,8 @@ data ChatCommand | DeleteGroup GroupName | ListMembers GroupName | SendGroupMessage GroupName ByteString + | UpdateProfile Profile + | ShowProfile | QuitChat deriving (Show) @@ -106,14 +109,14 @@ simplexChat cfg opts t = newChatController :: WithTerminal t => ChatConfig -> ChatOpts -> t -> (Notification -> IO ()) -> IO ChatController newChatController ChatConfig {agentConfig = cfg, dbPoolSize, tbqSize} ChatOpts {dbFile, smpServers} t sendNotification = do chatStore <- createStore (dbFile <> ".chat.db") dbPoolSize - currentUser <- getCreateActiveUser chatStore + currentUser <- newTVarIO =<< getCreateActiveUser chatStore chatTerminal <- newChatTerminal t smpAgent <- getSMPAgentClient cfg {dbFile = dbFile <> ".agent.db", smpServers} idsDrg <- newTVarIO =<< drgNew inputQ <- newTBQueueIO tbqSize notifyQ <- newTBQueueIO tbqSize chatLock <- newTMVarIO () - pure ChatController {currentUser, smpAgent, chatTerminal, chatStore, idsDrg, inputQ, notifyQ, sendNotification, chatLock} + pure ChatController {..} runSimplexChat :: ChatController -> IO () runSimplexChat = runReaderT (race_ runTerminalInput runChatController) @@ -147,7 +150,7 @@ inputSubscriber = do SendMessage c msg -> showSentMessage c msg SendGroupMessage g msg -> showSentGroupMessage g msg _ -> printToView [plain s] - user <- asks currentUser + user <- readTVarIO =<< asks currentUser withLock l . void . runExceptT $ processChatCommand user cmd `catchError` showChatError @@ -244,6 +247,13 @@ processChatCommand user@User {userId, profile} = \case let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] sendGroupMessage members msgEvent setActive $ ActiveG gName + UpdateProfile p -> unless (p == profile) $ do + user' <- withStore $ \st -> updateUserProfile st user p + asks currentUser >>= atomically . (`writeTVar` user') + contacts <- withStore (`getUserContacts` user) + forM_ contacts $ \ct -> sendDirectMessage (contactConnId ct) $ XInfo p + showUserProfileUpdated user user' + ShowProfile -> showUserProfile profile QuitChat -> liftIO exitSuccess where contactMember :: Contact -> [GroupMember] -> Maybe GroupMember @@ -258,14 +268,14 @@ agentSubscriber = do subscribeUserConnections forever $ do (_, connId, msg) <- atomically $ readTBQueue q - user <- asks currentUser + user <- readTVarIO =<< asks currentUser -- TODO handle errors properly withLock l . void . runExceptT $ processAgentMessage user connId msg `catchError` (liftIO . print) subscribeUserConnections :: (MonadUnliftIO m, MonadReader ChatController m) => m () subscribeUserConnections = void . runExceptT $ do - user <- asks currentUser + user <- readTVarIO =<< asks currentUser subscribeContacts user subscribeGroups user where @@ -334,7 +344,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of XMsgNew (MsgContent MTText [] body) -> newTextMessage c meta $ find (isSimplexContentType XCText) body - XInfo _ -> pure () -- TODO profile update + XInfo p -> xInfo ct p XGrpInv gInv -> processGroupInvitation ct gInv XInfoProbe probe -> xInfoProbe ct probe XInfoProbeCheck probeHash -> xInfoProbeCheck ct probeHash @@ -511,6 +521,11 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles group <- withStore $ \st -> createGroupInvitation st user ct inv showReceivedGroupInvitation group localDisplayName memRole + xInfo :: Contact -> Profile -> m () + xInfo c@Contact {profile = p} p' = unless (p == p') $ do + c' <- withStore $ \st -> updateContactProfile st userId c p' + showContactUpdated c c' + xInfoProbe :: Contact -> ByteString -> m () xInfoProbe c2 probe = do r <- withStore $ \st -> matchReceivedProbe st userId c2 probe @@ -722,7 +737,7 @@ getCreateActiveUser st = do pure user userStr :: User -> String userStr User {localDisplayName, profile = Profile {fullName}} = - T.unpack $ localDisplayName <> if T.null fullName then "" else " (" <> fullName <> ")" + T.unpack $ localDisplayName <> if T.null fullName || localDisplayName == fullName then "" else " (" <> fullName <> ")" getContactName :: IO ContactName getContactName = do displayName <- getWithPrompt "display name (no spaces)" @@ -771,14 +786,23 @@ chatCommandP = <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> displayName) <|> A.char '@' *> (SendMessage <$> displayName <*> (A.space *> A.takeByteString)) <|> ("/markdown" <|> "/m") $> MarkdownHelp + <|> ("/profile " <|> "/p ") *> (UpdateProfile <$> userProfile) + <|> ("/profile" <|> "/p") $> ShowProfile <|> ("/quit" <|> "/q") $> QuitChat where displayName = safeDecodeUtf8 <$> (B.cons <$> A.satisfy refChar <*> A.takeTill (== ' ')) refChar c = c > ' ' && c /= '#' && c /= '@' + userProfile = do + cName <- displayName + fullName <- fullNameP cName + pure Profile {displayName = cName, fullName} groupProfile = do gName <- displayName - fullName' <- safeDecodeUtf8 <$> (A.space *> A.takeByteString) <|> pure "" - pure GroupProfile {displayName = gName, fullName = if T.null fullName' then gName else fullName'} + fullName <- fullNameP gName + pure GroupProfile {displayName = gName, fullName} + fullNameP name = do + n <- (A.space *> A.takeByteString) <|> pure "" + pure $ if B.null n then name else safeDecodeUtf8 n memberRole = (" owner" $> GROwner) <|> (" admin" $> GRAdmin) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 395f62d963..b67bfbab7b 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -21,7 +21,7 @@ import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) import UnliftIO.STM data ChatController = ChatController - { currentUser :: User, + { currentUser :: TVar User, smpAgent :: AgentClient, chatTerminal :: ChatTerminal, chatStore :: SQLiteStore, diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 607e07ba1e..95f6dae0ef 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -24,6 +24,8 @@ module Simplex.Chat.Store getContactGroupNames, deleteContact, getContact, + updateUserProfile, + updateContactProfile, getUserContacts, getContactConnections, getConnectionChatDirection, @@ -69,6 +71,7 @@ import Data.ByteString.Char8 (ByteString) import Data.Either (rights) import Data.FileEmbed (embedDir, makeRelativeToProject) import Data.Function (on) +import Data.Functor (($>)) import Data.Int (Int64) import Data.List (find, sortBy) import Data.Maybe (listToMaybe) @@ -232,6 +235,58 @@ getContact :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m Contact getContact st userId localDisplayName = liftIOEither . withTransaction st $ \db -> runExceptT $ getContact_ db userId localDisplayName +updateUserProfile :: StoreMonad m => SQLiteStore -> User -> Profile -> m User +updateUserProfile st u@User {userId, userContactId, localDisplayName, profile = Profile {displayName}} p'@Profile {displayName = newName} + | displayName == newName = + liftIO . withTransaction st $ \db -> + updateContactProfile_ db userId userContactId p' $> (u :: User) {profile = p'} + | otherwise = + liftIOEither . checkConstraint SEDuplicateName . withTransaction st $ \db -> do + DB.execute db "UPDATE users SET local_display_name = ? WHERE user_id = ?" (newName, userId) + DB.execute db "INSERT INTO display_names (local_display_name, ldn_base, user_id) VALUES (?, ?, ?)" (newName, newName, userId) + updateContactProfile_ db userId userContactId p' + updateContact_ db userId userContactId localDisplayName newName + pure . Right $ (u :: User) {localDisplayName = newName, profile = p'} + +updateContactProfile :: StoreMonad m => SQLiteStore -> UserId -> Contact -> Profile -> m Contact +updateContactProfile st userId c@Contact {contactId, localDisplayName, profile = Profile {displayName}} p'@Profile {displayName = newName} + | displayName == newName = + liftIO . withTransaction st $ \db -> + updateContactProfile_ db userId contactId p' $> (c :: Contact) {profile = p'} + | otherwise = + liftIOEither . withTransaction st $ \db -> + withLocalDisplayName db userId newName $ \ldn -> do + updateContactProfile_ db userId contactId p' + updateContact_ db userId contactId localDisplayName ldn + pure $ (c :: Contact) {localDisplayName = ldn, profile = p'} + +updateContactProfile_ :: DB.Connection -> UserId -> Int64 -> Profile -> IO () +updateContactProfile_ db userId contactId Profile {displayName, fullName} = + DB.executeNamed + db + [sql| + UPDATE contact_profiles + SET display_name = :display_name, + full_name = :full_name + WHERE contact_profile_id IN ( + SELECT contact_profile_id + FROM contacts + WHERE user_id = :user_id + AND contact_id = :contact_id + ) + |] + [ ":display_name" := displayName, + ":full_name" := fullName, + ":user_id" := userId, + ":contact_id" := contactId + ] + +updateContact_ :: DB.Connection -> UserId -> Int64 -> ContactName -> ContactName -> IO () +updateContact_ db userId contactId displayName newName = do + DB.execute db "UPDATE contacts SET local_display_name = ? WHERE user_id = ? AND contact_id = ?" (newName, userId, contactId) + DB.execute db "UPDATE group_members SET local_display_name = ? WHERE user_id = ? AND contact_id = ?" (newName, userId, contactId) + DB.execute db "DELETE FROM display_names WHERE local_display_name = ? AND user_id = ?" (displayName, userId) + -- TODO return the last connection that is ready, not any last connection -- requires updating connection status getContact_ :: DB.Connection -> UserId -> ContactName -> ExceptT StoreError IO Contact diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 4af608f959..608cac4811 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -39,6 +39,9 @@ module Simplex.Chat.View showLeftMember, showGroupMembers, showContactsMerged, + showUserProfile, + showUserProfileUpdated, + showContactUpdated, safeDecodeUtf8, ) where @@ -166,6 +169,15 @@ showGroupMembers = printToView . groupMembers showContactsMerged :: ChatReader m => Contact -> Contact -> m () showContactsMerged = printToView .: contactsMerged +showUserProfile :: ChatReader m => Profile -> m () +showUserProfile = printToView . userProfile + +showUserProfileUpdated :: ChatReader m => User -> User -> m () +showUserProfileUpdated = printToView .: userProfileUpdated + +showContactUpdated :: ChatReader m => Contact -> Contact -> m () +showContactUpdated = printToView .: contactUpdated + invitation :: SMPQueueInfo -> [StyledString] invitation qInfo = [ "pass this invitation to your contact (via another channel): ", @@ -302,6 +314,36 @@ contactsMerged _to@Contact {localDisplayName = c1} _from@Contact {localDisplayNa "use " <> ttyToContact c1 <> highlight' "" <> " to send messages" ] +userProfile :: Profile -> [StyledString] +userProfile Profile {displayName, fullName} = + [ "user profile: " <> ttyFullName displayName fullName, + "use " <> highlight' "/p [ ]" <> " to change it", + "(the updated profile will be sent to all your contacts)" + ] + +userProfileUpdated :: User -> User -> [StyledString] +userProfileUpdated + User {localDisplayName = n, profile = Profile {fullName}} + User {localDisplayName = n', profile = Profile {fullName = fullName'}} + | n == n' && fullName == fullName' = [] + | n == n' = ["user full name " <> (if T.null fullName' || fullName' == n' then "removed" else "changed to " <> plain fullName') <> notified] + | otherwise = ["user profile is changed to " <> ttyFullName n' fullName' <> notified] + where + notified = " (your contacts are notified)" + +contactUpdated :: Contact -> Contact -> [StyledString] +contactUpdated + Contact {localDisplayName = n, profile = Profile {fullName}} + Contact {localDisplayName = n', profile = Profile {fullName = fullName'}} + | n == n' && fullName == fullName' = [] + | n == n' = ["contact " <> ttyContact n <> fullNameUpdate] + | otherwise = + [ "contact " <> ttyContact n <> " changed to " <> ttyFullName n' fullName', + "use " <> ttyToContact n' <> highlight' "" <> " to send messages" + ] + where + fullNameUpdate = if T.null fullName' || fullName' == n' then " removed full name" else " updated full name: " <> plain fullName' + receivedMessage :: StyledString -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage from utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 3053d52763..ad5c4ff08e 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -35,6 +35,8 @@ chatTests = do it "create and join group with 4 members" testGroup2 it "create and delete group" testGroupDelete it "remove contact from group and add again" testGroupRemoveAdd + describe "user profiles" $ + it "update user profiles and notify contacts" testUpdateProfile testAddContact :: IO () testAddContact = @@ -350,65 +352,116 @@ testGroupRemoveAdd = (alice <# "#team cath> hello") (bob <# "#team_1 cath> hello") +testUpdateProfile :: IO () +testUpdateProfile = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + alice ##> "/p" + alice <## "user profile: alice (Alice)" + alice <## "use /p [ ] to change it" + alice <## "(the updated profile will be sent to all your contacts)" + alice ##> "/p alice" + concurrentlyN_ + [ alice <## "user full name removed (your contacts are notified)", + bob <## "contact alice removed full name", + cath <## "contact alice removed full name" + ] + alice ##> "/p alice Alice Jones" + concurrentlyN_ + [ alice <## "user full name changed to Alice Jones (your contacts are notified)", + bob <## "contact alice updated full name: Alice Jones", + cath <## "contact alice updated full name: Alice Jones" + ] + cath ##> "/p cate" + concurrentlyN_ + [ cath <## "user profile is changed to cate (your contacts are notified)", + do + alice <## "contact cath changed to cate" + alice <## "use @cate to send messages", + do + bob <## "contact cath changed to cate" + bob <## "use @cate to send messages" + ] + cath ##> "/p cat Cate" + concurrentlyN_ + [ cath <## "user profile is changed to cat (Cate) (your contacts are notified)", + do + alice <## "contact cate changed to cat (Cate)" + alice <## "use @cat to send messages", + do + bob <## "contact cate changed to cat (Cate)" + bob <## "use @cat to send messages" + ] + connectUsers :: TestCC -> TestCC -> IO () connectUsers cc1 cc2 = do + name1 <- showName cc1 + name2 <- showName cc2 cc1 ##> "/c" inv <- getInvitation cc1 cc2 ##> ("/c " <> inv) concurrently_ - (cc2 <## (showName cc1 <> ": contact is connected")) - (cc1 <## (showName cc2 <> ": contact is connected")) + (cc2 <## (name1 <> ": contact is connected")) + (cc1 <## (name2 <> ": contact is connected")) -showName :: TestCC -> String -showName (TestCC ChatController {currentUser = User {localDisplayName, profile = Profile {fullName}}} _ _ _ _) = - T.unpack $ localDisplayName <> " (" <> fullName <> ")" +showName :: TestCC -> IO String +showName (TestCC ChatController {currentUser} _ _ _ _) = do + User {localDisplayName, profile = Profile {fullName}} <- readTVarIO currentUser + pure . T.unpack $ localDisplayName <> " (" <> fullName <> ")" createGroup3 :: String -> TestCC -> TestCC -> TestCC -> IO () createGroup3 gName cc1 cc2 cc3 = do connectUsers cc1 cc2 connectUsers cc1 cc3 + name2 <- userName cc2 + name3 <- userName cc3 + sName2 <- showName cc2 + sName3 <- showName cc3 cc1 ##> ("/g " <> gName) cc1 <## ("group #" <> gName <> " is created") cc1 <## ("use /a " <> gName <> " to add members") addMember cc2 cc2 ##> ("/j " <> gName) concurrently_ - (cc1 <## ("#" <> gName <> ": " <> name cc2 <> " joined the group")) + (cc1 <## ("#" <> gName <> ": " <> name2 <> " joined the group")) (cc2 <## ("#" <> gName <> ": you joined the group")) addMember cc3 cc3 ##> ("/j " <> gName) concurrentlyN_ - [ cc1 <## ("#" <> gName <> ": " <> name cc3 <> " joined the group"), + [ cc1 <## ("#" <> gName <> ": " <> name3 <> " joined the group"), do cc3 <## ("#" <> gName <> ": you joined the group") - cc3 <## ("#" <> gName <> ": member " <> showName cc2 <> " is connected"), + cc3 <## ("#" <> gName <> ": member " <> sName2 <> " is connected"), do - cc2 <## ("#" <> gName <> ": alice added " <> showName cc3 <> " to the group (connecting...)") - cc2 <## ("#" <> gName <> ": new member " <> name cc3 <> " is connected") + cc2 <## ("#" <> gName <> ": alice added " <> sName3 <> " to the group (connecting...)") + cc2 <## ("#" <> gName <> ": new member " <> name3 <> " is connected") ] where addMember :: TestCC -> IO () addMember mem = do - cc1 ##> ("/a " <> gName <> " " <> name mem) + name1 <- userName cc1 + memName <- userName mem + cc1 ##> ("/a " <> gName <> " " <> memName) concurrentlyN_ - [ cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> name mem), + [ cc1 <## ("invitation to join the group #" <> gName <> " sent to " <> memName), do - mem <## ("#" <> gName <> ": " <> name cc1 <> " invites you to join the group as admin") + mem <## ("#" <> gName <> ": " <> name1 <> " invites you to join the group as admin") mem <## ("use /j " <> gName <> " to accept") ] - name :: TestCC -> String - name (TestCC ChatController {currentUser = User {localDisplayName}} _ _ _ _) = - T.unpack localDisplayName -- | test sending direct messages (<##>) :: TestCC -> TestCC -> IO () cc1 <##> cc2 = do - cc1 #> ("@" <> name cc2 <> " hi") - cc2 <# (name cc1 <> "> hi") - cc2 #> ("@" <> name cc1 <> " hey") - cc1 <# (name cc2 <> "> hey") - where - name (TestCC ChatController {currentUser = User {localDisplayName}} _ _ _ _) = T.unpack localDisplayName + name1 <- userName cc1 + name2 <- userName cc2 + cc1 #> ("@" <> name2 <> " hi") + cc2 <# (name1 <> "> hi") + cc2 #> ("@" <> name1 <> " hey") + cc1 <# (name2 <> "> hey") + +userName :: TestCC -> IO [Char] +userName (TestCC ChatController {currentUser} _ _ _ _) = T.unpack . localDisplayName <$> readTVarIO currentUser (##>) :: TestCC -> String -> IO () cc ##> cmd = do From 97fde7ecd0efc264d9b78bbf8009f558e6c37722 Mon Sep 17 00:00:00 2001 From: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> Date: Sat, 28 Aug 2021 20:54:53 +1000 Subject: [PATCH 22/29] subscribe pending connections on chat start (#95) --- src/Simplex/Chat.hs | 5 +++++ src/Simplex/Chat/Store.hs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 68bb15f167..4db11757e8 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -278,6 +278,7 @@ subscribeUserConnections = void . runExceptT $ do user <- readTVarIO =<< asks currentUser subscribeContacts user subscribeGroups user + subscribePendingConnections user where subscribeContacts user = do contacts <- withStore (`getUserContacts` user) @@ -296,6 +297,10 @@ subscribeUserConnections = void . runExceptT $ do forM_ connectedMembers $ \(GroupMember {localDisplayName = c}, cId) -> subscribe cId `catchError` showMemberSubError g c showGroupSubscribed g + subscribePendingConnections user = do + connections <- withStore (`getPendingConnections` user) + forM_ connections $ \Connection {agentConnId} -> + subscribe agentConnId `catchError` \_ -> pure () subscribe cId = withAgent (`subscribeConnection` cId) processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 95f6dae0ef..a5f529eb50 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -27,6 +27,7 @@ module Simplex.Chat.Store updateUserProfile, updateContactProfile, getUserContacts, + getPendingConnections, getContactConnections, getConnectionChatDirection, updateConnectionStatus, @@ -336,6 +337,22 @@ getUserContacts st User {userId} = contactNames <- liftIO $ map fromOnly <$> DB.query db "SELECT local_display_name FROM contacts WHERE user_id = ?" (Only userId) rights <$> mapM (runExceptT . getContact_ db userId) contactNames +getPendingConnections :: MonadUnliftIO m => SQLiteStore -> User -> m [Connection] +getPendingConnections st User {userId} = + liftIO . withTransaction st $ \db -> + map toConnection + <$> DB.queryNamed + db + [sql| + SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + FROM connections c + WHERE c.user_id = :user_id + AND c.conn_type = :conn_type + AND c.contact_id IS NULL + |] + [":user_id" := userId, ":conn_type" := ConnContact] + getContactConnections :: StoreMonad m => SQLiteStore -> UserId -> ContactName -> m [Connection] getContactConnections st userId displayName = liftIOEither . withTransaction st $ \db -> From c51493e01647b8da402f023490b82080890d281c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 4 Sep 2021 07:32:56 +0100 Subject: [PATCH 23/29] send files to contacts (#94) * schema for sending files * send file "invitation" * receive file "invitation" * send/receive file flow (with stubs) * update simplexmq * send and receive the file (WIP - only the first chunk) * sending and receiving file works (but it is slow) * use correct terminal output for file sending/receiving * improve file transfer, support cancellation * command to show file transfer status and progress * file transfer tests * resume file transfer on restart (WIP) * stabilize test of recipient cancelling file transfer * trying to improve file transfer on restart * update SMP block size and file chunk size * acquire agent lock before chat lock to test whether it avoids deadlock * fix resuming sending file on client restart * manual message ACK (prevents losing messages between agent and chat client and stabilizes resuming file reception after restart) * do NOT send file chunk after receiving it before it is appended to the file * update file chunk size for SMP block size 8192 (set in smpDefaultConfig) * save received files to ~/Downloads folder by default; create empty file when file is accepted * keep file handle used to create empty file * check message integrity * fix trying to resume sending file when it was not yet accepted * fix subscribing to pending connections on start * update simplexmq (fixes smp-server syntax parser) --- migrations/20210612_initial.sql | 55 ++++- src/Simplex/Chat.hs | 327 +++++++++++++++++++++++++--- src/Simplex/Chat/Controller.hs | 25 ++- src/Simplex/Chat/Protocol.hs | 20 ++ src/Simplex/Chat/Store.hs | 367 +++++++++++++++++++++++++++++--- src/Simplex/Chat/Styled.hs | 4 + src/Simplex/Chat/Types.hs | 78 ++++++- src/Simplex/Chat/Util.hs | 6 + src/Simplex/Chat/View.hs | 182 +++++++++++++++- src/Simplex/Chat/protocol.md | 1 + stack.yaml | 3 +- tests/ChatClient.hs | 4 +- tests/ChatTests.hs | 90 +++++++- tests/fixtures/test.jpg | Bin 0 -> 139737 bytes 14 files changed, 1088 insertions(+), 74 deletions(-) create mode 100644 tests/fixtures/test.jpg diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index 86abb601c6..933ab1e604 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -130,17 +130,68 @@ CREATE TABLE group_member_intros ( UNIQUE (re_group_member_id, to_group_member_id) ); +CREATE TABLE files ( + file_id INTEGER PRIMARY KEY, + contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, + group_id INTEGER REFERENCES groups ON DELETE RESTRICT, + file_name TEXT NOT NULL, + file_path TEXT, + file_size INTEGER NOT NULL, + chunk_size INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + user_id INTEGER NOT NULL REFERENCES users +); + +CREATE TABLE snd_files ( + file_id INTEGER NOT NULL REFERENCES files ON DELETE RESTRICT, + connection_id INTEGER NOT NULL REFERENCES connections ON DELETE RESTRICT, + file_status TEXT NOT NULL, -- new, accepted, connected, completed + group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, + PRIMARY KEY (file_id, connection_id) +) WITHOUT ROWID; + +CREATE TABLE rcv_files ( + file_id INTEGER PRIMARY KEY REFERENCES files ON DELETE RESTRICT, + file_status TEXT NOT NULL, -- new, accepted, connected, completed + group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, + file_queue_info BLOB +); + +CREATE TABLE snd_file_chunks ( + file_id INTEGER NOT NULL, + connection_id INTEGER NOT NULL, + chunk_number INTEGER NOT NULL, + chunk_agent_msg_id INTEGER, + chunk_sent INTEGER NOT NULL DEFAULT 0, -- 0 (sent to agent), 1 (sent to server) + FOREIGN KEY (file_id, connection_id) REFERENCES snd_files ON DELETE CASCADE, + PRIMARY KEY (file_id, connection_id, chunk_number) +) WITHOUT ROWID; + +CREATE TABLE rcv_file_chunks ( + file_id INTEGER NOT NULL REFERENCES rcv_files, + chunk_number INTEGER NOT NULL, + chunk_agent_msg_id INTEGER NOT NULL, + chunk_stored INTEGER NOT NULL DEFAULT 0, -- 0 (received), 1 (appended to file) + PRIMARY KEY (file_id, chunk_number) +) WITHOUT ROWID; + CREATE TABLE connections ( -- all SMP agent connections connection_id INTEGER PRIMARY KEY, agent_conn_id BLOB NOT NULL UNIQUE, conn_level INTEGER NOT NULL DEFAULT 0, via_contact INTEGER REFERENCES contacts (contact_id), conn_status TEXT NOT NULL, - conn_type TEXT NOT NULL, -- contact, member, member_direct + conn_type TEXT NOT NULL, -- contact, member, rcv_file, snd_file contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, + snd_file_id INTEGER, + rcv_file_id INTEGER REFERENCES rcv_files (file_id) ON DELETE RESTRICT, created_at TEXT NOT NULL DEFAULT (datetime('now')), - user_id INTEGER NOT NULL REFERENCES users + user_id INTEGER NOT NULL REFERENCES users, + FOREIGN KEY (snd_file_id, connection_id) + REFERENCES snd_files (file_id, connection_id) + ON DELETE RESTRICT + DEFERRABLE INITIALLY DEFERRED ); CREATE TABLE events ( -- messages received by the agent, append only diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 4db11757e8..f8023291fb 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -12,7 +12,8 @@ module Simplex.Chat where -import Control.Applicative ((<|>)) +import Control.Applicative (optional, (<|>)) +import Control.Concurrent.STM (stateTVar) import Control.Logger.Simple import Control.Monad.Except import Control.Monad.IO.Unlift @@ -24,12 +25,14 @@ import Data.Bifunctor (first) import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.Functor (($>)) +import Data.Int (Int64) import Data.List (find) +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as M import Data.Maybe (isJust, mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) -import Numeric.Natural import Simplex.Chat.Controller import Simplex.Chat.Help import Simplex.Chat.Input @@ -40,18 +43,24 @@ import Simplex.Chat.Store import Simplex.Chat.Styled (plain) import Simplex.Chat.Terminal import Simplex.Chat.Types +import Simplex.Chat.Util (ifM, unlessM) import Simplex.Chat.View import Simplex.Messaging.Agent import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), defaultAgentConfig) import Simplex.Messaging.Agent.Protocol import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Parsers (parseAll) -import Simplex.Messaging.Util (raceAny_) +import qualified Simplex.Messaging.Protocol as SMP +import Simplex.Messaging.Util (bshow, raceAny_) import System.Exit (exitFailure, exitSuccess) -import System.IO (hFlush, stdout) +import System.FilePath (combine, splitExtensions, takeFileName) +import System.IO (Handle, IOMode (..), SeekMode (..), hFlush, openFile, stdout) import Text.Read (readMaybe) import UnliftIO.Async (race_) +import UnliftIO.Concurrent (forkIO, threadDelay) +import UnliftIO.Directory (doesDirectoryExist, doesFileExist, getFileSize, getHomeDirectory, getTemporaryDirectory) import qualified UnliftIO.Exception as E +import UnliftIO.IO (hClose, hSeek, hTell) import UnliftIO.STM data ChatCommand @@ -70,17 +79,16 @@ data ChatCommand | DeleteGroup GroupName | ListMembers GroupName | SendGroupMessage GroupName ByteString + | SendFile ContactName FilePath + | SendGroupFile GroupName FilePath + | ReceiveFile Int64 (Maybe FilePath) + | CancelFile Int64 + | FileStatus Int64 | UpdateProfile Profile | ShowProfile | QuitChat deriving (Show) -data ChatConfig = ChatConfig - { agentConfig :: AgentConfig, - dbPoolSize :: Int, - tbqSize :: Natural - } - defaultChatConfig :: ChatConfig defaultChatConfig = ChatConfig @@ -92,7 +100,8 @@ defaultChatConfig = dbPoolSize = 1 }, dbPoolSize = 1, - tbqSize = 16 + tbqSize = 16, + fileChunkSize = 7050 } logCfg :: LogConfig @@ -107,7 +116,7 @@ simplexChat cfg opts t = >>= runSimplexChat newChatController :: WithTerminal t => ChatConfig -> ChatOpts -> t -> (Notification -> IO ()) -> IO ChatController -newChatController ChatConfig {agentConfig = cfg, dbPoolSize, tbqSize} ChatOpts {dbFile, smpServers} t sendNotification = do +newChatController config@ChatConfig {agentConfig = cfg, dbPoolSize, tbqSize} ChatOpts {dbFile, smpServers} t sendNotification = do chatStore <- createStore (dbFile <> ".chat.db") dbPoolSize currentUser <- newTVarIO =<< getCreateActiveUser chatStore chatTerminal <- newChatTerminal t @@ -116,6 +125,8 @@ newChatController ChatConfig {agentConfig = cfg, dbPoolSize, tbqSize} ChatOpts { inputQ <- newTBQueueIO tbqSize notifyQ <- newTBQueueIO tbqSize chatLock <- newTMVarIO () + sndFiles <- newTVarIO M.empty + rcvFiles <- newTVarIO M.empty pure ChatController {..} runSimplexChat :: ChatController -> IO () @@ -139,6 +150,7 @@ inputSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () inputSubscriber = do q <- asks inputQ l <- asks chatLock + a <- asks smpAgent forever $ atomically (readTBQueue q) >>= \case InputControl _ -> pure () @@ -151,10 +163,10 @@ inputSubscriber = do SendGroupMessage g msg -> showSentGroupMessage g msg _ -> printToView [plain s] user <- readTVarIO =<< asks currentUser - withLock l . void . runExceptT $ + withAgentLock a . withLock l . void . runExceptT $ processChatCommand user cmd `catchError` showChatError -processChatCommand :: ChatMonad m => User -> ChatCommand -> m () +processChatCommand :: forall m. ChatMonad m => User -> ChatCommand -> m () processChatCommand user@User {userId, profile} = \case ChatHelp -> printToView chatHelpInfo MarkdownHelp -> printToView markdownInfo @@ -247,6 +259,36 @@ processChatCommand user@User {userId, profile} = \case let msgEvent = XMsgNew $ MsgContent MTText [] [MsgContentBody {contentType = SimplexContentType XCText, contentData = msg}] sendGroupMessage members msgEvent setActive $ ActiveG gName + SendFile cName f -> do + unlessM (doesFileExist f) . chatError $ CEFileNotFound f + contact@Contact {contactId} <- withStore $ \st -> getContact st userId cName + (agentConnId, fileQInfo) <- withAgent createConnection + fileSize <- getFileSize f + let fileInv = FileInvitation {fileName = takeFileName f, fileSize, fileQInfo} + chSize <- asks $ fileChunkSize . config + ft <- withStore $ \st -> createSndFileTransfer st userId contactId f fileInv agentConnId chSize + sendDirectMessage (contactConnId contact) $ XFile fileInv + showSentFileInvitation cName ft + setActive $ ActiveC cName + SendGroupFile _gName _file -> pure () + ReceiveFile fileId filePath_ -> do + RcvFileTransfer {fileInvitation = FileInvitation {fileName, fileQInfo}, fileStatus} <- withStore $ \st -> getRcvFileTransfer st userId fileId + unless (fileStatus == RFSNew) . chatError $ CEFileAlreadyReceiving fileName + agentConnId <- withAgent $ \a -> joinConnection a fileQInfo . directMessage $ XFileAcpt fileName + filePath <- getRcvFilePath fileId filePath_ fileName + withStore $ \st -> acceptRcvFileTransfer st userId fileId agentConnId filePath + -- TODO include file sender in the message + showRcvFileAccepted fileId filePath + CancelFile fileId -> + withStore (\st -> getFileTransfer st userId fileId) >>= \case + FTSnd fts -> do + mapM_ cancelSndFileTransfer fts + showSndFileCancelled fileId + FTRcv ft -> do + cancelRcvFileTransfer ft + showRcvFileCancelled fileId + FileStatus fileId -> + withStore (\st -> getFileTransferProgress st userId fileId) >>= showFileTransferStatus UpdateProfile p -> unless (p == profile) $ do user' <- withStore $ \st -> updateUserProfile st user p asks currentUser >>= atomically . (`writeTVar` user') @@ -260,6 +302,37 @@ processChatCommand user@User {userId, profile} = \case contactMember Contact {contactId} = find $ \GroupMember {memberContactId = cId, memberStatus = s} -> cId == Just contactId && s /= GSMemRemoved && s /= GSMemLeft + getRcvFilePath :: Int64 -> Maybe FilePath -> String -> m FilePath + getRcvFilePath fileId filePath fileName = case filePath of + Nothing -> do + dir <- (`combine` "Downloads") <$> getHomeDirectory + ifM (doesDirectoryExist dir) (pure dir) getTemporaryDirectory + >>= (`uniqueCombine` fileName) + >>= createEmptyFile + Just fPath -> + ifM + (doesDirectoryExist fPath) + (fPath `uniqueCombine` fileName >>= createEmptyFile) + $ ifM + (doesFileExist fPath) + (chatError $ CEFileAlreadyExists fPath) + (createEmptyFile fPath) + where + createEmptyFile :: FilePath -> m FilePath + createEmptyFile fPath = emptyFile fPath `E.catch` (chatError . CEFileWrite fPath) + emptyFile :: FilePath -> m FilePath + emptyFile fPath = do + h <- getFileHandle fileId fPath rcvFiles AppendMode + liftIO $ B.hPut h "" >> hFlush h + pure fPath + uniqueCombine :: FilePath -> String -> m FilePath + uniqueCombine filePath fileName = tryCombine (0 :: Int) + where + tryCombine n = + let (name, ext) = splitExtensions fileName + suffix = if n == 0 then "" else "_" <> show n + f = filePath `combine` (name <> suffix <> ext) + in ifM (doesFileExist f) (tryCombine $ n + 1) (pure f) agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => m () agentSubscriber = do @@ -269,15 +342,15 @@ agentSubscriber = do forever $ do (_, connId, msg) <- atomically $ readTBQueue q user <- readTVarIO =<< asks currentUser - -- TODO handle errors properly withLock l . void . runExceptT $ - processAgentMessage user connId msg `catchError` (liftIO . print) + processAgentMessage user connId msg `catchError` showChatError subscribeUserConnections :: (MonadUnliftIO m, MonadReader ChatController m) => m () subscribeUserConnections = void . runExceptT $ do user <- readTVarIO =<< asks currentUser subscribeContacts user subscribeGroups user + subscribeFiles user subscribePendingConnections user where subscribeContacts user = do @@ -297,6 +370,27 @@ subscribeUserConnections = void . runExceptT $ do forM_ connectedMembers $ \(GroupMember {localDisplayName = c}, cId) -> subscribe cId `catchError` showMemberSubError g c showGroupSubscribed g + subscribeFiles user = do + withStore (`getLiveSndFileTransfers` user) >>= mapM_ subscribeSndFile + withStore (`getLiveRcvFileTransfers` user) >>= mapM_ subscribeRcvFile + where + subscribeSndFile ft@SndFileTransfer {fileId, fileStatus, agentConnId} = do + subscribe agentConnId `catchError` showSndFileSubError ft + void . forkIO $ do + threadDelay 1000000 + l <- asks chatLock + a <- asks smpAgent + unless (fileStatus == FSNew) . unlessM (isFileActive fileId sndFiles) $ + withAgentLock a . withLock l $ + sendFileChunk ft + subscribeRcvFile ft@RcvFileTransfer {fileStatus} = + case fileStatus of + RFSAccepted fInfo -> resume fInfo + RFSConnected fInfo -> resume fInfo + _ -> pure () + where + resume RcvFileInfo {agentConnId} = + subscribe agentConnId `catchError` showRcvFileSubError ft subscribePendingConnections user = do connections <- withStore (`getPendingConnections` user) forM_ connections $ \Connection {agentConnId} -> @@ -304,7 +398,7 @@ subscribeUserConnections = void . runExceptT $ do subscribe cId = withAgent (`subscribeConnection` cId) processAgentMessage :: forall m. ChatMonad m => User -> ConnId -> ACommand 'Agent -> m () -processAgentMessage user@User {userId, profile} agentConnId agentMessage = unless (sent agentMessage) $ do +processAgentMessage user@User {userId, profile} agentConnId agentMessage = do chatDirection <- withStore $ \st -> getConnectionChatDirection st user agentConnId forM_ (agentMsgConnStatus agentMessage) $ \status -> withStore $ \st -> updateConnectionStatus st (fromConnection chatDirection) status @@ -313,11 +407,11 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles processDirectMessage agentMessage conn maybeContact ReceivedGroupMessage conn gName m -> processGroupMessage agentMessage conn gName m + RcvFileConnection conn ft -> + processRcvFileConn agentMessage conn ft + SndFileConnection conn ft -> + processSndFileConn agentMessage conn ft where - sent :: ACommand 'Agent -> Bool - sent SENT {} = True - sent _ = False - isMember :: MemberId -> Group -> Bool isMember memId Group {membership, members} = memberId membership == memId || isJust (find ((== memId) . memberId) members) @@ -343,12 +437,15 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles acceptAgentConnection conn confId $ XInfo profile INFO connInfo -> saveConnInfo conn connInfo + MSG meta _ -> + withAckMessage agentConnId meta $ pure () _ -> pure () Just ct@Contact {localDisplayName = c} -> case agentMsg of - MSG meta msgBody -> do + MSG meta msgBody -> withAckMessage agentConnId meta $ do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of XMsgNew (MsgContent MTText [] body) -> newTextMessage c meta $ find (isSimplexContentType XCText) body + XFile fInv -> processFileInvitation ct fInv XInfo p -> xInfo ct p XGrpInv gInv -> processGroupInvitation ct gInv XInfoProbe probe -> xInfoProbe ct probe @@ -461,7 +558,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles when (contactIsReady ct) $ do notifyMemberConnected gName m when (memberCategory m == GCPreMember) $ probeMatchingContacts ct - MSG meta msgBody -> do + MSG meta msgBody -> withAckMessage agentConnId meta $ do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of XMsgNew (MsgContent MTText [] body) -> @@ -476,6 +573,81 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles _ -> messageError $ "unsupported message: " <> T.pack (show chatMsgEvent) _ -> pure () + processSndFileConn :: ACommand 'Agent -> Connection -> SndFileTransfer -> m () + processSndFileConn agentMsg conn ft@SndFileTransfer {fileId, fileName, fileStatus} = + case agentMsg of + REQ confId connInfo -> do + ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage connInfo + case chatMsgEvent of + XFileAcpt name + | name == fileName -> do + withStore $ \st -> updateSndFileStatus st ft FSAccepted + acceptAgentConnection conn confId XOk + | otherwise -> messageError "x.file.acpt: fileName is different from expected" + _ -> messageError "REQ from file connection must have x.file.acpt" + CON -> do + withStore $ \st -> updateSndFileStatus st ft FSConnected + showSndFileStart fileId + sendFileChunk ft + SENT msgId -> do + withStore $ \st -> updateSndFileChunkSent st ft msgId + unless (fileStatus == FSCancelled) $ sendFileChunk ft + MERR _ err -> do + cancelSndFileTransfer ft + case err of + SMP SMP.AUTH -> unless (fileStatus == FSCancelled) $ showSndFileRcvCancelled fileId + _ -> chatError $ CEFileSend fileId err + MSG meta _ -> + withAckMessage agentConnId meta $ pure () + _ -> pure () + + processRcvFileConn :: ACommand 'Agent -> Connection -> RcvFileTransfer -> m () + processRcvFileConn agentMsg _conn ft@RcvFileTransfer {fileId, chunkSize} = + case agentMsg of + CON -> do + withStore $ \st -> updateRcvFileStatus st ft FSConnected + showRcvFileStart fileId + MSG meta@MsgMeta {recipient = (msgId, _), integrity} msgBody -> withAckMessage agentConnId meta $ do + parseFileChunk msgBody >>= \case + (0, _) -> do + cancelRcvFileTransfer ft + showRcvFileSndCancelled fileId + (chunkNo, chunk) -> do + case integrity of + MsgOk -> pure () + MsgError MsgDuplicate -> pure () -- TODO remove once agent removes duplicates + MsgError e -> + badRcvFileChunk ft $ "invalid file chunk number " <> show chunkNo <> ": " <> show e + withStore (\st -> createRcvFileChunk st ft chunkNo msgId) >>= \case + RcvChunkOk -> + if B.length chunk /= fromInteger chunkSize + then badRcvFileChunk ft "incorrect chunk size" + else appendFileChunk ft chunkNo chunk + RcvChunkFinal -> + if B.length chunk > fromInteger chunkSize + then badRcvFileChunk ft "incorrect chunk size" + else do + appendFileChunk ft chunkNo chunk + withStore $ \st -> updateRcvFileStatus st ft FSComplete + showRcvFileComplete fileId + closeFileHandle fileId rcvFiles + withAgent (`deleteConnection` agentConnId) + RcvChunkDuplicate -> pure () + RcvChunkError -> badRcvFileChunk ft $ "incorrect chunk number " <> show chunkNo + _ -> pure () + + withAckMessage :: ConnId -> MsgMeta -> m () -> m () + withAckMessage cId MsgMeta {recipient = (msgId, _)} action = + action `E.finally` withAgent (\a -> ackMessage a cId msgId `catchError` \_ -> pure ()) + + badRcvFileChunk :: RcvFileTransfer -> String -> m () + badRcvFileChunk ft@RcvFileTransfer {fileStatus} err = + case fileStatus of + RFSCancelled _ -> pure () + _ -> do + cancelRcvFileTransfer ft + chatError $ CEFileRcvChunk err + notifyMemberConnected :: GroupName -> GroupMember -> m () notifyMemberConnected gName m@GroupMember {localDisplayName} = do showConnectedToGroupMember gName m @@ -496,10 +668,10 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles withStore $ \st -> createSentProbeHash st userId probeId c messageWarning :: Text -> m () - messageWarning = liftIO . print + messageWarning = showMessageError "warning" messageError :: Text -> m () - messageError = liftIO . print + messageError = showMessageError "error" newTextMessage :: ContactName -> MsgMeta -> Maybe MsgContentBody -> m () newTextMessage c meta = \case @@ -519,6 +691,14 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles setActive $ ActiveG gName _ -> messageError "x.msg.new: no expected message body" + processFileInvitation :: Contact -> FileInvitation -> m () + processFileInvitation Contact {contactId, localDisplayName = c} fInv = do + -- TODO chunk size has to be sent as part of invitation + chSize <- asks $ fileChunkSize . config + ft <- withStore $ \st -> createRcvFileTransfer st userId contactId fInv chSize + showReceivedFileInvitation c ft + setActive $ ActiveC c + processGroupInvitation :: Contact -> GroupInvitation -> m () processGroupInvitation ct@Contact {localDisplayName} inv@(GroupInvitation (fromMemId, fromRole) (memId, memRole) _ _) = do when (fromRole < GRAdmin || fromRole < memRole) $ chatError (CEGroupContactRole localDisplayName) @@ -662,7 +842,96 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = unles mapM_ deleteMemberConnection ms showGroupDeleted gName m -chatError :: ChatMonad m => ChatErrorType -> m () +sendFileChunk :: ChatMonad m => SndFileTransfer -> m () +sendFileChunk ft@SndFileTransfer {fileId, fileStatus, agentConnId} = + unless (fileStatus == FSComplete || fileStatus == FSCancelled) $ + withStore (`createSndFileChunk` ft) >>= \case + Just chunkNo -> sendFileChunkNo ft chunkNo + Nothing -> do + withStore $ \st -> updateSndFileStatus st ft FSComplete + showSndFileComplete fileId + closeFileHandle fileId sndFiles + withAgent (`deleteConnection` agentConnId) + +sendFileChunkNo :: ChatMonad m => SndFileTransfer -> Integer -> m () +sendFileChunkNo ft@SndFileTransfer {agentConnId} chunkNo = do + bytes <- readFileChunk ft chunkNo + msgId <- withAgent $ \a -> sendMessage a agentConnId $ serializeFileChunk chunkNo bytes + withStore $ \st -> updateSndFileChunkMsg st ft chunkNo msgId + +readFileChunk :: ChatMonad m => SndFileTransfer -> Integer -> m ByteString +readFileChunk SndFileTransfer {fileId, filePath, chunkSize} chunkNo = + read_ `E.catch` (chatError . CEFileRead filePath) + where + read_ = do + h <- getFileHandle fileId filePath sndFiles ReadMode + pos <- hTell h + let pos' = (chunkNo - 1) * chunkSize + when (pos /= pos') $ hSeek h AbsoluteSeek pos' + liftIO . B.hGet h $ fromInteger chunkSize + +parseFileChunk :: ChatMonad m => ByteString -> m (Integer, ByteString) +parseFileChunk msg = + liftEither . first (ChatError . CEFileRcvChunk) $ + parseAll ((,) <$> A.decimal <* A.space <*> A.takeByteString) msg + +serializeFileChunk :: Integer -> ByteString -> ByteString +serializeFileChunk chunkNo bytes = bshow chunkNo <> " " <> bytes + +appendFileChunk :: ChatMonad m => RcvFileTransfer -> Integer -> ByteString -> m () +appendFileChunk ft@RcvFileTransfer {fileId, fileStatus} chunkNo chunk = + case fileStatus of + RFSConnected RcvFileInfo {filePath} -> append_ filePath + RFSCancelled _ -> pure () + _ -> chatError $ CEFileInternal "receiving file transfer not in progress" + where + append_ fPath = do + h <- getFileHandle fileId fPath rcvFiles AppendMode + E.try (liftIO $ B.hPut h chunk >> hFlush h) >>= \case + Left e -> chatError $ CEFileWrite fPath e + Right () -> withStore $ \st -> updatedRcvFileChunkStored st ft chunkNo + +getFileHandle :: ChatMonad m => Int64 -> FilePath -> (ChatController -> TVar (Map Int64 Handle)) -> IOMode -> m Handle +getFileHandle fileId filePath files ioMode = do + fs <- asks files + h_ <- M.lookup fileId <$> readTVarIO fs + maybe (newHandle fs) pure h_ + where + newHandle fs = do + -- TODO handle errors + h <- liftIO (openFile filePath ioMode) + atomically . modifyTVar fs $ M.insert fileId h + pure h + +isFileActive :: ChatMonad m => Int64 -> (ChatController -> TVar (Map Int64 Handle)) -> m Bool +isFileActive fileId files = do + fs <- asks files + isJust . M.lookup fileId <$> readTVarIO fs + +cancelRcvFileTransfer :: ChatMonad m => RcvFileTransfer -> m () +cancelRcvFileTransfer ft@RcvFileTransfer {fileId, fileStatus} = do + closeFileHandle fileId rcvFiles + withStore $ \st -> updateRcvFileStatus st ft FSCancelled + case fileStatus of + RFSAccepted RcvFileInfo {agentConnId} -> withAgent (`suspendConnection` agentConnId) + RFSConnected RcvFileInfo {agentConnId} -> withAgent (`suspendConnection` agentConnId) + _ -> pure () + +cancelSndFileTransfer :: ChatMonad m => SndFileTransfer -> m () +cancelSndFileTransfer ft@SndFileTransfer {agentConnId, fileStatus} = + unless (fileStatus == FSCancelled || fileStatus == FSComplete) $ do + withStore $ \st -> updateSndFileStatus st ft FSCancelled + withAgent $ \a -> do + void $ sendMessage a agentConnId "0 " + suspendConnection a agentConnId + +closeFileHandle :: ChatMonad m => Int64 -> (ChatController -> TVar (Map Int64 Handle)) -> m () +closeFileHandle fileId files = do + fs <- asks files + h_ <- atomically . stateTVar fs $ \m -> (M.lookup fileId m, M.delete fileId m) + mapM_ hClose h_ `E.catch` \(_ :: E.SomeException) -> pure () + +chatError :: ChatMonad m => ChatErrorType -> m a chatError = throwError . ChatError deleteMemberConnection :: ChatMonad m => GroupMember -> m () @@ -790,6 +1059,11 @@ chatCommandP = <|> ("/connect" <|> "/c") $> AddContact <|> ("/delete @" <|> "/delete " <|> "/d @" <|> "/d ") *> (DeleteContact <$> displayName) <|> A.char '@' *> (SendMessage <$> displayName <*> (A.space *> A.takeByteString)) + <|> ("/file #" <|> "/f #") *> (SendGroupFile <$> displayName <* A.space <*> filePath) + <|> ("/file @" <|> "/file " <|> "/f @" <|> "/f ") *> (SendFile <$> displayName <* A.space <*> filePath) + <|> ("/file_receive " <|> "/fr ") *> (ReceiveFile <$> A.decimal <*> optional (A.space *> filePath)) + <|> ("/file_cancel " <|> "/fc ") *> (CancelFile <$> A.decimal) + <|> ("/file_status " <|> "/fs ") *> (FileStatus <$> A.decimal) <|> ("/markdown" <|> "/m") $> MarkdownHelp <|> ("/profile " <|> "/p ") *> (UpdateProfile <$> userProfile) <|> ("/profile" <|> "/p") $> ShowProfile @@ -808,6 +1082,7 @@ chatCommandP = fullNameP name = do n <- (A.space *> A.takeByteString) <|> pure "" pure $ if B.null n then name else safeDecodeUtf8 n + filePath = T.unpack . safeDecodeUtf8 <$> A.takeByteString memberRole = (" owner" $> GROwner) <|> (" admin" $> GRAdmin) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index b67bfbab7b..ae6dfd5e79 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -11,15 +11,27 @@ import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Reader import Crypto.Random (ChaChaDRG) +import Data.Int (Int64) +import Data.Map.Strict (Map) +import Numeric.Natural import Simplex.Chat.Notification import Simplex.Chat.Store (StoreError) import Simplex.Chat.Terminal import Simplex.Chat.Types import Simplex.Messaging.Agent (AgentClient) +import Simplex.Messaging.Agent.Env.SQLite (AgentConfig) import Simplex.Messaging.Agent.Protocol (AgentErrorType) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) +import System.IO (Handle) import UnliftIO.STM +data ChatConfig = ChatConfig + { agentConfig :: AgentConfig, + dbPoolSize :: Int, + tbqSize :: Natural, + fileChunkSize :: Integer + } + data ChatController = ChatController { currentUser :: TVar User, smpAgent :: AgentClient, @@ -29,7 +41,10 @@ data ChatController = ChatController inputQ :: TBQueue InputEvent, notifyQ :: TBQueue Notification, sendNotification :: Notification -> IO (), - chatLock :: TMVar () + chatLock :: TMVar (), + sndFiles :: TVar (Map Int64 Handle), + rcvFiles :: TVar (Map Int64 Handle), + config :: ChatConfig } data InputEvent = InputCommand String | InputControl Char @@ -51,6 +66,14 @@ data ChatErrorType | CEGroupMemberUserRemoved | CEGroupMemberNotFound ContactName | CEGroupInternal String + | CEFileNotFound String + | CEFileAlreadyReceiving String + | CEFileAlreadyExists FilePath + | CEFileRead FilePath SomeException + | CEFileWrite FilePath SomeException + | CEFileSend Int64 AgentErrorType + | CEFileRcvChunk String + | CEFileInternal String deriving (Show, Exception) type ChatMonad m = (MonadUnliftIO m, MonadReader ChatController m, MonadError ChatError m) diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index ee79c40aa7..2ccb8289c5 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -23,7 +23,10 @@ import qualified Data.ByteString.Lazy.Char8 as LB import Data.Int (Int64) import Data.List (find) import Data.Text (Text) +import qualified Data.Text as T +import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Types +import Simplex.Chat.Util (safeDecodeUtf8) import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Parsers (parseAll) import Simplex.Messaging.Util (bshow) @@ -33,6 +36,8 @@ data ChatDirection (p :: AParty) where SentDirectMessage :: Contact -> ChatDirection 'Client ReceivedGroupMessage :: Connection -> GroupName -> GroupMember -> ChatDirection 'Agent SentGroupMessage :: GroupName -> ChatDirection 'Client + SndFileConnection :: Connection -> SndFileTransfer -> ChatDirection 'Agent + RcvFileConnection :: Connection -> RcvFileTransfer -> ChatDirection 'Agent deriving instance Eq (ChatDirection p) @@ -42,9 +47,13 @@ fromConnection :: ChatDirection 'Agent -> Connection fromConnection = \case ReceivedDirectMessage conn _ -> conn ReceivedGroupMessage conn _ _ -> conn + SndFileConnection conn _ -> conn + RcvFileConnection conn _ -> conn data ChatMsgEvent = XMsgNew MsgContent + | XFile FileInvitation + | XFileAcpt String | XInfo Profile | XGrpInv GroupInvitation | XGrpAcpt MemberId @@ -100,6 +109,13 @@ toChatMessage RawChatMessage {chatMsgId, chatMsgEvent, chatMsgParams, chatMsgBod t <- toMsgType mt files <- mapM (toContentInfo <=< parseAll contentInfoP) rawFiles chatMsg . XMsgNew $ MsgContent {messageType = t, files, content = body} + ("x.file", [name, size, qInfo]) -> do + let fileName = T.unpack $ safeDecodeUtf8 name + fileSize <- parseAll A.decimal size + fileQInfo <- parseAll smpQueueInfoP qInfo + chatMsg . XFile $ FileInvitation {fileName, fileSize, fileQInfo} + ("x.file.acpt", [name]) -> + chatMsg . XFileAcpt . T.unpack $ safeDecodeUtf8 name ("x.info", []) -> do profile <- getJSON body chatMsg $ XInfo profile @@ -174,6 +190,10 @@ rawChatMessage ChatMessage {chatMsgId, chatMsgEvent, chatDAG} = XMsgNew MsgContent {messageType = t, files, content} -> let rawFiles = map (serializeContentInfo . rawContentInfo) files in rawMsg "x.msg.new" (rawMsgType t : rawFiles) content + XFile FileInvitation {fileName, fileSize, fileQInfo} -> + rawMsg "x.file" [encodeUtf8 $ T.pack fileName, bshow fileSize, serializeSmpQueueInfo fileQInfo] [] + XFileAcpt fileName -> + rawMsg "x.file.acpt" [encodeUtf8 $ T.pack fileName] [] XInfo profile -> rawMsg "x.info" [] [jsonBody profile] XGrpInv (GroupInvitation (fromMemId, fromRole) (memId, role) qInfo groupProfile) -> diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index a5f529eb50..4896ea56e8 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -7,6 +7,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} @@ -27,6 +28,9 @@ module Simplex.Chat.Store updateUserProfile, updateContactProfile, getUserContacts, + getLiveSndFileTransfers, + getLiveRcvFileTransfers, + getPendingSndChunks, getPendingConnections, getContactConnections, getConnectionChatDirection, @@ -58,6 +62,19 @@ module Simplex.Chat.Store matchReceivedProbeHash, matchSentProbe, mergeContactRecords, + createSndFileTransfer, + updateSndFileStatus, + createSndFileChunk, + updateSndFileChunkMsg, + updateSndFileChunkSent, + createRcvFileTransfer, + getRcvFileTransfer, + acceptRcvFileTransfer, + updateRcvFileStatus, + createRcvFileChunk, + updatedRcvFileChunkStored, + getFileTransfer, + getFileTransferProgress, ) where @@ -85,11 +102,11 @@ import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Protocol import Simplex.Chat.Types -import Simplex.Messaging.Agent.Protocol (AParty (..), ConnId, SMPQueueInfo) +import Simplex.Messaging.Agent.Protocol (AParty (..), AgentMsgId, ConnId, SMPQueueInfo) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore, withTransaction) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) import qualified Simplex.Messaging.Crypto as C -import Simplex.Messaging.Util (bshow, liftIOEither) +import Simplex.Messaging.Util (bshow, liftIOEither, (<$$>)) import System.FilePath (takeBaseName, takeExtension) import UnliftIO.STM @@ -315,7 +332,7 @@ getContact_ db userId localDisplayName = do db [sql| SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at FROM connections c WHERE c.user_id = :user_id AND c.contact_id == :contact_id ORDER BY c.connection_id DESC @@ -334,9 +351,58 @@ getContact_ db userId localDisplayName = do getUserContacts :: MonadUnliftIO m => SQLiteStore -> User -> m [Contact] getUserContacts st User {userId} = liftIO . withTransaction st $ \db -> do - contactNames <- liftIO $ map fromOnly <$> DB.query db "SELECT local_display_name FROM contacts WHERE user_id = ?" (Only userId) + contactNames <- map fromOnly <$> DB.query db "SELECT local_display_name FROM contacts WHERE user_id = ?" (Only userId) rights <$> mapM (runExceptT . getContact_ db userId) contactNames +getLiveSndFileTransfers :: MonadUnliftIO m => SQLiteStore -> User -> m [SndFileTransfer] +getLiveSndFileTransfers st User {userId} = + liftIO . withTransaction st $ \db -> do + fileIds :: [Int64] <- + map fromOnly + <$> DB.query + db + [sql| + SELECT DISTINCT f.file_id + FROM files f + JOIN snd_files s + WHERE f.user_id = ? AND s.file_status IN (?, ?, ?) + |] + (userId, FSNew, FSAccepted, FSConnected) + concatMap (filter liveTransfer) . rights <$> mapM (getSndFileTransfers_ db userId) fileIds + where + liveTransfer :: SndFileTransfer -> Bool + liveTransfer SndFileTransfer {fileStatus} = fileStatus `elem` [FSNew, FSAccepted, FSConnected] + +getLiveRcvFileTransfers :: MonadUnliftIO m => SQLiteStore -> User -> m [RcvFileTransfer] +getLiveRcvFileTransfers st User {userId} = + liftIO . withTransaction st $ \db -> do + fileIds :: [Int64] <- + map fromOnly + <$> DB.query + db + [sql| + SELECT f.file_id + FROM files f + JOIN rcv_files r + WHERE f.user_id = ? AND r.file_status IN (?, ?) + |] + (userId, FSAccepted, FSConnected) + rights <$> mapM (getRcvFileTransfer_ db userId) fileIds + +getPendingSndChunks :: MonadUnliftIO m => SQLiteStore -> Int64 -> Int64 -> m [Integer] +getPendingSndChunks st fileId connId = + liftIO . withTransaction st $ \db -> + map fromOnly + <$> DB.query + db + [sql| + SELECT chunk_number + FROM snd_file_chunks + WHERE file_id = ? AND connection_id = ? AND chunk_agent_msg_id IS NULL + ORDER BY chunk_number + |] + (fileId, connId) + getPendingConnections :: MonadUnliftIO m => SQLiteStore -> User -> m [Connection] getPendingConnections st User {userId} = liftIO . withTransaction st $ \db -> @@ -344,12 +410,12 @@ getPendingConnections st User {userId} = <$> DB.queryNamed db [sql| - SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at - FROM connections c - WHERE c.user_id = :user_id - AND c.conn_type = :conn_type - AND c.contact_id IS NULL + SELECT connection_id, agent_conn_id, conn_level, via_contact, + conn_status, conn_type, contact_id, group_member_id, snd_file_id, rcv_file_id, created_at + FROM connections + WHERE user_id = :user_id + AND conn_type = :conn_type + AND contact_id IS NULL |] [":user_id" := userId, ":conn_type" := ConnContact] @@ -361,7 +427,7 @@ getContactConnections st userId displayName = db [sql| SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at FROM connections c JOIN contacts cs ON c.contact_id == cs.contact_id WHERE c.user_id = :user_id @@ -373,22 +439,24 @@ getContactConnections st userId displayName = connections [] = Left $ SEContactNotFound displayName connections rows = Right $ map toConnection rows -type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, UTCTime) +type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, UTCTime) -type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Int64, Maybe Int64, Maybe UTCTime) +type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime) toConnection :: ConnectionRow -> Connection -toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) = +toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, sndFileId, rcvFileId, createdAt) = let entityId = entityId_ connType in Connection {connId, agentConnId, connLevel, viaContact, connStatus, connType, entityId, createdAt} where entityId_ :: ConnType -> Maybe Int64 entityId_ ConnContact = contactId entityId_ ConnMember = groupMemberId + entityId_ ConnRcvFile = rcvFileId + entityId_ ConnSndFile = sndFileId toMaybeConnection :: MaybeConnectionRow -> Maybe Connection -toMaybeConnection (Just connId, Just agentConnId, Just connLevel, viaContact, Just connStatus, Just connType, contactId, groupMemberId, Just createdAt) = - Just $ toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, createdAt) +toMaybeConnection (Just connId, Just agentConnId, Just connLevel, viaContact, Just connStatus, Just connType, contactId, groupMemberId, sndFileId, rcvFileId, Just createdAt) = + Just $ toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, sndFileId, rcvFileId, createdAt) toMaybeConnection _ = Nothing getMatchingContacts :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> m [Contact] @@ -515,15 +583,17 @@ getConnectionChatDirection :: StoreMonad m => SQLiteStore -> User -> ConnId -> m getConnectionChatDirection st User {userId, userContactId} agentConnId = liftIOEither . withTransaction st $ \db -> runExceptT $ do c@Connection {connType, entityId} <- getConnection_ db - case connType of - ConnMember -> - case entityId of - Nothing -> throwError $ SEInternal "group member without connection" - Just groupMemberId -> uncurry (ReceivedGroupMessage c) <$> getGroupAndMember_ db groupMemberId c - ConnContact -> - ReceivedDirectMessage c <$> case entityId of - Nothing -> pure Nothing - Just contactId -> Just <$> getContactRec_ db contactId c + case entityId of + Nothing -> + if connType == ConnContact + then pure $ ReceivedDirectMessage c Nothing + else throwError $ SEInternal $ "connection " <> bshow connType <> " without entity" + Just entId -> + case connType of + ConnMember -> uncurry (ReceivedGroupMessage c) <$> getGroupAndMember_ db entId c + ConnContact -> ReceivedDirectMessage c . Just <$> getContactRec_ db entId c + ConnSndFile -> SndFileConnection c <$> getConnSndFileTransfer_ db entId c + ConnRcvFile -> RcvFileConnection c <$> ExceptT (getRcvFileTransfer_ db userId entId) where getConnection_ :: DB.Connection -> ExceptT StoreError IO Connection getConnection_ db = ExceptT $ do @@ -532,7 +602,7 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = db [sql| SELECT connection_id, agent_conn_id, conn_level, via_contact, - conn_status, conn_type, contact_id, group_member_id, created_at + conn_status, conn_type, contact_id, group_member_id, snd_file_id, rcv_file_id, created_at FROM connections WHERE user_id = ? AND agent_conn_id = ? |] @@ -578,6 +648,22 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = let member = toGroupMember userContactId memberRow in Right (groupName, (member :: GroupMember) {activeConn = Just c}) toGroupAndMember _ _ = Left $ SEInternal "referenced group member not found" + getConnSndFileTransfer_ :: DB.Connection -> Int64 -> Connection -> ExceptT StoreError IO SndFileTransfer + getConnSndFileTransfer_ db fileId Connection {connId} = + ExceptT $ + sndFileTransfer_ fileId connId + <$> DB.query + db + [sql| + SELECT s.file_status, f.file_name, f.file_size, f.chunk_size, f.file_path + FROM snd_files s + JOIN files f USING (file_id) + WHERE f.user_id = ? AND f.file_id = ? AND s.connection_id = ? + |] + (userId, fileId, connId) + sndFileTransfer_ :: Int64 -> Int64 -> [(FileStatus, String, Integer, Integer, FilePath)] -> Either StoreError SndFileTransfer + sndFileTransfer_ fileId connId [(fileStatus, fileName, fileSize, chunkSize, filePath)] = Right SndFileTransfer {..} + sndFileTransfer_ fileId _ _ = Left $ SESndFileNotFound fileId updateConnectionStatus :: MonadUnliftIO m => SQLiteStore -> Connection -> ConnStatus -> m () updateConnectionStatus st Connection {connId} connStatus = @@ -655,7 +741,7 @@ getGroup_ db User {userId, userContactId} localDisplayName = do m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id LEFT JOIN connections c ON c.connection_id = ( @@ -1011,7 +1097,7 @@ getViaGroupMember st User {userId, userContactId} Contact {contactId} = m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at FROM group_members m JOIN contacts ct ON ct.contact_id = m.contact_id JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id @@ -1041,7 +1127,7 @@ getViaGroupContact st User {userId} GroupMember {groupMemberId} = SELECT ct.contact_id, ct.local_display_name, p.display_name, p.full_name, ct.via_group, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, - c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.created_at + c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at FROM contacts ct JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id JOIN connections c ON c.connection_id = ( @@ -1062,6 +1148,225 @@ getViaGroupContact st User {userId} GroupMember {groupMemberId} = in Just Contact {contactId, localDisplayName, profile, activeConn, viaGroup} toContact _ = Nothing +createSndFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> FilePath -> FileInvitation -> ConnId -> Integer -> m SndFileTransfer +createSndFileTransfer st userId contactId filePath FileInvitation {fileName, fileSize} agentConnId chunkSize = + liftIO . withTransaction st $ \db -> do + DB.execute db "INSERT INTO files (user_id, contact_id, file_name, file_path, file_size, chunk_size) VALUES (?, ?, ?, ?, ?, ?)" (userId, contactId, fileName, filePath, fileSize, chunkSize) + fileId <- insertedRowId db + Connection {connId} <- createSndFileConnection_ db userId fileId agentConnId + let fileStatus = FSNew + DB.execute db "INSERT INTO snd_files (file_id, file_status, connection_id) VALUES (?, ?, ?)" (fileId, fileStatus, connId) + pure SndFileTransfer {..} + +createSndFileConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> IO Connection +createSndFileConnection_ db userId fileId agentConnId = do + createdAt <- getCurrentTime + let connType = ConnSndFile + connStatus = ConnNew + DB.execute + db + [sql| + INSERT INTO connections + (user_id, snd_file_id, agent_conn_id, conn_status, conn_type, created_at) VALUES (?,?,?,?,?,?) + |] + (userId, fileId, agentConnId, connStatus, connType, createdAt) + connId <- insertedRowId db + pure Connection {connId, agentConnId, connType, entityId = Just fileId, viaContact = Nothing, connLevel = 0, connStatus, createdAt} + +updateSndFileStatus :: MonadUnliftIO m => SQLiteStore -> SndFileTransfer -> FileStatus -> m () +updateSndFileStatus st SndFileTransfer {fileId, connId} status = + liftIO . withTransaction st $ \db -> + DB.execute db "UPDATE snd_files SET file_status = ? WHERE file_id = ? AND connection_id = ?" (status, fileId, connId) + +createSndFileChunk :: MonadUnliftIO m => SQLiteStore -> SndFileTransfer -> m (Maybe Integer) +createSndFileChunk st SndFileTransfer {fileId, connId, fileSize, chunkSize} = + liftIO . withTransaction st $ \db -> do + chunkNo <- getLastChunkNo db + insertChunk db chunkNo + pure chunkNo + where + getLastChunkNo db = do + ns <- DB.query db "SELECT chunk_number FROM snd_file_chunks WHERE file_id = ? AND connection_id = ? AND chunk_sent = 1 ORDER BY chunk_number DESC LIMIT 1" (fileId, connId) + pure $ case map fromOnly ns of + [] -> Just 1 + n : _ -> if n * chunkSize >= fileSize then Nothing else Just (n + 1) + insertChunk db = \case + Just chunkNo -> DB.execute db "INSERT OR REPLACE INTO snd_file_chunks (file_id, connection_id, chunk_number) VALUES (?, ?, ?)" (fileId, connId, chunkNo) + Nothing -> pure () + +updateSndFileChunkMsg :: MonadUnliftIO m => SQLiteStore -> SndFileTransfer -> Integer -> AgentMsgId -> m () +updateSndFileChunkMsg st SndFileTransfer {fileId, connId} chunkNo msgId = + liftIO . withTransaction st $ \db -> + DB.execute + db + [sql| + UPDATE snd_file_chunks + SET chunk_agent_msg_id = ? + WHERE file_id = ? AND connection_id = ? AND chunk_number = ? + |] + (msgId, fileId, connId, chunkNo) + +updateSndFileChunkSent :: MonadUnliftIO m => SQLiteStore -> SndFileTransfer -> AgentMsgId -> m () +updateSndFileChunkSent st SndFileTransfer {fileId, connId} msgId = + liftIO . withTransaction st $ \db -> + DB.execute + db + [sql| + UPDATE snd_file_chunks + SET chunk_sent = 1 + WHERE file_id = ? AND connection_id = ? AND chunk_agent_msg_id = ? + |] + (fileId, connId, msgId) + +createRcvFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> FileInvitation -> Integer -> m RcvFileTransfer +createRcvFileTransfer st userId contactId f@FileInvitation {fileName, fileSize, fileQInfo} chunkSize = + liftIO . withTransaction st $ \db -> do + DB.execute db "INSERT INTO files (user_id, contact_id, file_name, file_size, chunk_size) VALUES (?, ?, ?, ?, ?)" (userId, contactId, fileName, fileSize, chunkSize) + fileId <- insertedRowId db + DB.execute db "INSERT INTO rcv_files (file_id, file_status, file_queue_info) VALUES (?, ?, ?)" (fileId, FSNew, fileQInfo) + pure RcvFileTransfer {fileId, fileInvitation = f, fileStatus = RFSNew, chunkSize} + +getRcvFileTransfer :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> m RcvFileTransfer +getRcvFileTransfer st userId fileId = + liftIOEither . withTransaction st $ \db -> + getRcvFileTransfer_ db userId fileId + +getRcvFileTransfer_ :: DB.Connection -> UserId -> Int64 -> IO (Either StoreError RcvFileTransfer) +getRcvFileTransfer_ db userId fileId = + rcvFileTransfer + <$> DB.query + db + [sql| + SELECT r.file_status, r.file_queue_info, f.file_name, + f.file_size, f.chunk_size, f.file_path, c.connection_id, c.agent_conn_id + FROM rcv_files r + JOIN files f USING (file_id) + LEFT JOIN connections c ON r.file_id = c.rcv_file_id + WHERE f.user_id = ? AND f.file_id = ? + |] + (userId, fileId) + where + rcvFileTransfer :: + [(FileStatus, SMPQueueInfo, String, Integer, Integer, Maybe FilePath, Maybe Int64, Maybe ConnId)] -> + Either StoreError RcvFileTransfer + rcvFileTransfer [(fileStatus', fileQInfo, fileName, fileSize, chunkSize, filePath_, connId_, agentConnId_)] = + let fileInv = FileInvitation {fileName, fileSize, fileQInfo} + fileInfo = (filePath_, connId_, agentConnId_) + in case fileStatus' of + FSNew -> Right RcvFileTransfer {fileId, fileInvitation = fileInv, fileStatus = RFSNew, chunkSize} + FSAccepted -> ft fileInv RFSAccepted fileInfo + FSConnected -> ft fileInv RFSConnected fileInfo + FSComplete -> ft fileInv RFSComplete fileInfo + FSCancelled -> ft fileInv RFSCancelled fileInfo + where + ft fileInvitation rfs = \case + (Just filePath, Just connId, Just agentConnId) -> + let fileStatus = rfs RcvFileInfo {filePath, connId, agentConnId} + in Right RcvFileTransfer {..} + _ -> Left $ SERcvFileInvalid fileId + rcvFileTransfer _ = Left $ SERcvFileNotFound fileId + +acceptRcvFileTransfer :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> ConnId -> FilePath -> m () +acceptRcvFileTransfer st userId fileId agentConnId filePath = + liftIO . withTransaction st $ \db -> do + DB.execute db "UPDATE files SET file_path = ? WHERE user_id = ? AND file_id = ?" (filePath, userId, fileId) + DB.execute db "UPDATE rcv_files SET file_status = ? WHERE file_id = ?" (FSAccepted, fileId) + + DB.execute db "INSERT INTO connections (agent_conn_id, conn_status, conn_type, rcv_file_id, user_id) VALUES (?, ?, ?, ?, ?)" (agentConnId, ConnJoined, ConnRcvFile, fileId, userId) + +updateRcvFileStatus :: MonadUnliftIO m => SQLiteStore -> RcvFileTransfer -> FileStatus -> m () +updateRcvFileStatus st RcvFileTransfer {fileId} status = + liftIO . withTransaction st $ \db -> + DB.execute db "UPDATE rcv_files SET file_status = ? WHERE file_id = ?" (status, fileId) + +createRcvFileChunk :: MonadUnliftIO m => SQLiteStore -> RcvFileTransfer -> Integer -> AgentMsgId -> m RcvChunkStatus +createRcvFileChunk st RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileSize}, chunkSize} chunkNo msgId = + liftIO . withTransaction st $ \db -> do + status <- getLastChunkNo db + unless (status == RcvChunkError) $ + DB.execute db "INSERT OR REPLACE INTO rcv_file_chunks (file_id, chunk_number, chunk_agent_msg_id) VALUES (?, ?, ?)" (fileId, chunkNo, msgId) + pure status + where + getLastChunkNo db = do + ns <- DB.query db "SELECT chunk_number FROM rcv_file_chunks WHERE file_id = ? ORDER BY chunk_number DESC LIMIT 1" (Only fileId) + pure $ case map fromOnly ns of + [] -> if chunkNo == 1 then RcvChunkOk else RcvChunkError + n : _ + | chunkNo == n -> RcvChunkDuplicate + | chunkNo == n + 1 -> + let prevSize = n * chunkSize + in if prevSize >= fileSize + then RcvChunkError + else + if prevSize + chunkSize >= fileSize + then RcvChunkFinal + else RcvChunkOk + | otherwise -> RcvChunkError + +updatedRcvFileChunkStored :: MonadUnliftIO m => SQLiteStore -> RcvFileTransfer -> Integer -> m () +updatedRcvFileChunkStored st RcvFileTransfer {fileId} chunkNo = + liftIO . withTransaction st $ \db -> + DB.execute + db + [sql| + UPDATE rcv_file_chunks + SET chunk_stored = 1 + WHERE file_id = ? AND chunk_number = ? + |] + (fileId, chunkNo) + +getFileTransfer :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> m FileTransfer +getFileTransfer st userId fileId = + liftIOEither . withTransaction st $ \db -> + getFileTransfer_ db userId fileId + +getFileTransferProgress :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> m (FileTransfer, [Integer]) +getFileTransferProgress st userId fileId = + liftIOEither . withTransaction st $ \db -> runExceptT $ do + ft <- ExceptT $ getFileTransfer_ db userId fileId + liftIO $ + (ft,) . map fromOnly <$> case ft of + FTSnd _ -> DB.query db "SELECT COUNT(*) FROM snd_file_chunks WHERE file_id = ? and chunk_sent = 1 GROUP BY connection_id" (Only fileId) + FTRcv _ -> DB.query db "SELECT COUNT(*) FROM rcv_file_chunks WHERE file_id = ? AND chunk_stored = 1" (Only fileId) + +getFileTransfer_ :: DB.Connection -> UserId -> Int64 -> IO (Either StoreError FileTransfer) +getFileTransfer_ db userId fileId = + fileTransfer + =<< DB.query + db + [sql| + SELECT s.file_id, r.file_id + FROM files f + LEFT JOIN snd_files s ON s.file_id = f.file_id + LEFT JOIN rcv_files r ON r.file_id = f.file_id + WHERE user_id = ? AND f.file_id = ? + |] + (userId, fileId) + where + fileTransfer :: [(Maybe Int64, Maybe Int64)] -> IO (Either StoreError FileTransfer) + fileTransfer ((Just _, Nothing) : _) = FTSnd <$$> getSndFileTransfers_ db userId fileId + fileTransfer [(Nothing, Just _)] = FTRcv <$$> getRcvFileTransfer_ db userId fileId + fileTransfer _ = pure . Left $ SEFileNotFound fileId + +getSndFileTransfers_ :: DB.Connection -> UserId -> Int64 -> IO (Either StoreError [SndFileTransfer]) +getSndFileTransfers_ db userId fileId = + sndFileTransfers + <$> DB.query + db + [sql| + SELECT s.file_status, f.file_name, f.file_size, f.chunk_size, f.file_path, s.connection_id, c.agent_conn_id + FROM snd_files s + JOIN files f USING (file_id) + JOIN connections c USING (connection_id) + WHERE f.user_id = ? AND f.file_id = ? + |] + (userId, fileId) + where + sndFileTransfers :: [(FileStatus, String, Integer, Integer, FilePath, Int64, ConnId)] -> Either StoreError [SndFileTransfer] + sndFileTransfers [] = Left $ SESndFileNotFound fileId + sndFileTransfers fts = Right $ map sndFileTransfer fts + sndFileTransfer (fileStatus, fileName, fileSize, chunkSize, filePath, connId, agentConnId) = SndFileTransfer {..} + -- | Saves unique local display name based on passed displayName, suffixed with _N if required. -- This function should be called inside transaction. withLocalDisplayName :: forall a. DB.Connection -> UserId -> Text -> (Text -> IO a) -> IO (Either StoreError a) @@ -1126,6 +1431,10 @@ data StoreError | SEDuplicateGroupMember | SEGroupAlreadyJoined | SEGroupInvitationNotFound + | SESndFileNotFound Int64 + | SERcvFileNotFound Int64 + | SEFileNotFound Int64 + | SERcvFileInvalid Int64 | SEConnectionNotFound ConnId | SEIntroNotFound | SEUniqueID diff --git a/src/Simplex/Chat/Styled.hs b/src/Simplex/Chat/Styled.hs index 9bbb88d277..f7a3a80acf 100644 --- a/src/Simplex/Chat/Styled.hs +++ b/src/Simplex/Chat/Styled.hs @@ -7,6 +7,7 @@ module Simplex.Chat.Styled styleMarkdown, styleMarkdownText, sLength, + sShow, ) where @@ -54,6 +55,9 @@ instance StyledFormat Text where styled f = styled f . T.unpack plain = Styled [] . T.unpack +sShow :: Show a => a -> StyledString +sShow = plain . show + sgr :: Format -> [SGR] sgr = \case Bold -> [SetConsoleIntensity BoldIntensity] diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 94708cdf97..b73deab15b 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -299,6 +299,76 @@ serializeMemberStatus = \case GSMemComplete -> "complete" GSMemCreator -> "creator" +data SndFileTransfer = SndFileTransfer + { fileId :: Int64, + fileName :: String, + filePath :: String, + fileSize :: Integer, + chunkSize :: Integer, + connId :: Int64, + agentConnId :: ConnId, + fileStatus :: FileStatus + } + deriving (Eq, Show) + +data FileInvitation = FileInvitation + { fileName :: String, + fileSize :: Integer, + fileQInfo :: SMPQueueInfo + } + deriving (Eq, Show) + +data RcvFileTransfer = RcvFileTransfer + { fileId :: Int64, + fileInvitation :: FileInvitation, + fileStatus :: RcvFileStatus, + chunkSize :: Integer + } + deriving (Eq, Show) + +data RcvFileStatus + = RFSNew + | RFSAccepted RcvFileInfo + | RFSConnected RcvFileInfo + | RFSComplete RcvFileInfo + | RFSCancelled RcvFileInfo + deriving (Eq, Show) + +data RcvFileInfo = RcvFileInfo + { filePath :: FilePath, + connId :: Int64, + agentConnId :: ConnId + } + deriving (Eq, Show) + +data FileTransfer = FTSnd [SndFileTransfer] | FTRcv RcvFileTransfer + +data FileStatus = FSNew | FSAccepted | FSConnected | FSComplete | FSCancelled deriving (Eq, Show) + +instance FromField FileStatus where fromField = fromTextField_ fileStatusT + +instance ToField FileStatus where toField = toField . serializeFileStatus + +fileStatusT :: Text -> Maybe FileStatus +fileStatusT = \case + "new" -> Just FSNew + "accepted" -> Just FSAccepted + "connected" -> Just FSConnected + "complete" -> Just FSComplete + "cancelled" -> Just FSCancelled + _ -> Nothing + +serializeFileStatus :: FileStatus -> Text +serializeFileStatus = \case + FSNew -> "new" + FSAccepted -> "accepted" + FSConnected -> "connected" + FSComplete -> "complete" + FSCancelled -> "cancelled" + +data RcvChunkStatus = RcvChunkOk | RcvChunkFinal | RcvChunkDuplicate | RcvChunkError + deriving (Eq, Show) + data Connection = Connection { connId :: Int64, agentConnId :: ConnId, @@ -306,7 +376,7 @@ data Connection = Connection viaContact :: Maybe Int64, connType :: ConnType, connStatus :: ConnStatus, - entityId :: Maybe Int64, -- contact or group member ID + entityId :: Maybe Int64, -- contact, group member or file ID createdAt :: UTCTime } deriving (Eq, Show) @@ -353,7 +423,7 @@ serializeConnStatus = \case ConnReady -> "ready" ConnDeleted -> "deleted" -data ConnType = ConnContact | ConnMember +data ConnType = ConnContact | ConnMember | ConnSndFile | ConnRcvFile deriving (Eq, Show) instance FromField ConnType where fromField = fromTextField_ connTypeT @@ -364,12 +434,16 @@ connTypeT :: Text -> Maybe ConnType connTypeT = \case "contact" -> Just ConnContact "member" -> Just ConnMember + "snd_file" -> Just ConnSndFile + "rcv_file" -> Just ConnRcvFile _ -> Nothing serializeConnType :: ConnType -> Text serializeConnType = \case ConnContact -> "contact" ConnMember -> "member" + ConnSndFile -> "snd_file" + ConnRcvFile -> "rcv_file" data NewConnection = NewConnection { agentConnId :: ByteString, diff --git a/src/Simplex/Chat/Util.hs b/src/Simplex/Chat/Util.hs index d2fe0c3d40..05ea20cf8d 100644 --- a/src/Simplex/Chat/Util.hs +++ b/src/Simplex/Chat/Util.hs @@ -8,3 +8,9 @@ safeDecodeUtf8 :: ByteString -> Text safeDecodeUtf8 = decodeUtf8With onError where onError _ _ = Just '?' + +ifM :: Monad m => m Bool -> m a -> m a -> m a +ifM ba t f = ba >>= \b -> if b then t else f + +unlessM :: Monad m => m Bool -> m () -> m () +unlessM b = ifM b $ pure () diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 608cac4811..5139a94d9d 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -24,6 +24,20 @@ module Simplex.Chat.View showReceivedGroupMessage, showSentMessage, showSentGroupMessage, + showSentFileInvitation, + showSndFileStart, + showSndFileComplete, + showSndFileCancelled, + showSndFileRcvCancelled, + showReceivedFileInvitation, + showRcvFileAccepted, + showRcvFileStart, + showRcvFileComplete, + showRcvFileCancelled, + showRcvFileSndCancelled, + showFileTransferStatus, + showSndFileSubError, + showRcvFileSubError, showGroupCreated, showGroupDeletedUser, showGroupDeleted, @@ -42,6 +56,7 @@ module Simplex.Chat.View showUserProfile, showUserProfileUpdated, showContactUpdated, + showMessageError, safeDecodeUtf8, ) where @@ -50,11 +65,13 @@ import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.ByteString.Char8 (ByteString) import Data.Composition ((.:), (.:.)) +import Data.Int (Int64) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (DiffTime, UTCTime) import Data.Time.Format (defaultTimeLocale, formatTime) import Data.Time.LocalTime (TimeZone, ZonedTime, getCurrentTimeZone, getZonedTime, localDay, localTimeOfDay, timeOfDayToTime, utcToLocalTime, zonedTimeToLocalTime) +import Numeric (showFFloat) import Simplex.Chat.Controller import Simplex.Chat.Markdown import Simplex.Chat.Store (StoreError (..)) @@ -124,6 +141,48 @@ showSentGroupMessage = showSentMessage_ . ttyToGroup showSentMessage_ :: ChatReader m => StyledString -> ByteString -> m () showSentMessage_ to msg = printToView =<< liftIO (sentMessage to msg) +showSentFileInvitation :: ChatReader m => ContactName -> SndFileTransfer -> m () +showSentFileInvitation = printToView .: sentFileInvitation + +showSndFileStart :: ChatReader m => Int64 -> m () +showSndFileStart = printToView . sndFileStart + +showSndFileComplete :: ChatReader m => Int64 -> m () +showSndFileComplete = printToView . sndFileComplete + +showSndFileCancelled :: ChatReader m => Int64 -> m () +showSndFileCancelled = printToView . sndFileCancelled + +showSndFileRcvCancelled :: ChatReader m => Int64 -> m () +showSndFileRcvCancelled = printToView . sndFileRcvCancelled + +showReceivedFileInvitation :: ChatReader m => ContactName -> RcvFileTransfer -> m () +showReceivedFileInvitation = printToView .: receivedFileInvitation + +showRcvFileAccepted :: ChatReader m => Int64 -> FilePath -> m () +showRcvFileAccepted = printToView .: rcvFileAccepted + +showRcvFileStart :: ChatReader m => Int64 -> m () +showRcvFileStart = printToView . rcvFileStart + +showRcvFileComplete :: ChatReader m => Int64 -> m () +showRcvFileComplete = printToView . rcvFileComplete + +showRcvFileCancelled :: ChatReader m => Int64 -> m () +showRcvFileCancelled = printToView . rcvFileCancelled + +showRcvFileSndCancelled :: ChatReader m => Int64 -> m () +showRcvFileSndCancelled = printToView . rcvFileSndCancelled + +showFileTransferStatus :: ChatReader m => (FileTransfer, [Integer]) -> m () +showFileTransferStatus = printToView . fileTransferStatus + +showSndFileSubError :: ChatReader m => SndFileTransfer -> ChatError -> m () +showSndFileSubError = printToView .: sndFileSubError + +showRcvFileSubError :: ChatReader m => RcvFileTransfer -> ChatError -> m () +showRcvFileSubError = printToView .: rcvFileSubError + showGroupCreated :: ChatReader m => Group -> m () showGroupCreated = printToView . groupCreated @@ -178,6 +237,9 @@ showUserProfileUpdated = printToView .: userProfileUpdated showContactUpdated :: ChatReader m => Contact -> Contact -> m () showContactUpdated = printToView .: contactUpdated +showMessageError :: ChatReader m => Text -> Text -> m () +showMessageError = printToView .: messageError + invitation :: SMPQueueInfo -> [StyledString] invitation qInfo = [ "pass this invitation to your contact (via another channel): ", @@ -202,19 +264,19 @@ contactConnected :: Contact -> [StyledString] contactConnected ct = [ttyFullContact ct <> ": contact is connected"] contactDisconnected :: ContactName -> [StyledString] -contactDisconnected c = [ttyContact c <> ": contact is disconnected (messages will be queued)"] +contactDisconnected c = [ttyContact c <> ": disconnected from server (messages will be queued)"] contactAnotherClient :: ContactName -> [StyledString] contactAnotherClient c = [ttyContact c <> ": contact is connected to another client"] contactSubscribed :: ContactName -> [StyledString] -contactSubscribed c = [ttyContact c <> ": contact is active"] +contactSubscribed c = [ttyContact c <> ": connected to server"] contactSubError :: ContactName -> ChatError -> [StyledString] -contactSubError c e = [ttyContact c <> ": contact error " <> plain (show e)] +contactSubError c e = [ttyContact c <> ": contact error " <> sShow e] groupSubscribed :: GroupName -> [StyledString] -groupSubscribed g = [ttyGroup g <> ": group is active"] +groupSubscribed g = [ttyGroup g <> ": connected to server(s)"] groupEmpty :: GroupName -> [StyledString] groupEmpty g = [ttyGroup g <> ": group is empty"] @@ -223,7 +285,7 @@ groupRemoved :: GroupName -> [StyledString] groupRemoved g = [ttyGroup g <> ": you are no longer a member or group deleted"] memberSubError :: GroupName -> ContactName -> ChatError -> [StyledString] -memberSubError g c e = [ttyGroup g <> " member " <> ttyContact c <> " error: " <> plain (show e)] +memberSubError g c e = [ttyGroup g <> " member " <> ttyContact c <> " error: " <> sShow e] groupCreated :: Group -> [StyledString] groupCreated g@Group {localDisplayName} = @@ -317,7 +379,7 @@ contactsMerged _to@Contact {localDisplayName = c1} _from@Contact {localDisplayNa userProfile :: Profile -> [StyledString] userProfile Profile {displayName, fullName} = [ "user profile: " <> ttyFullName displayName fullName, - "use " <> highlight' "/p [ ]" <> " to change it", + "use " <> highlight' "/p []" <> " to change it", "(the updated profile will be sent to all your contacts)" ] @@ -344,6 +406,9 @@ contactUpdated where fullNameUpdate = if T.null fullName' || fullName' == n' then " removed full name" else " updated full name: " <> plain fullName' +messageError :: Text -> Text -> [StyledString] +messageError prefix err = [plain prefix <> ": " <> plain err] + receivedMessage :: StyledString -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] receivedMessage from utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime @@ -382,6 +447,90 @@ prependFirst s (s' : ss) = (s <> s') : ss msgPlain :: Text -> [StyledString] msgPlain = map styleMarkdownText . T.lines +sentFileInvitation :: ContactName -> SndFileTransfer -> [StyledString] +sentFileInvitation cName SndFileTransfer {fileId, fileName} = + [ "offered to send the file " <> plain fileName <> " to " <> ttyContact cName, + "use " <> highlight ("/fc " <> show fileId) <> " to cancel sending" + ] + +sndFileStart :: Int64 -> [StyledString] +sndFileStart fileId = ["started sending the file " <> sShow fileId] + +sndFileComplete :: Int64 -> [StyledString] +sndFileComplete fileId = ["completed sending the file " <> sShow fileId] + +sndFileCancelled :: Int64 -> [StyledString] +sndFileCancelled fileId = ["cancelled sending the file " <> sShow fileId] + +sndFileRcvCancelled :: Int64 -> [StyledString] +sndFileRcvCancelled fileId = ["recipient cancelled receiving the file " <> sShow fileId] + +receivedFileInvitation :: ContactName -> RcvFileTransfer -> [StyledString] +receivedFileInvitation c RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName, fileSize}} = + [ ttyContact c <> " wants to send you the file " <> plain fileName <> " (" <> humanReadableSize fileSize <> " / " <> sShow fileSize <> " bytes)", + "use " <> highlight ("/fr " <> show fileId <> " [/ | ]") <> " to receive it" + ] + +humanReadableSize :: Integer -> StyledString +humanReadableSize size + | size < kB = sShow size <> " bytes" + | size < mB = hrSize kB "KiB" + | size < gB = hrSize mB "MiB" + | otherwise = hrSize gB "GiB" + where + hrSize sB name = plain $ unwords [showFFloat (Just 1) (fromIntegral size / (fromIntegral sB :: Double)) "", name] + kB = 1024 + mB = kB * 1024 + gB = mB * 1024 + +rcvFileAccepted :: Int64 -> FilePath -> [StyledString] +rcvFileAccepted fileId filePath = ["saving file " <> sShow fileId <> " to " <> plain filePath] + +rcvFileStart :: Int64 -> [StyledString] +rcvFileStart fileId = ["started receiving the file " <> sShow fileId] + +rcvFileComplete :: Int64 -> [StyledString] +rcvFileComplete fileId = ["completed receiving the file " <> sShow fileId] + +rcvFileCancelled :: Int64 -> [StyledString] +rcvFileCancelled fileId = ["cancelled receiving the file " <> sShow fileId] + +rcvFileSndCancelled :: Int64 -> [StyledString] +rcvFileSndCancelled fileId = ["sender cancelled sending the file " <> sShow fileId] + +fileTransferStatus :: (FileTransfer, [Integer]) -> [StyledString] +fileTransferStatus (FTSnd [SndFileTransfer {fileStatus, fileSize, chunkSize}], chunksNum) = + ["sent file transfer " <> sndStatus] + where + sndStatus = case fileStatus of + FSNew -> "is not accepted yet" + FSAccepted -> "just started" + FSConnected -> "progress: " <> fileProgress chunksNum chunkSize fileSize + FSComplete -> "is complete" + FSCancelled -> "is cancelled" +fileTransferStatus (FTSnd _fts, _chunks) = [] -- TODO group transfer +fileTransferStatus (FTRcv RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileSize}, fileStatus, chunkSize}, chunksNum) = + ["received file transfer " <> rcvStatus] + where + rcvStatus = case fileStatus of + RFSNew -> "is not accepted yet, use " <> highlight ("/fr " <> show fileId) <> " to receive file" + RFSAccepted _ -> "just started" + RFSConnected _ -> "progress: " <> fileProgress chunksNum chunkSize fileSize + RFSComplete RcvFileInfo {filePath} -> "is complete, path: " <> plain filePath + RFSCancelled RcvFileInfo {filePath} -> "is cancelled, received part path: " <> plain filePath + +fileProgress :: [Integer] -> Integer -> Integer -> StyledString +fileProgress chunksNum chunkSize fileSize = + sShow (sum chunksNum * chunkSize * 100 `div` fileSize) <> "% of " <> humanReadableSize fileSize + +sndFileSubError :: SndFileTransfer -> ChatError -> [StyledString] +sndFileSubError SndFileTransfer {fileId, fileName} e = + ["sent file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e] + +rcvFileSubError :: RcvFileTransfer -> ChatError -> [StyledString] +rcvFileSubError RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} e = + ["received file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e] + chatError :: ChatError -> [StyledString] chatError = \case ChatError err -> case err of @@ -394,16 +543,29 @@ chatError = \case CEGroupMemberUserRemoved -> ["you are no longer the member of the group"] CEGroupMemberNotFound c -> ["contact " <> ttyContact c <> " is not a group member"] CEGroupInternal s -> ["chat group bug: " <> plain s] - -- e -> ["chat error: " <> plain (show e)] + CEFileNotFound f -> ["file not found: " <> plain f] + CEFileAlreadyReceiving f -> ["file is already accepted: " <> plain f] + CEFileAlreadyExists f -> ["file already exists: " <> plain f] + CEFileRead f e -> ["cannot read file " <> plain f, sShow e] + CEFileWrite f e -> ["cannot write file " <> plain f, sShow e] + CEFileSend fileId e -> ["error sending file " <> sShow fileId <> ": " <> sShow e] + CEFileRcvChunk e -> ["error receiving file: " <> plain e] + CEFileInternal e -> ["file error: " <> plain e] + -- e -> ["chat error: " <> sShow e] ChatErrorStore err -> case err of SEDuplicateName -> ["this display name is already used by user, contact or group"] SEContactNotFound c -> ["no contact " <> ttyContact c] SEContactNotReady c -> ["contact " <> ttyContact c <> " is not active yet"] SEGroupNotFound g -> ["no group " <> ttyGroup g] SEGroupAlreadyJoined -> ["you already joined this group"] - e -> ["chat db error: " <> plain (show e)] - ChatErrorAgent e -> ["smp agent error: " <> plain (show e)] - ChatErrorMessage e -> ["chat message error: " <> plain (show e)] + SEFileNotFound fileId -> fileNotFound fileId + SESndFileNotFound fileId -> fileNotFound fileId + SERcvFileNotFound fileId -> fileNotFound fileId + e -> ["chat db error: " <> sShow e] + ChatErrorAgent e -> ["smp agent error: " <> sShow e] + ChatErrorMessage e -> ["chat message error: " <> sShow e] + where + fileNotFound fileId = ["file " <> sShow fileId <> " not found"] printToView :: (MonadUnliftIO m, MonadReader ChatController m) => [StyledString] -> m () printToView s = asks chatTerminal >>= liftIO . (`printToTerminal` s) diff --git a/src/Simplex/Chat/protocol.md b/src/Simplex/Chat/protocol.md index bd761ec46c..111319c593 100644 --- a/src/Simplex/Chat/protocol.md +++ b/src/Simplex/Chat/protocol.md @@ -65,6 +65,7 @@ refMsgHash = 16*16(OCTET) ; SHA256 of agent message body ' x.grp.mem.inv 23456,234 x.text:NNN ' ' x.grp.mem.req 23456,123 x.json:NNN {...} ' ' x.grp.mem.direct.inv 23456,234 x.text:NNN ' +' x.file name,size x.text:NNN ' ``` ### Group protocol diff --git a/stack.yaml b/stack.yaml index 2d17c5d3ae..9a58b8394f 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,8 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: dd5137c336d5525c38b068d7212964b4ab196a33 - # this commit is in PR #164 + commit: 2ac903a2dd37c11a8612b19cd132cf43fe771fe4 # # extra-deps: [] diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 5b266e8c77..eff8ff6c10 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -15,7 +15,7 @@ import Control.Monad.Except import Data.List (dropWhileEnd) import Network.Socket import Simplex.Chat -import Simplex.Chat.Controller (ChatController (..)) +import Simplex.Chat.Controller (ChatConfig (..), ChatController (..)) import Simplex.Chat.Options import Simplex.Chat.Store import Simplex.Chat.Types (Profile) @@ -145,9 +145,11 @@ serverCfg = ServerConfig { transports = [(serverPort, transport @TCP)], tbqSize = 1, + msgQueueQuota = 4, queueIdBytes = 12, msgIdBytes = 6, storeLog = Nothing, + blockSize = 4096, serverPrivateKey = -- full RSA private key (only for tests) "MIIFIwIBAAKCAQEArZyrri/NAwt5buvYjwu+B/MQeJUszDBpRgVqNddlI9kNwDXu\ diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index ad5c4ff08e..0afdf62efd 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -7,10 +7,13 @@ module ChatTests where import ChatClient import Control.Concurrent.Async (concurrently_) import Control.Concurrent.STM +import qualified Data.ByteString as B import Data.Char (isDigit) import qualified Data.Text as T import Simplex.Chat.Controller import Simplex.Chat.Types (Profile (..), User (..)) +import Simplex.Chat.Util (unlessM) +import System.Directory (doesFileExist) import System.Timeout (timeout) import Test.Hspec @@ -37,6 +40,10 @@ chatTests = do it "remove contact from group and add again" testGroupRemoveAdd describe "user profiles" $ it "update user profiles and notify contacts" testUpdateProfile + describe "sending and receiving files" $ do + it "send and receive file" testFileTransfer + it "sender cancelled file transfer" testFileSndCancel + it "recipient cancelled file transfer" testFileRcvCancel testAddContact :: IO () testAddContact = @@ -359,7 +366,7 @@ testUpdateProfile = createGroup3 "team" alice bob cath alice ##> "/p" alice <## "user profile: alice (Alice)" - alice <## "use /p [ ] to change it" + alice <## "use /p [] to change it" alice <## "(the updated profile will be sent to all your contacts)" alice ##> "/p alice" concurrentlyN_ @@ -394,6 +401,87 @@ testUpdateProfile = bob <## "use @cat to send messages" ] +testFileTransfer :: IO () +testFileTransfer = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + startFileTransfer alice bob + concurrentlyN_ + [ do + bob #> "@alice receiving here..." + bob <## "completed receiving the file 1", + do + alice <# "bob> receiving here..." + alice <## "completed sending the file 1" + ] + src <- B.readFile "./tests/fixtures/test.jpg" + dest <- B.readFile "./tests/tmp/test.jpg" + dest `shouldBe` src + +testFileSndCancel :: IO () +testFileSndCancel = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + startFileTransfer alice bob + alice ##> "/fc 1" + concurrentlyN_ + [ do + alice <## "cancelled sending the file 1" + alice ##> "/fs 1" + alice <## "sent file transfer is cancelled", + do + bob <## "sender cancelled sending the file 1" + bob ##> "/fs 1" + bob <## "received file transfer is cancelled, received part path: ./tests/tmp/test.jpg" + ] + checkPartialTransfer + +testFileRcvCancel :: IO () +testFileRcvCancel = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + startFileTransfer alice bob + bob ##> "/fs 1" + getTermLine bob >>= (`shouldStartWith` "received file transfer progress:") + waitFileExists "./tests/tmp/test.jpg" + bob ##> "/fc 1" + concurrentlyN_ + [ do + bob <## "cancelled receiving the file 1" + bob ##> "/fs 1" + bob <## "received file transfer is cancelled, received part path: ./tests/tmp/test.jpg", + do + alice <## "recipient cancelled receiving the file 1" + alice ##> "/fs 1" + alice <## "sent file transfer is cancelled" + ] + checkPartialTransfer + where + waitFileExists f = unlessM (doesFileExist f) $ waitFileExists f + +startFileTransfer :: TestCC -> TestCC -> IO () +startFileTransfer alice bob = do + alice ##> "/f bob ./tests/fixtures/test.jpg" + alice <## "offered to send the file test.jpg to bob" + alice <## "use /fc 1 to cancel sending" + bob <## "alice wants to send you the file test.jpg (136.5 KiB / 139737 bytes)" + bob <## "use /fr 1 [/ | ] to receive it" + bob ##> "/fr 1 ./tests/tmp" + bob <## "saving file 1 to ./tests/tmp/test.jpg" + concurrently_ + (bob <## "started receiving the file 1") + (alice <## "started sending the file 1") + +checkPartialTransfer :: IO () +checkPartialTransfer = do + src <- B.readFile "./tests/fixtures/test.jpg" + dest <- B.readFile "./tests/tmp/test.jpg" + B.unpack src `shouldStartWith` B.unpack dest + B.length src > B.length dest `shouldBe` True + connectUsers :: TestCC -> TestCC -> IO () connectUsers cc1 cc2 = do name1 <- showName cc1 diff --git a/tests/fixtures/test.jpg b/tests/fixtures/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4a110305929671604423569157d7186833a66a21 GIT binary patch literal 139737 zcmbTd1z1$w+b%wcNOyyD4k;}q(#%jYl(Zlr?T~^ZNVhNy-Q6hNAWEkU-BQxs5}v_V zeShct|0lk0dtI{j-fPWz_F7Ne_p_hD>zl$!fY(tPU z2}4|BVl#*LF_~pF&0Jws)t^TxSY&mc`}$?&)Go5hx%tOs*L)e1*EJ7_A75HI$Zc1! z=;#zdPI@0H4hq`;B-5`+(eGg(?>-?!-o10@?mb*|EHu<#-EQv^q7gCh5ld@8qKEI1 zkRoLf&@_cQ!7|YWW!`0#?<3`r)iTR&TU??LdhUzxi#cFsF|YVs>HKpVfP;#HTo6=3 zfE2*!RI(b=#3(k@S@@lazMlQU58vfW+G+m}WqrP>1IZske{9pES)rn9$A68d3W^M{ z03EByG}XcnC^WVWjAo^#Go6<%K3be+i_gd4ub?JghuJm;U25^Xq3x)i`*K}G{z&W@ zKs0@_rm@buT4t^7&!FWzz<+dh%zqX*tOTv0%!kdn^x)=a)3Z^65nLACjJ%kU(fDz9 zTi*b$PqJ>xz34?nuSj&i<}y6tfv6y|*6S3VVxsz(mhsJ|cNcHQu+wQ2^~V+#8~Ywa zp$lA+O}OKq78ssO{*lV%6Tn%j+}{1=Pk>Fpkl!D%x>NxF_udi5R|L~QJmD{bhX!Sd z3{@`Xcb9?s@}4<_-dAVhna~^Od@|?n!+`3+6EL9+Fwl#(gdoqw1-z~XH+t_6gnx^;W ze$ow=?K&i?jvUBpt>L1@R1Xpl|G>68Lh<^~PHc+x5I(wiOWgvMraykK^WFQ;zM=o! zmB<7Dp4BOL*)HAAlluL?BR6~i|JU9JE$P0T@(k?{2Bc^vaH9N=&F{|VT9fhT5RzVx z&@j^3-Mf7jW9(7+7+V!|I(<$Q-TYkTL}D>84kCL!xXdt1fQi?}RdGsQuX-f~h0Q&1s{F&Iu--RH zRrY=lk`R`4Qt~Li4qbFKOpaC6>tCxd)+C&C!#e3L(-uN1hPcsPof~B5*`Dt~%~B8% zlgkjpuNldVtxAC_*Df7tNBeI!8=hV-f4kk?_#t(z)NvDHwj|o)Bk-KbZjQ5A7zno; zf%mJM+QGiW<_(QeML9T+P8}_m(+ihC0<=k zYi{Ib8!oLt~N-PASL8OBO3Qr$E3TOYRpkMKuLRK9!a z_tM+eZ|J@9-5S5@)y{7>U&Y=r8_1d|Z$Sa8pWA3Mcf3#Qq^fY5gYC&}e*$VAUuU?= zU~fTjb5P-ZsN>;30o#YmRX+jK_l&>9#Z-56StC~f?;|gIaG?2PQNfQw*5ZbSZt0hg z&nC?fr&V=3;-}FQn7pO!!WlkJl+W}>=&q9t-BK@=`aiN*U+NorCSNLT-;D^Nzwh>x zsOq|eM^f!N$NVXNHTqA0B{^;0&8RpFkhk=U8Ig&B4>a+a@aXBsx@(cs%;;wvV|3SX zhHsNEpX}Tvn1-QrSokDsPgg7-4^0QPd^-J5E#I|~UX3?t=KumMF@EAM+0lUhu-cL3 zea2}popm7-j~sZ+9C(V*1?0VSHsdI#?|je5>avN^qa;eGWxWZv7!y8-Ug1aZF>!KB z`~(dA1T63kV$2d%gU&Zf-6{sAN%JkH;CCYnPHoo zd~xFqEO$$WkLkD!zIM7%31>rEq|m0ru(sZa=uY3oBM&mmMmz6QzBl=b*Hz|viKVU9 z%Bx~hJK5_Ygt);Vdwiu6$5e~NxS3OUi&pmAY02w;x8#YTHM;f8T8D{6_tGELca zgqcB-66Im9PPB3TUHL`6;WP9Z0Bjh8=A=x6JO@Lzgakd~%X>12K>QR-=J-L>cg7Ga zkLOjn492JrF4OZJzf+F8dfXTBusQJe>o?if#r0g7MIwR3Axo2il>?ffXM2Xf^>Q`- z2pQpPkC4a!8Ow<+W*%6~@HyUU6;egIJSccvz45YAF2ue0{2r+-^qUGBRo*kI#5;bF zf^`0h|CRf}gG4VNo5OfG_x=+nknh9D0!J1#&=)ZN0EH~xdkL+9+a|bXJ0ifMl8uzp z!>+>UfNfdKpk~!+n_@AmhcjKY`K+zJp+87+c+h%5Nm@qa*o-iDN^@^J)4u?*U`u{y z?~H6O66UE#4Be{7C!ku>2eMWvvAI`X{; zsg^2}amHhxm9>2h6B&5Gr+Sb8I4owWlNOzBEvtLq)O$1P3&--RvvX>ak0 zxfs;M@kudzv)2uO;K37dc~j;{%K_CNrCOCHYR7u>~7k=XCI18fN2Hgbo$^QE=0 zZG9|v8HCDtO)c1Y-?~uRU0_iXPu3zS#jeA%F zW(1f_om;MYk_V0n94#YMH#z4S)P_hX2gp5y9kLwFkXyi1orWWoS; zLC<3Inu_y7Kwx`>xquRGH}w-CdIu%k(v657DuWSf7^>Nwz++z*Gz^radn*y_)KRET z_eLX>s3EAH3Zu9n>L;jz5d#~zFBqzsQJsEYvAOsCI1r9IumPBreg0ezH^OH&x*AoF zVA6*gCY)iTH0?tjZ11RK!%*E!Pu&Tk2#29a;|5c8+dalzr^c{-Jo#88YBIVcu>E!% z6GUOuXFfU+he&`Pi%3AJjY-hw$$Y}3P;H(tU^cp(NnfEIdA}MJ*$*)P?qgsTu0N{B z@1HB6h6(1_KSql3&*Lb4vVZ(!K1$6evzeYU$3Y1ZT~9AUZ?A-yis(UUX0o~GpD;fk zh4WQJBu6mE!BOenF>V1>lyJs91a&fkO6m5>5;cJkbLS{>>VKxZ6+sU-7{IG^^+V%x5_SYOwlt{-(OVr<|w1q9|eTD(E~z&^`V=-RURW0I61?Ui$WbD8`Xt)_+~MscrR} zB7YGhUH!3@m-83MQ1#F6-~RvaLj8w1P}_PoUWs^EA}y}j=!Uwf^3MVw2cZ1j3ktto z{FBe=3woB%=nl^6Z~Wz|S~e?7>~ z3}{1WGgW_VVW4{~2F`J-p=)Pa$Nz>xCr+xgW5*k;`1MY?R^<5SbAPWpj{$@?uno!5 zfCJoXR75#E4S6~6`pFFdNB()C{7-;Y`|atGS&2N&#@8gbM9j`vXjLh8cc_w> zkzd{NmHu0wO#p5Z3bvEtdDS(__jWx`=Sow-ZT3FwzY?>6wI-rOv`rOb_GDksPmfKXQwV zck$aUs<*SZk6(^08yb}=q0ee(L0K^bzFw?M z72a=f!7`kjjheM!SKq>Z+GM4x=*(VbZR?73Ky)uQTbN3mF-pP0%a-*kQAyMy<|zu! zkr%;2Ds7|af-%rNLZ9!;u(ej*_gCn-z=amhuA4ji>Aviny#b^*M90Ca%6ECQH3j$= zIGZv!858yvNKf{I8VNR2lQHwP9E6ukSzhR96|!bAv5@sx%wTQm+;Nt2jyUpE5ay3% zNb<*V>kpH(<&ot<`tHq2Bjq6$9w1VMU@!@Pq;eCQV}@oS>4)wzr7qUm0y30$zpsbO%bwy3b*3-qujbqn0?)QL9cr?S z=5o9YVJ`8Pt7K%v?EN~(dz9bG_1R|p*4ZyS%!ooQ)CbNK9^QDo$3tidv$S4VQhh?1 zEajGUle(~!+(^r8WPW2B=mT)))X>z!`_`N?ah9>nqjKh@6}=3ZXXt;Nx~k5&7VM};WeUh#h+t&HTVm9UCVb^< zoWhaZqTWg2a2tx|kBTq>QZCEdJN_TSrE)xV28(AfO(;x#0L*CiHgo0i2YHekI=sbN%}A^@|1Z`ut!)mzIm_|e|K0GB zai+4}eA|dkPCkV>%6RQ&K(hN>)k}TC)NJXJLCoKztnV3q`Bnk=$95fXb}H%oL)mAv zj#EijWLh{LO*~n^g!E+cF=n&BR5iu?57~9ok6yup2k75^3-}i>^8H1TKEmTCNGJGf z0e;7)f1d{RZ!F^b7Y5v$9{<1;cs7W1=M}Y6M86))5cSiY#l?BlueW1AJqG;U5*%jJ z%YR|UA8@Ms8%l=np{%34JpD#{JIv$N={Kam{FT9!s0t*Rr~CK^Ng|^yGJrH8XDq9G zJC9WJJ+X`^0DuAmfU-z|9EI|d=N6~fB56SNFP!pdg38;=HZBUcx)d2|Z}k8frjeOu zq-xww_0NHNieHV4v`Eu>5VTE)1O>N)@Adw*BP1C52T1+uj;B?0Kao{##wy6Dez&l5evO;XUKdeFKD)TA3M$0$2MHBPElJJzFKTN46 z*auR@MOf!f-34{`{-kdA9Y0GBLc`enf*+#ZU~oMwSaPOst#7_YAS0w2=6HLx1&L9&0)B*!he`1rC2w)W>!Lam`e_T{6gdqJ@g0P zts?{NU22j*3^kzz(*!9YV1gw~Ambz>BW;kv;>(E#@iqyGWl5}VyAh52t5%CU!`el8 z?(IDH#Y_u-2-W}vyFsZ>UPW2PmLUACf}E9@^Et*#CHXo1+^|2cO8zMGnJDb`6S4}P zPSQ)XN}Nt*9Z$)1ijp(W5+I!?iv4mQ9 z@rQtA;m4e0+o7^p`h9HQ=mv$!svM~7MpTB=s*{Q*R*IzJGv9_w63Z!=C`ycMc?4k< zj-_T!{V;&6C<-xjH0`pAeVx|%4y4Qlf}R_B#=^f0qP(15b)9-yL@}1*I4e|B z#h?(R)Fban5=%BWB~Qc3G#)yxs`?0|K?BY)VrhErsz`ZvB#e2P^^Cl_N;g0+jqK&7 zcCT1eBEN~Htx}SofR2Qnol~#XXsI$&t6jj>G#fUBb#R{0(j2L$nN3Q*=-h*P9jXY` zSq)&y)z_2yy#Wq1n!+dF2phJ_#4IZNU$@G$U&FQvwOp> z<>ElEHkkA%Ghb*#Q|5HPZ)fM~s?4sW9nM{4!D4-0D%X@Y9=FP(?;YSld+y#(hB(+# zvlu*h#@x7gKbLOcWfwwUxP$D2yx?k^jm?m_0oT#0L91DCR!KHjMX!-1(gg42AhD=# z^jld*{^He1}8|?|oc~1j2_;I*2X9APHqcGm# zt_5#O$C9~39XhA>cKGDkvaqFmm2|-PXiPmwFws7;Et;?vYc-RDX@-H~!&3@~Bfm8B z(2HPnK0_4iX%&*Dxy@$>!7YtuEyYh(ItAFMC6XUgKL!AhHu^jKA#IfQw}Jm}ZS4nCC_X;_0pOLsoc=}dzh=Fq<9d2XmVo5U zpqKan?4Vk}K2lUv>VEV8ovUp4?O6!}V*fC$-yQ(@hwi0UncywJy%qC!yt$R~A6kW^ zeMtX&o8tIA;=#;(yZ38^^!e|`V+PUxg-#(86nQQA|HlNy;w2rrGg0j>Vz-VnWqcf5 z%u|)m2-$?_j|?$=iaUI?QpQpVV~{|P9-`~P0%_z4ik zMY)|;_u?aN2A)3Zt*jr8o_(d933Md&P6kZ>t2eAZgUBZ;zCy}Zbt|835I4p@^1*K9 z)5-=BlAU9)60LVUToKGqdrsPA*lB7RFt2RXBSfaD7ALLo%qg<4$m=Y(zlAfQK;i9o zMwoe7E00n1*XjnGv1X^D~1S}c(%xGnsN>%8D)&&ra)CK}?(#2cu5a=7H8zdR0j>epk_?+%}VqDul ztE-b+7eFht1`ne)H_)CXKDL9adZs@Si4Jk&^0*t*z6Fg*T}4ppZCa_tDZ> zD)<3}2hO;*lVn@h8cNTW7;2e!j=to?8S%^J7FjzC5WoHu8$K1&{%q3$QlxUA9tBPF# zH4R5qT(#dx%j$khT_ws6y9`ZFa{(K^9FLuC3}Z-BkbC*~! z(WtZ0pgYBQ9wRJr%mOWkO0|Q4R$^{W@MT!2Opli-Copj*VS}mz&nfLqFU}VxgFpq1 zbbfxJ#OT;=e*WF4a1lRuO(E?^z6HnNFoM1JOyZ3sG2>RR>&jKh&M7g4Dwm#Sp0kr%w2EdN7ML>OH@B%l!%{yU80H) z&$e&e)1V69?X`-#{CuBHeuy!#%qfH4k42&?Ic0L;2)EL=mDblywN= zF62;vG;*jm`o)&D^_|Eu6h7D#oyMnt&ERL97`bkqHe~>3cv+tlZjL-JlXZl*gY*%CN%sr&Vvxy)zbi)%T-+KL2+ zs=1ltx!e!q7oddM^doYgmdqMP)&n&4%ERq$Z{Si@9T0~_p@}|;Wt0`N30--V_+Z|; zhA4;x&Vc1C&fm^d_XEZ-d`=F5ghjJL<_QzRy}3 zPgZT5uooS@yvza;%x?5rtzi;WAxzPJxF@Ym1GN@*o=AdK=T>q?unQ=t_)xrT9_Ez+ z(1!@J=&jpnl3IX3|u#rUH9f}Q$JM`wdz4cMXJThi|`LlOk>G9<+B zG=!tFU9p7C*MjxZ_zK`kGn9~*U=>zr7c}`*Fr)0CM1O3l5;r4Ld(Sr%&{I7`QX<%( z81|Vt>0s7%tYNk|yt`b}yLuz4Z41Ofa^ZwKq0_6&5U!~o!JI2-85VqK&gTqy@IWKg zVktn&J#$Z(U<*PL*gtra`!M-AaFSBC{Xq0iF0!Bk`!w6?tWQrk z9S?ONOFU=UK8~}``0a9N2V*5W=*3_f~63fQhx5yAmcLEUh5p`*m zLPn$aKYac>7yqR_iioRgWU!UmL@&lxNSHn6Fa#-qjjX|tWeIpr2&mUaa8|zMj+EX% zB#JQSL%05G8N)EjM_NH;k%t7fZmYo9obZT_L`upyNS&UWBHDm}(~5Df^{n}wyi7fy zGs)A4M6%w1LW_N^5qBQx=we%51XtaIB=d3uY34?Ml7#TjR2F0sPW3h2O{SlM{4?0U zJd%#O$NG#3_XQ;Y@Qb$LkQ6uifWzA;D*>OzC514%2Wii3F?NbF;^&3`uHbS)p)iks$M4?4tl`3kQ723OG`?AVaiHPsS z5lZF}+});6%ZSvrzGflx9XSsJ`^lfNMn8@oE=fMjjRvy61$Pfr=uz?#0vQ=KqGzr0 zUsiVq>SB*BACOFc%c>9c5HMUU4s~^&%=`E*aW`5U-r#E#i=~+#zPVH%s>Vwa6KSnM z%Ml(1jBVSxJ4u~0^#wZVzlAVjlvzHv{LdAO)ZcAA3bwd5zyeI#Jx1p_mb$ZgyuDm@)K4Fhy%muj znGvk9imWcRF^J?k9<62{JnHq-C4eXOfqw!3r#}H0NPc=f{ZMQxG4EleruL7b2))$s zPeL(1BG`6KOvFNkDan8bT~qfws#v%_{RWYM13BtV7fWW+LHhR%_c6LMIKni2^HQ3j zgUi!>uZlUx!G`j*;x_z~kG^fj(8t-`OXJcd0P}1BR?tYN@YQY2rJn7#zWJ3Jk*~Gg zHy&xXeebEA4<$?RotmK&qfsD=`;s?pWH(x2uK>@1rxfvX`QIV^L)y_BI*6>B4&ao5 z3i)`4;TO^KOW|ew($PpDXY>aV-us45^QZj>WIm(zx$p8Da6bDAMB+7)=m8%G0Q67W z?{U5ZWfMeE7^Utnl$QAqkm>Rp#34au61;9G7sv>4N{SF6boB9A)EQ<>2!Uul?H!_# z1xYeN9XQF3Cb=s~jWM^d#8R?igm_DjnGsnUYqloFIaSHKAQq7ur51bb=(h&%SvKwB zle?hi;NZiUw_@SpVMNPKa4QdtCKY0a_*B>!zs-FQ)~PwlD=or&f`O_eF+af-z_35( z(>XCwU^V^xo1+@O_XRBGo%;P1W;u}u_uB5Sik;|)SEK6BvJTYtD0)N_CoV@24K6VX zF$WZb+e{TL_*{6N4`hs15f7WZMA-DYQ)VmK=0A(Y;Q$YVliy@@K-s9|+hazQ&6T)h zq#k2s38;Ypzd&=fTf|5cXcz~@wGo8D zr;L+1kuM?4X+%Q z2@K&3P`fY0sF*vTA02a;@rm6*pro*i^l?D^>!j$5cJ5SIg3g8$^P6t~fkf%C%Q7?K2NQzxW;#VI!wskadVF z-?veRR|gaCX--`AT$(?yCi>(s>r;}{6bnw!xT?dVG<0%-${jCvB{U_PO|KSJP3s9# zEBk?GAGEjL40O2K7zAr98+#~~*T`2JwMi-T$W0WUpn6cmn{`EjB-CmtMdytekf2O| zGtAoQ*=UGLbnd+wB0?y8_ux-}?Jfa2*|=hE{444GOhtnYkz_Z`NWoaEmkF;DSql1_ zt457Pz6s@R)FlUJb7dK5M=cv6nb0x-q^=x^0$=HVS$pekAAyJ_G zQDd#Fc8{(E*wT1%c)3$HYkCx#q`cR;!`GE8YNB7kyo)U9Lb6!7lK#%i9;{ zW%_b>LGP;CJ!*&Vr$w1YDA+=#;Xh`*IWSu!zQtAWa)KOis`HekUB z6v^^8vbH^MCSpe_OO?|S+33eV=_`OnMDGAVx?kR-=WM5h@E z&|n*63>Z}K1Ux|G)+4t*$3~Jf8X6sE(B1CP`)zUBR4ifNMnvM(R#)VYhO)Kll@S!h z))(fG=@QNR9%D^l8M9I_BtCRw)-tB$=Ht!FqK~t|YE$3IE+>1N)f4N=N|m8l61GD? zGU4u;;ySkV9`>Y@M;VSxMB7gfG1)@Km)VGUqIxHum1_%C)jzP2t5HJu*(YMu!Og9_ z25v9`Pe1M=EWcho~qtX zXURz-o@?!#>v~U~$oK>IVasYM5UjeST&pthMsmU^`e}T$PnPxz8Zk-Hz?Ba)T{``- zxi&*Y3QKWts_;D~ehsw#QS%X#()P&9LBTH|Xg1VxDOnHZ_|KkqSG~WBv2Og1!|w3K zdkL0f319d7k%z{jOu)FPIq5b<84_RBM_HRlg4gF2!o#gp*Sj@9DR|`roaWs7;uge0 zWCGR3Im&vnTm!FD{X>)_W93=lh^mEa^&Cx}=r6 zeu9%$cCWsi>MDwXrQpQqwHp}^Zv}CI{EzJ#&hQ=`g<_(rAPdB%MzTcxN-)us;+!|e zmK;nXtMigR|23goYezY7ES`Oe>UgqHLq3s*k2X;;mQj@ULa>6q$dZ7wM~}$X)jtkl z;~dfV-w^VFurX3|aQbr_Beq5*vXJ702=wr^st-syTOcuJ^cdWUIQ! z#<0fdznUXuQg+nBg_go&YttDs1!qP&*9*y=+MTgn8~SZ@Tz*gqlchkQSiN%DjEIE? zS-sO(uLW7UTq$SszA8E)tToaMb3I&*Q7qZppJo-uZ-j1y1dmsW4O4E1B(4m3u5OdD zazu`gwkuqGTeH(lTPUNy1Xm->>r={~B-G;Xf;DgIIO1xwtZ?Hui3t|HdV0Ml2&mE2 z;$>9yH-qD58jARhVFLV2(%Z9E@J2ssZ`F7sm84AvS$i9$9?#&&<*tQB2J8`s*H*q0 z*_xUL{^R?uO$*iO3MUDTwz{=K+11tO5|vlqTUm|$PcAK3aMyYZ23U^|8i1Eg-&{i= zMPd_jjOMF?9?%93g}C_wM&Z1#BMX@#lyEDUPT0Yd7)Y2haBh6#?j%>44l|;%VEsjc z3{v*$$b35^Usaq#Dv9|M!Si-A?`z!{QiE}48 zTrkleIEk793JkWc6^UE#>f}bg<01SWCM$ zEW~Zxwi2`!oYxN`nyrfg9&&~8Gj=xy2ZwI?DUjJ~{os}?-0n{}oE4A2>xaSPAY6d~ z(c2N#1GlfpTxFz^yq_-b#m6$D1t_{1jC*OXYH^@30J~VR3@!#!r_`yb+fCC2BAi?c z9Q0QPQ%RPW&EOu61+i{YM(ON*yUNcb^&FviFQuN!JELw2Nv$DEbOE952B8r$O{=&) zkYo$24z^()1y2j;Y6oP1+2c<5!|5LZ=|u{8eEf!i3;ay38%{(^jvc*n0WftHxd*bl z2u1vHJj!yf;gPex6Ts7lP6aBJqjh7#9KZc$gkaqR--I4X*IPKC%yZ?QMicRSXVSKSINM z?vB>8MZo)tgg&sweM+3P>F>~DV9;bKW!aX6BfrS!<}2pJu%vg?td z8fKh*zpzG$Fs>I&^I#D+B@+*DGH*c|Zg&My?-FNOoiA~fK&WJvcXr;gMv}#;a@Ioq z>O*%bLy6bjwShuu-LG$%zP*X5!3V$2RY26|VpYQ#HjK4F=4mqHSccDWr-H6kei|$= zAxbKB23OtSt1as%Hqm;;B=vzi{AR$?>R~5=S_6ZXL9zbXPL{cNR<&=gIa@6_H<-Kw z1b#YRefmS4C2u_+b2G*8D#R)dV_x){zQ`0GR=3LAs8Cx|99vp*KjS527w*kiWew35 zjlrDGsRa(+{%lRMZ10N{d%f@c@lX@!3Q^_R)3qhvtFHl8N@slL-c(W-)1TDy#w|to zCuD&m5yGwshtEq^TO%0M2@`Xdy6RxM{^;0{Z){&&RbeM0r%bsQ96So+7c5jzFj%5g-`b+X_C_$rp&HV=BTLUvRxKNa zcpu3Nn-X#(>iJC=yDJ&%Ld@mlkzVK6yvR0LYJ+4L>Hsi6J zm-9zq#IIN`2RJ>9wE=k_jRL4jjk8?N%#|$ezN%k#W}!OnuZpW=M3BYN&Bj_T!eO*20qN|-^ z)B>4r_lteKOP)s-({5k~J`;-^%Q6&BfRb|f4j`I(AgOh=W!g6n;y#aC^1LA_GtA9Z ztBPfDN{47E+Un0JtW(Ux^vn&y66nzoufQtL7=_!ICD_xH_D(*QhuNpW}8W%VPU;fHOfp0jdRQK3h~oaCm=YWzaXU+3o^;|fm2f;gCk_-&9MKpgP%dm9=gNLC0$)vSOM zxnJdhStnSIK(qP^v0Mm?cyr~F>gX^`mQY{F^+`LkIqU%N&eViTLI{{oW3T6#)3C_N z>vol>@SDPw6;wMqd<33_-K4^TAj-&i7N{p zsA?{%^oXi8+S9r@@Iydwv3Epm7~%tAg%SR{qS*;Z%At9!I%_fTO!)doICwuu3qH@o zoQUyFV##SQdY-f&wP#(m1BT866JLS>DOLK~ripMaE-JW&rZi*JXKX>C@|Do(}?nBWgywMJ64ZrHuS2wEOpm@!t8DJJykNj)gBv*4uBG?60-teHcn><K|Y2}{*n9imq~j+b zu;sJv#0zKNrua~nsA{`0`4j#a&B2nvt76v<+QHuNe(i^5WomZ)$*DqF(+-CIb~mX0 zTz#AcOkXxgX{nA!1-*hMv^~v=Y*Lm*r^J&UwR09V^JxpILDM!A>`Jo>j%Bdrim;eQ zSk&SymP8zA=pLujPuVLR6$^|A)m2JSCagBHP2onSR?bCE&b`ThYg6=43Vp1+{zCO? zbhzzY^w3WC{8?$gBSmr|cd4YB=KAzmD1-52vb@GnnOKChoVhM6A1v8id$$i4ZNkef zz}3vv8^u#GYuh2z_G_h)2C-P<`FvP;1Mq{)fVP~<=H9aY#s{^gmbnD)E%o@V$f&mD zJ|}9hKD?SX>2@tIdV8P8$K)B~vu|vZxnWBgm1n%b*zaJl{{l(YpV5iqC;Mj|)72Wq z-TJ3~0>qFux=YoFDVw7H{4;9Sz8-Lbkz4KI(pY?vL8yL>o0jMYA>7OYMaWU8|0z$? z%|tRw1Z>LsGYy?D&u5b$gs8hvyy05V$z$zFwwkf{T>8`!?NrXbLZ@6I(1=vMW~bsJ zo%BcZAG5(RP+PsZ32ud4YucWKi}H7Ay$Tl|Dqc)&R;KxW>ksZ!Z+?EmKZ*6%OC8UZ(zo0fJz5WSM z!pj)mm{Xsr#8cnGx-SP(yqH0KsPM_2n+`B8r!g{)Rb z*mcW|Ys$K*|3H&N+FYX647SVI`a)|=wEAhUpg6ycO6vgx`=m=JNx73LE~~pEDW+4J z=yVrlKwnA<>2jS!KfSoRo)d}aUb`DOH3=e68DO?in3u-o=kK3?>~jr= z(cH(;CSkcL1wxN`V8vOK)#Wf4uKO;fR8AN$BiXMVRii~LAxHDnO; zy1VhyiI476WcfU`qU1V1OJ8JQcz24Kc>vdL=LnmQHBZ%xFY*lqUyDj1Q?cBqsS=v5 z8!*u4ZpEDG9=1G+BO=?;(i87~j3d3v+=$P-j2LLGCeb_s&{=tfjBJ&tDeS(*znjE5uFSCk}1BRcNoP~$ri`9xSYi~d1A zbvZ3^a?1Ri!ST>vs|<|wDC_pSzoy3ApVXi~3R!5z%}UswJz=)3o9$BI?t(2hiRe~a z-$xeP1@=izjhb}sz4d{iP?ObU6mYAp_q^)M(=HB-42ua>8_7Esu#2xTwg=K^%)2k- zm?ARulSV3LfFt4AQWj>c7mo8e`eZYjn*v?Ec>^{bJvs6ovW@_OyTK!{fRpyt|GB5`{qbF@(y)cT%{Q9;@fU#e)OvQ&hPWkIN6 zc%wkbY1v^bf=cA-(L2J*)G+n)dqm#gg1e!r8A-7qkXrI- zBbk#1Wv_RUwq1NW{g0OFds9o>JBdMk_(gv`9uzx9N?8EYmMhH6?iOrcb~e7 zY~E%p-MdLT=66gV8&=UQY?xr+UkYBMTfz#$@JQf#nq)DGw(C$TEjNR#SQ*j_oKN^RlfT}d- z9jJqZZ9G{BXf#Ob(Sx12sujL+Gg~(0sLR+_T#=^r5c-zBSbH$rg=IJ9!p|lq=d|K# zgl%Dc`i|MlSb09K=A4~7C9|yGO_Hr?*t^kY{czMxQny*hyHTGVeM?2FsY%eJ=;mm{hg73zgqa)e^5!@uvLn(GGT#(( zsji(_2x|5;A0>IHdyN=+PA_kiTr7D+TQ63_=GxY@O0- zc=b@6cG;A_C04uH-yyJR%Qpa>bBABk**z9*uW}yu=gqbx6Juzcf(WjBaRB#mCdt{> z)mE(HkCW93{m$dKX8oarhB-;$;kE7ji3R2J)+SjAL@NJ$DATKib&2!pX%U3EF3{Mk zJt>tlmLI}{d}(L~yI&VpM^iAqnRCpRmr@ASw3{#|woYUtnE(Z-CLwr*7DB7~yeUov zs^%&;CAw_%v4@m3d&@sk z$kABHp4DaXa>^V{)!PBWSNH{(rVL6|f}KFl-_vDjRKbrz-vloswp`Agbl(cglM26f zbk|AsUtygtC#f~B%pZ;%&7w7E2<^l-Q|C%%b!=w{uQ`^Vpl&!GGC0_qdU%@lrJUhj z3|9`gP?N}Dec#79Mn9>iAT3o?bOc82!NR?T=;s8_Q9WO=Z1NB0U+it<(Jpw;GT}j1 zEBY{>!{vJqr$zjMd26_z&DUnDO*JCbFZDHXMXy)f)2o|`_r%NWz%F`yADC?NQ=`)$ z{6Thh4b-62Gqbcv1myMV$9r&Xc@_s(Tx~31$(W~VUCrm{ulV%gW%ZpqG%Nv>8_~sP zm!BSLayr9RQW6g*JJ-ofG?Zakmde$laH>%3;-+ERf?y-^5vdqGw3QMs8zi)$;1H6B zK^*G4$MX^imn9PN4#;GhJknfDsYiGtXDbBYF!4x!TMl`$h92NO#Wb0!ucj(PJey$Y zS##ZlyW>f7T8y2d_Pq%nj9gJy^~^8iYEmPs>RuSNUy=sHvL5=jY)y;rX+)_fu3?Rd zbto`F?z>_P=`(?p;T0M*t4^@$S&Q7LFT!L-&~C?OhP%^a!@Lc>Zx_2v9MY{BGg;jD z6iQ`-BzcTt1;#?<^wdNWwp+c-7V^^t-rhXGt|&K3QGWzVeh?hDNJtVNT*i`U1m#)c ztifQ}@O`_R+_`Yl8G2jXefuRLp3$E?sMxdHkY9WK5=q+r&ljK9lEJcqKLANF*R`ju zkzAH5H^F36&# zSk}7VGpw+Yn9B$~e@%b&2!j7qeoJ%lklDsjTe4Qga(!BOpnluwWZ>!e_yXLjqch^k zsf^8gV`)`(PhHF^1tsC_FZPa3lPz4I*R%l>U0{IACMKQFXzYbYOxBdhao zKXKfFzPl|0N0<73n^;|-ISQrFW|PVsT?^$uD(D4AjBMc?RMm8PC($a|8Lq{>4dR~4P^Qjr#Q zgzmcOKM7&S`)n{+q2LFOo3Ulo{vK;qT(;>JE?ng7fG|gV?=Vsya#TifG3Zq z_*?p?8zPIK8eI(fbV6VB7TH{l4MeMGQ;lh>BUaNPt4%@;JQ{8(l22a^nk7~mS_p_U z+GLv#2&wh>&FOQMY^Gb816c!r)tQHN5h|3=mlDb6o)Z$D;gA#{j*0?OBP*C0)VLi} zdj%_ujDdvL<9vx^!vqt;;zNyu7x+2j{hIOjWYl%nT`Lw7bE@KC%~(9^r+2LqoD2>Y zK-}wv@nYcjPlm>q{jEV8rE=!}@lID+1u5C<7i#3(Ca8gnuC9mQ_um+vgtv;M=~UOz zs8sB~GBSEQd777w%HuY$<3^T?g+^=r$a<)MUIqeBO3`DmVU+N9CzBbWexR-!j~BUE zi{ii_S8dgqlPL*_D~Vs7!nvlJ5ey!!(=dEnsM$(m?k8lH6ai+`?_I7(|7hBW;X3Wl zuCb0QW5ClS=L_PlF6j}WiVaQW$+zne;T|?z)h2YZEG*`%0Rffn8ZAA#yFt1^x=Xsd!~4Scp7T5B-1lEMf9?7(wmr77 zU9WgQKbf|<0S1QRso^@f0fiH``Fv`@C7AtNM}DzEWdSBuinYWDnX(?jxs3Qm2}+=h z$c%B!HG(q2EulV+GrQZ4qaaAJWSW{`fuJ@j51MIWxPc=_Jg7XLvZqORJ7=3O`$ zFjI|iKL`^1?LQnvxT$~KL0rHb)XgWx+|>|A-RCUf_?$mn6?OHE%NOwdioA5fhia-S zYVg{xJinH4nO|LqZvHX?!|#niyjbkL8=^wuxGINsygMjT(mUO{1NUkljG$ z>1Ev8$kmirts&<3cAY`V|2=-kUG|?|oWz^R$wN+ox1s;cdB0`*JJ|SH`){A@zQ%+6 zPQ>OE15b`A>AS9R{_%0NgymyH7h}e{r+Tqo&>TpIoy?KNE?gi$Qh98s%12hZL)Kn_ zOoA&(j8q*cVNFtfeCm+=Ko){KdI2pafC@Y|yit)`Sf|n2&q11_YuM=+B z+#hz2n&yp1PGmlC7MFilR;+%t8=@x{_klM(t5|)K?$)t0PlH46mnUWK_{%YPD_&5U zDcIQRuz9uf>V4&O+~aH1384IL@VJN(&EM;mu;%o(cRn%aL%Xb3VU5sKmYEKdvL0=r z!Yo996pi3W_s&_h(EUWG)69PXyPZ_=z>UPWWID~M{ZL}`BauYs_v?%;ca1wc>n)*w z*+Xm2wK*ZN&8qJz>M{2cG2D|Y~1{Q8o$1ziWcfEt^LBwAFjAm zBr7!gugk*1le(AvIyI&|sDS^GH~vMPzstgYH)+SQlj)y}9mlKE9jSGfN{0n`D6DQ1 z6Q-){l&S~VFtth3>!QN%g%)x}i;NN{i9sIWgQX{PfPWe;Ut02itd~CO5k^F){n=BV zN6Ceg$AjEft*UxdcK$mb#KnI z^`SDLYT@QMTAYoH7@1*)=QuhDp_FYl-uj%8lIKD;!_jLkOk5P@u!toi+CW1E$LK+U zN5-;@Z#{SKIR`ZmxCyOaB}Wj49>?rT=rgEK{NIn)I;6&R zvJW3&2z1}(JXxG|gfZ!9=K`utR+ub}AMfBK^c=m)f#~djTLp+emGgH!GibRpxhdgn z=BUB`KwA&B;j4H#gH2S&xUT~+SK zTc|9`ZWBdstj;%(NdJ53HA%5Uw7?Y~q)8eH)2Y-f8MQep&QSc_Hl4?b%ZPh42F|Nv;yk|RI~(P=O3L0vaxtd4c_Pfgi9(O8a{8{HLrHDEuGdIYJ&y=(F=Tw0 z|EYn1_{{THBWzea{j?{_@REOM8ItI=#%Z0-8SVk^B;Wj=Vx)e}bSFFw$ZU@$th@KbMx|f_iVMXGt3}O@7vco! z{RGI8NzR1vy)dH*+?~WCHrh6Cs(KP})lBx2suYSF@eEBGT)&VX?d2~kEg!NpHI*A` zk2%3{fb_YLVhor=D~YSr_4%p8bxuRdQ70gDShD2GMx04Vgt1@+&m{T_^p_Cn5Ebg7 z2^(5&N7xAaa9_9N_4sO9MWh-w z)X{TZ+#?wd6I_I&t;{oWE-oSlnvu_szzi--IL@?jd`Ym;S@m=E83;;OVFQzyMQb*0 zphZw!osKWgDg!Qd;#&vnx5BVIf7XO|5qSzmu#=JR6{Gaf)o$l~R2S+bbP!pwQyia~ zJC>HUIZ29Uk5M;60X2yk1YI`>79K4f^w8jGJMo*jjmVC8GY`Jn8|B=Q1hWKnNu%n7 zWGU{CE9ncw;?VtCme_4=lDUjz7RNJoLDH6dqcWs3tTmTovrzdcQtD;7tT%Gs&BSfA z?YExR^h{&~<3~cMFAeiCr$7@k_F&)+<$%FV>a|&10PoRKYxUj;LTq}#Wme= zCV25Nb^A$!*++g6A5FX|#HAzOe3H|ggG&rH>_#W)jE#k%g!u_2HsL9xTt4SVtbAed z=@T)Z9JQ&h7!9Uv1Km}$`QBZl2k%SzF1G%;u+*pi>DN8}nrMD^pQzpgL#CI1og1Nf z^z%#w+>g^@PP)F`{VJm#Hty?xR5Q6nyH~dt;Y2X4M*-RE(*?%SQ(u2uH6y^K6dj}P zB(Hj!ZQqOUS9;2UPRQ_0wz@_Z)F0QB$g(K1dU~rlHX^G0g2_&q#-w?Wy5>53Tgsp% zUqe-@OwtyUU-MD8`jJ9KwDgP(r41x{U{WOEZDRn}i@Y$%*yM^O57tLzEh$f_`rwSN z&>&CAxv;(>(l=ic#JwRz?zU@XCQB68Vbei#&fGea(Ck8z_F+DrmaRv++{V&Ow4jTZ z#*C8vwxL)umu94OSfo-?(0rItP}+4W3+q@S*9a4C2~=}zDzCHbGA9eQxzF9$VgM$i z%r(K(%YC_e(*|5v?x|mO{|@HYD1pHT!j%u`Fu2zJi8P}3pv`p z7ZN5C_hxQDTT?6xDW-?{xH7@b5_X6q?2|!Dgh?hLX~a@(m0@BNCnLTbuPM7v920NE zu+F%gS{4!A)i139%9zbCta%vGg!48Rm#jQHJUu?liMLW664mgvvrRe?C5C~u;E5!| zN-}sX!mrO?RL71bneI-2NF#Q1p^W}=edKh#YN9+t#oG-_>D`1L$h`DPoH5<2Oh4#Z zq>YJ0I4S1Ci0UHnM0U%1&S?VcRvsI;HCy^)!13U4MGC+qgcZ@V=|-9o7lDt*(^+UL zoO~cEIgx zPp0u&5dgmNg=#c2{sT@)6w{*2I{(CYVJ^kjOe^zsnGY;n{S8TEgZKqKf4}4Zkx;My zQ)WG)#p=2Cv-^D2LLEz_nDY}(pl2g$O80l=h2>Y}(`M{CNqhk{PUH4-arp(t zQPc~Pa|o&LHX_PoeVMo^^)6xZ;LTd&uApK_NH$FG@iy5p?1Zb)JI{q&6L$u z7l^A0$a%~<>EH!bYW=QaZon31>y;=tCf~x;KDCXe3q&YXa6P^!3*?^(&jOBwr~Wna z+($x2Dz0%?;=TKU_Yya6tHx*8&OrWKFC>sOsnR<^s4Smzzlal-dU-2a@il zUbtm@8sqM42FJq>Oo?hc1zuvWdC_u<*94MY3<0dkak|$3(fp9kt=;tkFn2=7W=}RX zDv>!^xX>O&{jPI3S3I;Y(0qiC^VJv5{PV;(Kh{q;m?Yif>bLh8Akz2TSDh^jC;sH0 zC+-nz8gJSE#0_opkISUyRkxpr{yvqVlU+1tEm(CgM$_wCE!tkfhS^(L?plc*x~Htu z4QSjoqPvKTt<8_GSYvO&`K&#o*(*hc)mLh zvgsYmU&1h;p;8{lF*kMDDX0KJ6|tnRjG!&=t9(mZS_$z(jZlbS)3(WDP`G*f`vVs4 z3D&>04Y-9--2f2S_qFr#s3lD0W%2yP9@`;D#r>EtU!g_$eECmyV`)3-3yj2Q-rthd zLg7wysTWpMZeulDFS>PJvSC`kT1k+eLSA4RpHvtH*BeAwG8)o;?8sXYUaPqEurJjA zXmtD;mFxo_iKbTr>#d`g^n)vjt6hI9jFXYmX+msy_sEmNp_T8T%^1%fZr4YNUj`>E z?2zqyn^y_=z6>;*Y*+GW4Ar4(d!-JLrWcP*O5y*2=d-L}Mm@tTlAn0j>iuugKAZCg zdl|}&)4`%Wos(VZ4jKz`haRkc9;L&81SD-+pJmC*SMT>De!?C1G}xV|o$-L=E*hzE zLp?WN@`a4OXk-i!OJBSa~7_)(CUT1(YmkrQc-c#=`{Xgs>q%pUEROJbNYV|()qlBlEc_`v}f_cac+YtdE|~y zr`*(Dr-dEnKNqd}LG@;MO;L=v)5Wdgb3op0_bcxf4fYAGz1l@yYumbmKM9qb?7YOk zW|W;AynK00i}UZT3H+9S*QcSp$C*29ju^h2Nu6GQMCpBuomwn9DSdDy&ahUx{S}Z! zTXg7-7&rXky*2r@>p)}-AN+05i1cH9CVw~(1&=hJyt=IUuRMAtzV|{N&|~kv?HeZn z=jPl3Y%vE(L}U0%pXdAefPY0qv8=scN?Btbza_cm*K2D2;bHObH{+Y>Jl4JwT$PB9 zNfoWn7jRtevufvybb~MZ%ckliL=m-Bb5Nx%Wve52PJ+%5`Sp<7cpjN)Jn1hRuHCLr zA%1X;I1MnxfPeH?0J!E4T?0Ye?tZ8(QDM$&!7hTAUkTSd{+P6QC#B2(luwvznm!K@q=@8R_N|2PhI{qGJRT5 znfmlglH@~9USPP_{T>|R@Y~x-Egd{_z&@;7NFfXU@1;)x|0~T5C-BTK`3X0)EqkRV zk=GCjy0Aj!?S0KY#KRDCa6{&9`C4>!n-&N`cav`!$Y1_Xq4g;-PkYq^#%g`x22X0j zgOz-MLUtFW5JRiI0=$O*x%{Z?zXVi>+!&1jLNUL1TSyDw%H4%>zs36>3M$`G`1Wvt zJ4J1;JGiCn&Sm(Mc^N^dDiMS0D5~(RPq;&{!Ctd#9|q zJ%3@Cyw4x|Lhor|&Uv2ZWqS~^xtr?(O62YO2%Ld|gIW_(md6K2Kel%x1+b4;9qxd< z81LBn_dHJGPabC=Gpe@FS~MhsMDeF{MkclyA4sqkbw&M|><=Uc_G$fQ!DHb*?^;%L#F~9#J`xz~qvK7^R;NI8Cite;Clqh!v`#4ylkY9jSklC&zHR?f3Au z)`}&3ktz`Lbcx0b(hHqe93u_Cz++i>#ZYZ3JZ8W7LIq<~#}%h1QY@FMZ;*UwIN{85 zG%I3ENjRV@-p&0*LyLZsm^2OP9_~B}mV30hS(McKT7Q%o>DQY^jFv&yWjR&nHh4TC zw=A!pvLaEGD~>vqXBB`WTy|m$MwQb?MYH>k(5d{Xm#_}x;aI~|ES*5f2NLWsv+(s&V7A_ysyiU)VG8w|k47|NcCyQ`x5992Q z!o~Mp!H`%|%3x6@n>ChG&$DP|;SIVwo1MT~+BGqPAqZvnhbNNFwFgbZ)P0k-ZKzK* z^HjJ~yfu++75)t82R$ix ztG6T&WWZV_6}{WT;8#*X&2efefMcRqGR8)`pk?_w)Y54oVkuJDs9V`KPRv0mDFaMg zoEMa?QO6${sFR5sN+R_UYurVZP?`3d@IhLu#W&~Z`N8yd5z~MVy}41sv77y+6=eCP zIf?`vO9Yp4*Mky827S)%0qsrl{Kh}wdY;9~P_4>e%hOTJI69D(&M^#twWQWYM!k)s zq0@0u?G7smm3^LA26^R5^iys%U+dgdzr#=eWgGq{>FL3jKLEm|KNlVhjB}JeYhC@D zJTXtscL_X$^66n0$orm(rJWCK+Be``vA9leiDkPIvx$Qk;gs*y6?ZCHe};z`-L;&e zbx&_rK9N3C!{(ctR~CORaC!dW`23+MRrUM>RkVhN;s1n-cP+zQAY6TB(EwrN|NNPD zI+baRS47OXuCSf7owl0`F)r(xUi)xQ0c!k?lfHsPB%?AaDf)wm^OLCe0a;W7)HPt` zQckX({4B%OQwq8ft#${s+`G&<4R-K0U5mGnKXn&tENX}S1X!lZNz$BB=!F(e^gJ`LJP<07EZYnO7~`UstMD&Jn~gGI9g+!}H%XOtk* z3`sIfp)VuOEB^8r&(p6nhehfglKQ(@4q|CaC5_;}EIZHo72#r~O|xoSAAZr;MA&a;)x!x>JS-|0k_C$)U#(c5$4My&EOO6%IYGh(pAWXN%}=0 zd$X54Vl3gUCC^~~sxO(tKM5lXeC5~I)mMpf^!NsQlqx(iQ8MX^GBwMF3255ic-n6e zQv}k{KpDx<-Yi-W$^6Lbt5V;}3u+C8Wyo14t|Donl>0^@{p;v?)pw?I{F)pXs!8m3 z%&43VlG;I$B)!g0q3sSMJPcr2&=3nazW!A}&Ww!MMcl&n02YrfkD|7US)G_&;tU5x zy!lw3p(w9<3xOFeb*8-oKXpPFOvfTTL&S0?hdf9%-ulgQ?6I~!0CyE!U_ETM$sTOi zG*dUM!C@w7a|$!1fQEQGEzl}J(y}w@zOnc|uc;1)!eh z4(ByIZLc5C-2m!|^Ikn&0O*_=H&3g(d-XI8s3$f+JuwSC(>kX$`w553bh(7o4#+2S zKt36I5!URfjNP#s9Jf3G{Lx#DruIjd+4-hPt2@4bQmLl90$rdvKdQniZ+j+unEn$k zG5=vlUg4y|1sS2F7bsEl5id>>M9@-IyEyegf;aL>4tFK;2Nqu7tN7pL)5!HPZ@J$r zT4TvU)h&tSJ2n7p6bCrJMoxh93)rHvfQ?(QeBuZGzsH}}WCplz8qsZo>>Vxpt;Ei= zxd0q*6RnpH7%jNJ(xahk@hXKl?z8*;_I?`+`(1Uq_vYKUdpT9q^t^uN>UZ_r-rYo7 zPxawXI5nGogE`{&eU9w^v4E(eM_KaYy>yq*|CfS_f+o5Dq7j1R z2RNxS)2!>LehyT&I+E0_mN8)!5$F@yU7;k>7Bpp*wF!zGQV%zV`xDBB_1dZ}=Z!@} zL^GhFZ4V9}gbZqI83}f(dvmj9sr77I)Jkg_1wk4_G_)0QJ|HaqBmDL@iM;H6{;0q` zx9_>3xP0&Zl`B0_ zL7*z$093_~&?;J-ua&BN$u)GkR4#)}x_tZ1ug3IMI}ombiMzHsq-48>4dUnDLN$O` zEoO;v&ox=_*;C-=Bu%!ptA4yr(0MR2NxOsP)Ih=IYyJ(8fNoy~&S9N-njC*5)eml- z-y(JWIr_Hc_y1Dh>*MGNT?;|&#q-QDL>>U5Liy!hu-?xi5ZpL+6+}t+S zQJy*K#W-y~7|NXll2Wmaa)NZ765Rn8W$J00~hlNoZF9R z(0%94y`pt?BYmjDUQ$`Mv;U&?Ls?p*FGdWWBK=bmC6&kq8p)^?L!(SpxXA}-EstNA z+QVCm=AyJI*JxUbRMeKz`i;s~MuI1jVi$~xy~T4yUDDxQne{r!@dncIru~uFXbET} zsVM`*oV1JKK@T1lUuc(&Wkun}DeEh5NY-W!$0!+2yR15x(snhFf)iTl6S8|{*)hSs zYx%Q6I8xdTQl`+XCs0$8E=iSvZ%^S!^OR>IyxD(905j9wOALYFD3WCPq|rDVn_6Nh zd5iLgz&7nQSU5pXeW@kUgKq9J6$k4>}qA zC4s-G|B<&GJ(GXstryYGr!!v0wM)$xK`&gxojz|UAZs!)H1sa0-h7K>E2@>KCs~2f z^+^}mo)2~vhyGo8)NIN2MC;{|1o7X%(|deG!MjEj6} z#o^Ov-MKd$kKK(v;h84JNME>P2(X8djM3{y-i3S@kbVXIHyxa-(N1|wkm4Tw=EVft zy!@50_)j?BY4oQ&ljT3*KIu`O5wOKU= z=^x^HY(39B#1o0}lXF4tkKWZV`V)9fQ1RUo+J9mPUjtw0>tC66gLa}h9X$p$6jycc z>#@$Qgp&i^2EHaYrx|b~#yo^k^QKL66&6^@C0h+!Ww+Bqg8ysrE$7T#W5#@e^*RyQJ}a#8a*^{6G`uihuIcg!E|Kz2a4DOZWp!jUZ)hkW4z3y2Zqo{DVRTLd2hN ztiWC@w7}>f#td3_om6(YhOH?>ylbw%f~FUV`tOR`V)8q^G8 z^$4qt4q|~ySI=lt)q2Iorj4sKh*N%_ziPtC{@?vi+`=v9xX zh!-d2^xhEDNArisCPP#SL%y56!M>1Jjtcs+7e^FkAWAkmB4*ig4i-z0bY;*9NnfUi zCQS#v9JpKh1h!-C#-=LN?;j%yI@Augm)4oW@eq^ekGy*)EAuIK(-+<>!a~qCLvNeH z&H60}Wkta-e^4G%%81;t=cE%_L6Hv{wl8scpV=fD;dC06`_IQkFzpq05^m*UiC$;vWD~19QrqeC8*sJ#;hX)$+_f?8pVfN;uuXr z8VhBo2z4Z~ZLj@84~8;XC=KP<=Y&G=r+;*h2lbLO&#G?^A4_$Izc65h?U41>rWQP< z$zjbZ3LVijl8aB{2}m&Cxs1@SHz}?PPTML)pVc&=b9+wbRFX=Nu0bNFh;w%|BSk%j zrfY*C3&Xs8vQN+RP4rDVc~(ls_;Z;GE~VUZpT3}wWTjC{KBWFMjl(2KTI1_-FAt`* z+V<%|^*Wd9TaF76U*x0F6C@>Vhzv#ACI=`uY{1_vXM(*-(J}Icy6kyWdqyO+4@zgu zM!x3B@tXBQ#{T%NfAmo%=C-@epVV4xd7nYQsOSxT2m>-O6k*3)0%OHsr*BNm0h5i_V_+FI* z@pOzXm{qDJ&>|-?sJ?55TN&NT6|c;SCfGC?n{$BU(1dxFRpg+w@m>Je>i~#6T^`fu&n)H?C_XP!oXS{ETG$a#|Obz%r zKI~$sXsEr`jyV&5)KijSGompFgE_2o}RiVnAcxg)>OsgWtSZur$Oo~hg~ zm=cD!jTO8xR;BYvBrPUj_@xmrjcDEL9IfS}@2G;>XXhkLVG=N+D%9ls6Rr-KsN@LC zG6yi=%bilL&~JRJa)JUNC1%wP=o}27Nn?KGztT<(80Y?G8UOR!7}IT zO!#)uqqbv1^JPUXdDV@60DkJW-6lNuha@cE+OOyl6vk(ol%NW6L(2?;wy&thrCpDv z@f49VV@!|1+qY%ZJU0eVACma$I0D(o^zinqIn@WomEMMmFV%58vfpIR4NSz_-JDfL z^=_CXBS!Da3!Pi(?uN{brTdI69`$}(Jhz%l(!`9y9crMc92xQROEX~2K*tR;h-7*bc5>9I<%E>lee!qUj1W8+E;76hwdBgPnsJmYRiq||q!%>ojEozk^9 zEWEN8xf`lS9(6cl%KE!@j{12H1B#b5JzvuecjlU`3(VeE=A?y86t3o`QBcHxOma2h z4AtO?my46vk00u=M6#m*ZEnIujtRmyU^>92DH>xu#3V{~Rh!2uUnV(=j`b!Ujgu>w zv}oF2%D}#wj%J8E0JO$H>KutJ=~@HaK$o}+2i33ZiFRM3x0+ZxEwmCU>~a@kC`YY- z#e=J{p=-(GoY$+X60s^&cF^NBg-jgWS!U*d%V_YthD3E&*M8%{h<6@6e}%jm!kabb z%D$lS9j?cT%)@PxN#6>Tz(#IuT;Fc>F#AxmM}NX410U~F)Tq7sgSlVquReFN{&=y9 zezPubj!m?Na+$DUXHiR@CWBKNB6^WUNV?hHgjYDIpmXs_HJ0ccC_JLv1U^01$G*Hz z9!U@?e2Y)1FxP2AM4`*HDok%?C2=+}J0o!<$QGc~tx@6G8ptrSgy*ZBmY$^SN&c^A z!IQOO)2QUI2gmHHHPapsn-n+~??>hljq{BoL-IyjnSQQO$wz_E!Q4(UZNAT}#iJ0vY#|$k)zpXEW=b)bCmCc?a$su7 z>cCof9-uT@w_IQGc=Zgm?!Gh{HkNHCyAB1Ob%SarsL7zLm=P>6C@AE*i-d=dCw2D) z0RsEgh)i)$Erw`cPFaK4x*{~R8i59=noa;!)9xpHcC!U}H>REIUUZ!HaRvRQk!UE( zvdMZErOy?KI>NFPOi2w7nUqw2ByQIkimy-hc$T~LzfO~;G_%9YVcl#Cv#cB@qLi%& z=Lj;d{ZU^E7wz%CTz~=Ieq6AD@cKH9cdFrpP(xB=&45D^kz!!t!d$(`B5)^tRM092Oin z6!Al#wiN|H=*=?! zZ|IGZ#655~ueKc64aGso5)PGtaCdKrXHBtra68 z<)VH=_{DzOhsWE7F0C#gg0C_2_6`12+@S7Q3!}uw?9B9Z@V=C&`j;K7F>1^g*>!}% z*(~HHmMziJbXgIn9FAe-c5gXohDBx6PPV)p$O=2%X)_Z;8!#&CJ`rDjZv1U!9Nt}| zdklvg7{4z&&M(PJdp7`;8s7L23cK6h_a~W}LE%cFBM=*wqGtNh8x&D}sOw8EJ$XHh zQpx^JYbw#fTkFNGUME|qy>~0qxEf=Q1WZ{j>696^?Gq`-qe#%!#E`^}X0ruV#cE)4g* z%F^Kvnk^irWgMbm%)$_wHV_c5N{Lr098|PP05~B%2#E(=)E(KJVwo9cAkG4686Bqx zkW~*v)d>_GX43GXu-L|ouq;?ABH@rWY^~F5zs8xd0J;!KjUG||m`A^3&Dm2X4V-P! z=U}b!JaZL!II?5~iB_^n?<9ZWRo3_;xax}36{-s88aYp1g*@OId*=Y8CYO8{_yHY7Rv8R}Cr1X@K&otbW z5*61oN+TMCWcA-PS<^U_rnN=MBt>H-1xMoIx?pB1IMwE#iaXDWz@ny2!YZwbcM6S# zvq2Kc3FG-xCPuN1nT01=uW3kFH!5vs?s=sp*kQ`$Tlo7Y``8_b=Gx}jpAfxC(itsS>Q;#<#cVVv zeiJSiwk*{aay76^#=I5U7Qxgr9?~-=d&~?QLn-3T$&yw4(4F8@wxW|==2PSqo8=MA zB)+%_$lZz5ATeg&4PHQsnz8RwC}gik$SPKyq}z9qu?wKGD`G6qw2>rC z0Z0nvFd+!7R9IeVhAAsm)v&XKOB9Yvf2_Gc`F$(l2e&v1)2t(I#EiL(&gl~+sEdY( z2-mqFr4{v8_2b*Oo6M)FXZLp63lLzZNfboEyCiJAljGZnc(z6c6xL0)9+oyH^Oo0c zC`nX$M7{zm^0LY)olk0xc6_Q4=`*PU>QsB8y>g@V`k5{2r&8GXxE5c`_Iy8vG9I8^ zqLSpd@I7`3Rx36x`ivn|9~|5fevscLHi3D`TBTjU1_rl{;0#Rp`)z(rMJ%~W6Frn3 z<$XwTOb?=$$b&hn(n@X=EhC5I!V6&nsGcnzyW5o=Ow zpdvG_hAIjZ^BQm70_Vytt@fyUeQ5h17kPHZGc0D50H+g^$70qUYUGH~A=12PQK}bX^z7v5D z#Ig!swZZanIv+g=U3#*dCy$K`;L_&ZrI2dyJ)L+ceim^aI?UzD7J83Q5LbFv$P?VA zq-EJL7L#N1RB&g9&aabwEv0M>30YV8TQbS*Ae!`*liOz5LvwLD`ZHTK;(a!EbGd4ckVZjYN2Xst+;eqc?_IrK{TYiNpz7$S{_sBPM*{4qH@ z{~#1LcSb>6!z_J2UUp+IOdgUonv8G0kl^VQcvsp!t`-xtOPD_`cTCQZz(qYZ!p33) z+5KFAUou?Rus>mF?+}v3!W@xZ%H;3+y)uzCe@2nLlv%r%w1JCV+EjrXfroOMh1p{% zd1mqHny(;Nxo^%M=Z&1drA$s6CFa_2eE@4jQxx*7WJ^^u#RXT#1{JnyIWOQ&}nGfdp=X1^bqkGxIF; zJkxU8hjQh%Eg3wzU;4$7d*GfKok zHJPJMUt@i5^OHIq>P1ubWIH^G$+O-uM7Qfaa;{pX5arAKQQE#SfQl++%Pgx(abuzS z(wSXYgUbMBbB9dB9?zFrSlHVNl)r6lbu34We;A}E^6GPZ6|yPfDc|2iLNjuE?mjwR zOD*B6WD^kmv6L(aeI+jQR4WQsmKxj{c7~|ktx`zI!3HBnPmn&g?e~(W>CP9qISdq@IJ*q7q1vUO4Z=T zBpj^)4Fmikv!6 z+Eq|8(=!t?wHJ;rXXOsx>lpg-?~-{ZM=#<_h=PC zb+L))Kw0QWVMTE=DnzexA`vtefzhGXt{ zo&wC@M*G9FDG(G~&~&(|qs$1oM8X5CLB`O`B!;w`O{y)qBhvcP=MFiF6tz2@$S6Ok zndxBV;cXvF6ipH!jIgV(0X7mU4BDi$d&6)M{!{i%zDuOX5O{i}acY5$U4)Yo|9V{B z@g$$a=C4M$vN>PVs6yl7X?JQz?Xi`#mCe*~OE=7i z`7+s)BQ5&2EwDd>GKXZ_Z%_i^IDVhSV6fZEptTd82z5uX$C(gr9$fhoPKv6HLGlo#qyI|tzcNrid zosa2T++)k9Jj7?MLu(YNNap_3Pbd`t2x_vFlG=(>4zTAy5nmYkRueE>gn#VASJDvC zi8gq-ErmTs6)AfTQplt#nFV%3@K{(V_9P{xs|uSChRKF(%Y8-aA)|>;mlXXNvO$|i z-_L$B3VysdJuq;~{D2VX#!3BZ%vC;>pFGt$RlnOcO&t$6F=3u%A9&er&-|J-ohpJm z-Pb4=%gtECv`g%yV~!q1uE_ykU_x0x+tKr~WdkZ_Nnt~#6&YoebM_&qE8IDp58dhj zOOxy!+Y&-ij?;agE*P0M5$MxJ-L4OJ%*cu-(yhk7?icJ5g}@9%-PE!`wmy}kc?mrd z-(w`Y%KJy>V8G7jpibFVsuzI-=SJxEwClE76~(*gWP4)C!kxTk(Pxy!A+ zls)Qcg>c^kQ%&x_rkc=Srwy-lYSMy`dIo0wUw5VPiy0A)fN2Ayti|HW_%eZ+uv)<# zf+d3Jvg1@JGbf9RVtlGNuUhHTSDT7?LDjJN1&#FFN+b7HN_Q|ioJq!(%z3=(M)nWc zzcW&(#XCDE#O3xcTiZm~LpDlTmxYa)Lup;aU{t-;PJ{|S;q**L6#0>3&@?b&G?bv# zd%yAP0LhkN^V2%ruj*gpdo?v=X6f;VcWc5b0qEzlZ8&qsuZma{95t$~KMo*uAQKJz zcz34Ks=OtGvx{-u@r_T7@(P#0FFMaJ2TapXyc-xFQ57#_HAy=vJZp*Uv(d>GviT?Lwxy*9ao5~ zu`+qF9dWUEkfvEzV!>;avk21SDLTWP##o6ITTDtZl;Re>3%GJe!&`%|Wd+OmCk%H{LQ2RfdZu3{g_HHmFjui( zm4{=_7)ZVD@g&rG%w5~y8J-5}uK}qCu+Zvk;W-z zln&=YS3Y%jXf8o%}M9%d>zOa+-@(jaiR&6hOKze@$mmsfn3n)j0Ncsw3 z`CDyX$||f?I*-!iDrq>|s2Gc=M$Nn>cfTjR0A^-p(GJaK_P_bSU*`0%X(RQH(Zq3) zAD4xhcwK2_VtBgnMn5^z4LT+-fmyxwGqV~2pcYpIc;Y%~JopVFV>sJZ11uZ;!$bxP z3Q?z1?^V?ccHLCSA+n;mpmcU;mg?ix;?SCFpE;5#EXK*R-@AyPS{Phj2mx2&eyV5>kg+a0SrwR9t8T^GFvr55gpYnHk2ix7)de<%MXzn}gn)5&y!t(Gcdu zvIv^|?+z_JkoSRg+}4Hg{fJu3^$|-kw)P8J@^1#eY#h8&3X7<(OPHJA=tQWd(@$#w z!B+X#e!@9bvv6{|M$XAvBU~A~(SshSr)?W$%0=I7HdY^>i>r7%3za=>OF&U0v_b|g zyAo-a46=~mzJ%=>zKW$;$Mg*neK`O-j4ZX)BC6#syU8L<*bd}i>A=i&hz}B5uo>k@ zDvt!%ElYGkeL>I|Y=>!0gYfp#*adBp^EGFo#Y#)J8e6=*jGmy%uJI+Z0OeI!842w@ zrBt2ab#j*f!!LFs#`=WeCt9AjHu7spJejobN@kD0Tr}UI#pfs()JL%BFeuX(qZ{do z`0Wj;$L!i`-c>tg%-}Gy;Rdzq{)7wkn84rmPQcpb8^h5N(G`3Yi&swJtK`XdeIA6U z)EcO1UnCN@#juitzX;finNsIu1Cm&?z)|IaC|MGOa`sBt9XB(Qp6WXJEiyxfB}}~Q zR7#&?wNbYaPthA*aTcr|bHgBBNEJjo0p0Eq0?#tWa=o){spD&ln>Tcw`;I**XDj*m z%ul9`o`p;PYH8%r*_2)G^RU@Vc`TP{6>lkLNkv@0dnPtH)`hB99}$~uN|*iBjaKrT zbUT9W`_N_}1tXtXPd`WE@4Lwg)GblFN-zoYo)w*a&fJfuai<*GciK@u$`@UDaZaWD znxSZp;Ll=*oro%y6EP#zbz~;dwjZB&vDE-;!OZvWO+vZ6b?n=~;sgB%Nfg*vrWten z-FtK@PMwNSv-c}`w~l%qeK@i$V+AH4PpY~McD+k{?0#Us5|WySnr%X0AghDP!NtvjmV<2Zw^f=PFBSOWV zR~Fwu)95*8Nb+sH(ApI&xjKeu1-vGE2a`)VK8^`2W0Q|`plvW!0snHgAos8=IfGQU zVcro{bR7U8=6bDj8O%1#;r{N?l4@Z;WfDmO6PdKV9{|)NA$bpA!lFe)BPG84{{&*i z7%c!*KF`ADhoF1>NM46GqVfwGlqd~QMTzd}8=w_$a=dsv;zL~qLZIDf!4{MS99OjVxd^`8GIID*c95c9JPoOs^R@$UVhan0gJ z=wtU_KIsdyOskQq5G+#1!aT=X(D{j!2{+4TY0ivPat{mFw~qO6lp-RAg&L4nE=%HZ z#}&;08USRr3LZLvZ1LGofp#W9~fJW_3|+{Ogr+<5$^Jd zHzv>|mgn4Lup|=NPl0zcl<)t*=+P23P4EaGC1mp0-nIOcu+}`+ap3+I-n))yD_1%Q zKr`)9BMuOeROwC+(|q7yWFa9#`i8NA?Y*7p*@R`#yBR*&1XOUNA4Xq|!LrH~0HpC~LlBGN%D-EFgX=&01FMr|H`T?FA7dv5;lV+z(}9?M%JPA#FKy*h9YpeFSYZQ(=`17wq1dU% z49{L)XFIBc;MCJXXbZhwN3eG3=rQRFC$GGgypM;2nmfgPeTbyT*a z4)9J#KS8aUV-g)`C(4CFVJ7}|vm&~ZvoL-NNtEaPY7&bDW7cz0@r;o(YF3+BR zk^cN%j%J^~$VIYUXMJKHJgyR7UOF5@Rh4XCAAn=p*FVJ=#JBxW6@GCyZ}#f{q3tce zs(iY3@r|g6APCY(mvnv&ymX3wlyGtca2o@dR>y6@Gvxhv!JXW(9k$R8N{*uloe5y zp9s#s{^v-Ny;TU?>PcU^wNk^X`>BS}$(Vs<9uMw$F~hKlTE28=9H| zwhdn)_#4#RcsOv?g2m*g&#l@R*jU1i-rrrEi;5>kZZvA^$X+w`63A3g7&|MvUme^w|bo_ty3HJHr62w_cPxE#fP z5-*iMf6@G{T{EGgqtRt~Q<}C|hQ8EMU|W{xy+8&RL53V6J7na%I>{1v6*C|kYnVFv zEK_z4Rk?})RKcxDdmn){hy=A{u{nfc227L)Tlc`V2E2bO58CxgOCq{8%5-C7)D6{0 z!rDjQIzKY8*|ppgxmsN<0tVZkD%H2vOGPM4BPn#%t4)H-tk9o6Xyu%!Aqp`k=pRX9 zY;H%ocfWpC!PZ(g z6jk1iWpfcz%HJexEq z<872}p`;ysPxH#E)vm`N9>n2du7UClgoGsFNJ#1`(5r|-q!lsCJ&9FgUc+tMnufwn{tMtIW>SAFpckS4J$ap#MTe0cd|ST zM#1J*B{Sb~Y2Q^~O5AeNh~#W%MMS@BKlU6jNH(y?7Bv_xNK;izD3nofYl{;xPgWr^ zKHQs{dS$Mv6hKWl77xGnG1!hn2BR!tXuM&O=Dp@ggwldT81L++T;VNy@i68Dm0XXh z827HVXshT#mJ_zVqly13sVoFkA?8mJF;xBK7WDbK5KE@A4+J#tD)t9aXCe5oiRHmr z-Dollj5U?e6srNbNJ)L7%{&ei3Ui9$L3bNR`dE?bs*v3bd37T->KD}&Y9(s@*=s7f zStQXXF-lIT=NL!K3AQsk5}n6+2k=`g+_{EAmc)JYq_PDkb)yE1BiI?^2HmXGZ{KAq zT*=tIDo(>$Xp*t1l6?_M7!>Y&^!kN6+7clXnn@_vFp`?sn(`HJ`e8#9r$2(HFeUjk zov?$EGwDZr?7jx!!ugtw2m7}7+8bgFdfJPXsT) zJB0*nWvbm>4Msg1Q!JJwOCh``k^D2g-!jwy?80c0o(Yf3N#lE@ot4wrQ_ug!YVzN$ zJE3uED(q1NokVh_`HH(T`y#+-vJj?Pr_VoATdiD>Zudn&27TdXTV>m#rV6rIBxQF9 z0+&`qdrga-k{nOkuL-ceY5>+HCtq@(QHlQ^04=BgbQ_j_=2ySE%1g$Er#JJku9llB zw?EX<_!{WD<6;U>n9;m+VXCHX3^G0g9@& zN=*M>_&o&oe*T9w>{mFLXajo~h?nH5M{3(O)1emK_3~_LDAehhb{)7*@RSJM_=m1_ z)R+(!V*$0_AoaG7)KhC+y)0I$fTNl!R~^kK)NoOkWKX1JO}Y}!#NXBIC^Y8unx2O-P+AP5QG(y{xkSeytX6eb#yOK{l z77`@WCU09MqP+3!TStlal)~k!`we<0NLs9*iruX!97mZbHP%zFkCeYEmR1(E>a;{? z!Qq&{`^9^@`r3O|D`!T=>bBOXXsc_0`Lyu`8ZPydEoA2>%v;PjJ$q=9bdZJ@T8NxX zafLm{SMyYTNZP+a$XCI}Y|5l==)~GYZz}IWl3-GZjlGHiK%R=C%6VyED;Pf14qrqzKDDs-&j$4w!xpS^dzF^ zOyh#Xg#%A+q1V^&Hr$}NgdNEB@KRw+*UWN_hKFOco0Wm{&0HgilFz?E@(gQi3CVOx zOSjrH2`mmo;xz01qn^uteH3;OQPlHfnfcm;9YVtIG0_QSfUgk7ig|yuM}`rJkP`SV z5@*}Y)MD7A9=4X-YkC1na&4fxi16jFs`N$`8mguf8I=%w2(6p`Z%`q=vO>k+I4Qeh zTpFp7{q+KGiXj_iez?dPu2B?J4l|-bK@(TcYfii&XOf?4-$senIn&+Z?J-8+t7bnp z7t)o=h-QaHP;+KKX)P~}@FEmNLV|n#bw12%=^{Gs#Yv z(ZqjnmV^9QrrC+4tFn_sso^Mt$;$9%RMg+dM=OCYo8xVtb@E4BeUID_?(1h`Z?|WS zPM-38+Y!Hu-Ao6Bsw7FU%#n(KLZ|;3%bWMsW>LkVxf{jBg0WQ3Ex~^H8wQiI~?V<7@(375nKh>}?MhFVc z?^j8*;GBsU1=goT!%M;$8|FNbIb*LB)P8`VoJ9VJ{#|giY&oy&=k(AYy7M&~med9Y zAE90|yFsec?fvFy@n@OZw(l1fHLeE!N0rX22icT8W7|LBGh%4#7 zy%%Myi83g4|M8LrM}Dh-WgFF(kW6HGox%cL0|;qLTv6rFDXpEANvJ^nssIO8-7d{> zAt-_g3Fn#(hC{)EDIfw?T4l5e4Bq?{{8I+pp%ek}q93Xuq|AiCy`HI+sqOlSf_LO_m#hSF_%Pn!5nn9E<$YT&=#N z#%{n~m>E@<>>y1*ver)kt@^}h2OxJF9bBc{gefIOJeT8%D#_&cuzA`vow5qNWMKAq zV`k)Tn(wqpHQQo&<|OW9o5Ea>fybgfp;#%f{vZ!N+LdjVPpC%0cJ6v*Fh^ z)yw9jsjh%&>FpPBll6wdrc22x-Fh3P7T$v5=8CG^YVI<7q0tLb%8QzzwPoc@@>MPA zSyhzWu^dew?ONuJd|x^;WW@nluh6JVRZf7MP_a7UGNNx}LVZFbdnT;(xeTG^O<%e( z0rDnRfM`Q6|I@)4SCX5W$*C}EU|LOLG17eZm!W2kby#K?M1e?D z1ofNMR6WOd782i%TdPNkb3_n7K>w}`+@l(>4@IUA-EMtmewt}%-9 z@Tt_7iI+E(B3P*>gwr=LeC<#dU(dy+@yhhwb79!+J?lRH4nm?XVJ89(vTW#SHx@~S ztSQxmPUO+4W!78;RCTbJd0hnw*?5X<1T|n5rb)#qecDE8L&3J-Co(LN(C8M_WYhF! zB%+dIG>>u?o_J+%v~?B_141q+1Hn> zvY7>hrsPyULc%tv&Pa13P_L`Ebp^TYb-(P&kgCt6mxpMIeWPYY)@J~(it0rK?Ge)C z(i^^?5NW(j{^;`4?xIpLOMGCAud?(_l0xdcpx8XtaN>$Nn^%ChM!b@b#Ql4|nXv}| zu2eb!!6vr6c#A6wx)vMPBLljBq!P4Dav8$aL@|~%=i<07>f~{v)))6-0;vIPOO>_&mDP`y8AOvUnSn$fRFI4Zt?uharU0;(>ZvuKoiO^sZ=hkcvN zB3athYoWNAX-3Z~D5UE>{Ksa{n1y6EUg#weB2Ryu1_5O8x-bj z7~~LEt%RK}j_C_AvOm|cHLW^+Z=Rob#Ya{Wuq`IrZ>rV<2Qb1Zi0lAIcUt( z{zH5(nX_|rE0jEZUd8n#!dT}nHDiH=0evt719AASPwZ!Vw~V&fo_HCPxOsPhU>`IW z{9TXR$Vz*j5&!YuAO{y);V&oU{NG2s&d3j=GSOl#90fQSD6CBndrj8FuYRidG?15b zvt>P3@%T~oRh)tFFn@Tg3ZNm#%cv`tKbP*|v&rU^lQOyMQv&c#$TQan6> zLWXK9S|W1szQAL_aR9-mg_$6JH*&y|$UZ90U}S_RRTGvGMJ?ISEbXnLT5;XoYu~lS z^0?{alN@PUVx{*mc{G6<@ADZ&^^i1{uY|=7d$O_>sF7t=O^BM0cm){YBs-;O#ukcS z8*at~7zRgncfDYQ?m8RGFuwg1$Ql${6*fP0Gm#2Cpi{sRyt^e8Eqj>Dwl-b;e9Df< zv|%u>Ce(`zGXkHKguq53kkG2RPAIKcSv|IH*EJ*a&@WnFADRPNJ4)UkOeW_j=Y$v3 zH$gXy0DV6u+sXTn$sGP*LTx5RS=wGL|7;x5$_^NEIh2BbG=!WU8-* zPyO691}#>SJIl$$PnBYBxL8uD4oWa1N}S(+hKLpu!LaWVovmVkh)lo2i6fKdYEIst zQYJYZFHEL3$zc5~+|r`UXo#2U<@f^)`TNu`%AyB~@&MTTsr4kbRiRLH0W}BNl%{T( zvX^FPgA!dtzWc=oG(Ls~`&TcMWv2KA-T8W(pqJa{ATVEjr_}0Gfmimj(pMizlW18y z{k^@fU^xbh-k@I6mbn!K>@PQKDqFj~V->wzp%Eh1tVqjb9sIMW+!qb{9Cu=&m>Mz{ z9R-66lpVUEhfpmSI6xq2#zls?@&roTj9Z##gL383KYSuwHQXLG7{c9a)VU+zxzK9AYpwkaYWSu8&?fw!mbV_1 z(BA9wy#VO(7Cq3f5!UaHzu1mmuUvGQ>x?4BD5hW{oIPk`ZZfi7<-znziD{a=nJ`%` z`nCSka`4vI^Z@DSdUEk>t&MeojT+$?(xm|7Ms(9fXb-_CC1b-;N|2M&Zc32wr}tWj zT2yCya+hv_?uaJq(Sww?AzFVV@$JO5T!mL(Z@~?k-)J|S4R+w{0{bUFeuG}iTmx5F z+Ix#Uu>fdB@HgA{yGh6SaIX?QdFrCIv1H)_Z|NnE?j7wJn@76p^%Bp!FT>Pshj>x^ zxwKs6hhE1sJQ$9Zz-Z8XlRwS%$4Sm>P68brsRmSa&}^Ed1hq+d{o-LO zS=yazCc5Dj*x99B`VG=cnThXj-z~b6fv=-ZX{x@|r>VpQdeE{649HEfoY{|E^P{Qc zCY9B4hr-(}vegbUD^p-JQE~*!&w-~1)t6!O+tcP=0{aW;4()@Kpw^>S>7C}byVj#c zyfTkB|4#&k7$2JUG4D1JGmlt;-zTbx63(Qt<=(vLBQC(Jq}u1AE=QF+D#h^sRj)>E z1;hQu!G$d#_tGE-mENe*X5zqhk=3X?cJ57~kfhfPHodfQ`#ZlPoHdKozLP$P5Z4Ba z`$=dweoBN$J^O8Ww2Q0szMvLixP$$*r5;z4bj6xIUf7j~*&W__aiQEaoo@1QfKuwzHH3%w%aHl)CSJscOT5tOoV$hdl>Y-c z`Up6WHr>cSO%ntTP1Va<9f^>wL?_Vybm$u>o~>kEtxh6aVTqfX@o2p}|3DmSd?Uow z&XsY9pku?R;#Pzg!H?i$HBZ*Y=hEDAffct}&UwCCs&(NWI%9MAhLSN!Qjx%F3^dD4Y57SvSSb9V!V;Di@!w+rsaY&fy>EUeUNoZ-EuJ${@RC| z*%JnG!>q!;lRadpWgq!jPA|;3T#BQpPCzNMYoDQMcf0fBWPXc8$hYu$eL*Krw^vp1 zF~g-P0cLGv5f;lD?$qZ7^*`QvsTQ6svn=mZB4u}tSE?J?h)e8Ir7}%TI|=B-u4HK1 zL{$gdPfnQ6l03$d3m2XER1RBBlqmf1S}22Dz=9K;g|096x!pSHN}t9YLxg|Cq!|A& ztQu2R+B{g4f#>yeGXvbr7q>w1XP&lH3?678I7MK-CoZ#TzyEz5I*1k+`Ea$Q&&9fPy`fyxX#~2 zHyvA|m4{~W)WKV5T&B&$g8J&=ejd~mcKlBv-lP0~s!W`TiXg&oUTtdF{(NqHXD5&h z2G*J}uWhd&xk7wV4|NGEXcX|5^ala_k z2gAgj*8NbYmJx?%H+#LpiVzQFUxvrbVJ2Z~DsQs#z^k@*wM)G`g2P4r>1?Kcd;>$c zz~h@tYwu>MjJ@a@pCWBHSop;beFlNy2H-QZ{yg;D-CD&sZP!~6(VTf?wo9dsXYR5? zQt2pKD^U?fVJ}oQKWR0Ja4EjRvQuu+V8?}OscLwWps)6E)t5oNby(;hcm4AO!M4w- zxU~LQy&OA$42Y<6c(S!b89E=SX)+I3c!Tdr;`}Mx5H*~@IPCsI{1`gpD^5{B^zUJUef8+GCx2=sk zvRl^jqBOY!x8(?zHRg4*0_?N8TQlt{@pk)E(uc@!7&~fCMhI`Rg&EkLQ{0>|lg0PW zQwcAeGHPE6P}&P@ zCy{2MX2w1)gzN7jTlwu*R^>2&I}Dvz%5ob%C={22)u4EUjB6kp5uuoCa^R<6v~Eo& z_KZo;s-2l#uRXh7mRJ=NgvdI5Oa3a-fuE?BESxN->do5=hoX*Org5&9#6BTchgJW&_=}q1lBMa(9Uo&VwL{A; z7ad>vVfU+hoAYZqJk_+SB(wZZF8110qq1WoC`2z{_-!*kuhu$8lG# zk8qZcYr1Fl_l)NI?5ThE;XoJ`>$*IRoA@`6iPu3L&KD^#;ky5Ki@V0JJe3&pSNt3I z5uXMQVSe3PpuoR>ySN*?%o*GT=7)_p%X4yxUMK201N%E~M9kd!2adhMop08klEwYG zXb;hg^sF(I;}4F(BC-B%uf9j4r683#B4IKtoYJHG+T{Td*S2qgbL6Lm31t3iOK=uAwZAYxQTJu_VUP9N6Rc z?NTM?WK|ka7T4EC={U??3~^208d(S~oEf0>a~EmEQS|;CWB%p);{BT4)|YED4QYk) z9EXmvpVD8kk$@fA70Tw1jFpl1fgruuTY(nl?Ok*de3d)+EeU76(AxA_UcRFl+w#DHt$J{dFs#xi( z?{iQb7dFv_^n&dI7gSc^V(1+YF_qkH3#hYIu*)B|?vL6tVp%35&qs-7XJ!TS-^;7k*!Pus#czY}jNx&JA87bxXF6?#U z+<04xTfUmGv+4-pWK9@>QwTGJ*4-$I2wd(^E0aHp>QvD}iL&1vwRZwbdhY`Z(7Jh= z^>aH)$m~+jD}S5XoQMz@yx-*q4m9LPyR)axL24%^)#@ZCwdXJ6!5^t&O#C(2v+SpM zRZp&hX9%uHl+%_tBlN#cRSh}ulyq*AxN}{*T{;UU@Ij!avWH5b=Lnnd9l(Ec zqws<;9-Dl}1VjmEPA>Y5OTd5qzJ=995D1R*ICtz1In_8{hupPh<2hMf73qdlellj9n93 zU^^@N+A`+gkey(?^@2NDH=F5>Um%d`A6eY638EtU?>@CSCP9hlF&Wc7+_c>aTTwF3 zhII7tCw zATF2A?=NXac^0@PlP3c|vN8tL2hbczRJGn0^2O^{(k-cr&YD0?k~1(6Q4_tefZkzh z9S|jCc^xSSggX$5s3(~rsA^H@QMaSj52OwHIU^?}&;=o)R`$1lDOxhWV7+h*DtKd_ zTOFSNbqTY3cdf@-Bvk(@-2GRPWMYp_g@7b>5VoUEA$DpN)FV8mya?J1DJEMZ{`xo+$V4Tmj$R>QrtrPCbclhaDI{F<9mTt0x=3oK3eQquh!^b7SGRr}hm z{8D%uda?j!CM%_fNBIf!npf#QQq?G0x7n8L=Q<&eKHN#scg`8l#(P$%$D<;qiDaH3 zYBxRX;2Q%w`07Rz=59mMI#?E{81Kv|O@L_`Jx;F%3h$tbQZ>0x*5q}rN7Ar#bXnx-u)rTt zNWI?354p65%_ATcQbN}Xns)I&{PyPHnMx+r`s7uW^Ma-XiBkAEd)DqeU7U|eUq)(C zd)$VPU#Y^o2q8!GIeYSEX>|Rk&I^;9Cp~6+Q`fw6x}J;npAs45g2Gc~m*0(L8!aIP zb+T^);r?a^q}RVip9Sh>3bQBIZ(i!U`@o#% zNgqXk1Jl1-=6@atAf4(Yzq|8H?}NVyPd-M>?Il-S!NAa+Z>C7@_0$2j^vO+REi1}3 zcU_8U=-XwJh}@l3X_VV#zjDeAx0jmgVbKvM&mX`9r~_s$L<%vga^d9mDN1U*L zcZCh|j4{NJ4F6Jxr+VOeH5tU^l{h85QCGu=$9Yl)!MEs8G_#U+#fJ;77!WJky))k^(lxC-057(0u zA?qx73aQb#PBi1`vBqpl<*^M0oV9}%KFS1E&#)M_+p?Fp#U@6;lP$WTqP;?&H=3K@ zb9|1$)cVAv5cx$H5KQRyN0(C)rK(bGo8guB&%Sll8C$>p)8_(8IQ@GLS_#6C{*0Fx z8X6+O&obTdcyp5MMh#tb*KY@y0oVi34&GzO;LVw9N=>PTlMAg$jkxkZYN-UDKG+7Y z-Pf@Oj}nEfa;f?Tl_~Ou^XcEZRV#YiXJG}r%|sT7Qjs?koG{pfEBmJRcf%W2@wNH7 z;FOqwOhlt~8RibShmaOO&@hd*`hK+p=fWr!sZs`3l`Uq~o6n4n15d7Rye5&aeP&E0 zTf*sxAsmRZH@Wv(w~v~GGiP*>67UnMmlo&5Vnpd-B~aWbPFfqL@+T_=8)|ya#f}-F z)vI_q3z^l_*W~kvJXi1QVkt6TYkqN~?|mhNnW{Qi6vfi3qm>`F=!I4uXiwi*sp%2n z@d5fp(E9ipI}6y!U5r=6_OnDC7sX4v2|p!A_!(qugLk=T)R{<2)F((9)y~CJ3$I-# zde|I7LZ6mJNIo^V0qBuDt^2EqkMnVm${CoKh#_jMScpT~1mnm9C$GYDN0TWN!C^rs z1E(nv492%+0Cr&Toyt`HTS44DS(6vFl&=0_gd_ofUSgJ{OsvyXsSstl1UesaxXS02MmV-;aAlEit> zpRwu8?D{4_m*9@YIGmNhpt8*{amdYWD{BDH7RaLRmYVB}vo`nOx<%qQa1R%O!Vpzk zr=|s^FF7n@PnNI~Ic0rzps$3=m=EQ7#h=#=XDn3O@}VAlZDY8$MCqc zTIMHGo)jIQ7{!eC`+I3WW5*-1?ONKqN_~_;tC#u>VkmJa&UjA>l1n>cO)EE0` zU2>gsf$`8R>7Qs$P?EuT(hnO`tAE4al*>e_xf*$haFu@7$oai|+VN)2eOE(38}2bb zbtujyTDpy6%LgcTNcs(8)vEtj1uNh~1n0C8X)2r?kN%0hu~8v9M-f_}VuVr~+g6a0 zXiTkvD}QosXZzS2(_H_hlv<`=`|B&)=cZf2gA&eYNw}IT;P$!G52?qj#Kpi!aXUBr z)R%C-Qd5ZG(z|aqqNX0`HLl@94Z-B`dSJYgGaZWd4DLJ53upJY z=pSV`MUHVw^<2WH^J_{#-vmI_AJ!~I)q!EfLS(cN1Ae!(=?N#WOZn)d5(-#~x`@`6 z;lE-edBcVYW}^Jjh6Z5)j2-TTyuCjb_u8*WO$s_fGdP>F9YR_)gCuL?Dg4HBHU?>2 z15_Fb!hK11=EMz?WTZcB9_`bf>oJtu<4(xCOULwwey7%@HtqZ-wP$DFG+*goi6Ue# zSD%Dqm_wy~|go4|Pm{AKdwIH{D`brbdz zUu%Jk30I!tMXny}%%EM4UJs8uv=_T^pV-HPaIXH*fI}_sq9t?5t;K>|=8N+Rb#UR zBG6eqr2;?><-FxVCb8C;M4`Z;BzMVf#PS|>+g{SPJVfnX$KugZl*{O894~I2e7-|* z&XL`KInF+BQV})&sfz%nNGiy?Nn%Vuc-bV%@mEP7zj{bT>%JIRYHajQDy|wYg0ZD_Sc6huvCR-00S#Z*H?5bbslEEq~JXEBzX+)t( zBB38KcW3!vSdeWt!;w9?dLSk*F5k^~<7r@~>VirBZxE^E+7}45RmGS`B z(9h(`BGDCLUSbWx@oUL@g-o2AXw}oE&E1hsse3-y12X$^Q@o~U?ke@Lpa9(6=o zA+S{Yj3D-nf&<33IL*XL_n0GO9~LPrZ3B&BNXQ8Rj9hLbO}nptOAAeunTyinU06{m zmy@Q1x+zaCB7

B{|Q_b~vq8TsdT1PC6S&_(7Ca$%%3yX+_Y(<`5D~5K@&Ti>)$W zLBk;LHX8rp#{1_Z@|9beovCBJMRl>44|&-zi-xjWOe88XAF!{e50E%?i~_P({5lTH z#MS0B)srV(ga|!q=aS;fN)}Yyzn5;Khyr8>5^O#~)e^zNf{Fz@RP4G)DNu#2 z2bRjXcwghq?hIO@FKZPdJ|@Vnpk-!lYYIa@Vm&6FLZ5Rm&V0L=*#0L0d*?ao_;!zy zX!QAG0t$3H(08`z&V2RbG4azQl^Lg#BcSUP>M;T0o%{h!700e%K>LP0gG2hs)pmbNQKmfvzy`*OGVI9Xp#0 z=Gl=G=b1x|ysuW+jsZ_3JXe=>5`q_fvyCff~xJ~Z(Vb#Pzw(O(K(BerY~`$KTVUCzke6CxjVw9%~y#bp_T2vJL%Rn%3a zXs#7~{Uj={jg}zayoxpLX350LDkaPlCa>n(p@uf=;ObIH-XL}na=>zzzS>VGFHRnb zOe|H`Q4`5_>kE&kcGNkUl?*qg@;l8JN|&Wd)9|n8@%u)kl9M(Dk9ldtaM5?h+zPjx zn1c#^OuKV_5ujJbB~tBbp65j-=!_XiQG^nt9DgGx10nnE>%>PIWedrqoK&HFXwk?G z(6>&>#}5?WHIlwlhK*Flt{#1+2Qj6Hn6?Xdwq&4L3TpLVbe`s;TB|pdlQAyCD#d10 zTFLp$EF<$3BgV+a=v|=BCk-BoryU9AMc4WQ%cO@Me1wOi3bX%>cQuGNEil&!|*%E%mjG!GRcga=oY zh|5GMOV9~c6e%t)KB!pK#N^)G`nx2%R7#VS8;sYHkinV*cqCkVDSaHnBWM_UzayVC z^L*-G3cd2g=6zeIl~QAPD16In_djR;0iZV$lj6`00+98LyHwq3G>7!u5)!7%ZH|ga z1hFV5agm7mkEq3ZT&~jIU8UByVMTnj69%Q~hGJ#R%HT{6iWu3TsntPZ-sqnUdT0uD z!^ENQN}Iaq=iB7==8jX}6wt_&F>jWN3Zj{JwK*GLiSHooM~V}^%+`6*01hXuRT0(i zQebOe`Ajdv!si^4n*s!fP9oD?aB;BQ8YyC|D$HP8=!`^|9fYIX#zr*Wq6p|NPUA8( zn4r)HzdK5fONis&3;he|@tx#u)BD^=aA9kx@_&Rr7aJWL^!nBpT|s`2KKGReBNd2> z$_4?bSVEn1n7U=@`}nCqeD40g5QW7D1QNP6(metx0gC3aBg3lqL7sc*!l0ucvzYz@ z-=U2`Y{n+>``k*6$fh1)Nr$rfe}E?-^=mthTl_Ypm>UbuX!WAUMI z|3&}Yxq-)Z+0_SwnL)Zhs=*6}h6d__4UpG(WjY3L$atJ^eT$9w2Byx3@*=fdy1;x( zg75<4VxcYtFM$({`a)68ER>-0#v(`870w^9Q8OyW`Mq1=@L=of5#slnp08&o#{}gI zvS7;_<86(C)L=OU=2nnyIt{J?xBtqXm%3H?Ub`0dtsSDcc94p$vUr=%B4mB$T20pV z+nfcSYKmoZiLCPy0rlZ^)uQC;6KW9EG9;`nzv4-PZwXbjqq76r3dDUb`_^j>TMeSN z_*u46DKn;kTj8@T??40b(x|805ZP5tYkO97MWwiKgPPj|m~ClO64`OdQB#Jr_r;aS zhIP~6Os5yKd%i5hifEleBv=0Jgbgcp5?-degq(V|?|gshJ&!P>WizD~_lfO38=rPq zrVKdhQaXpzCi$7voMKK&I50Izp6_DhSBh%2X;+j;{I)6MNZqo{!LodkNJ|l8+H!lqNqPFEpyNRap#QDDw64)P_l zwfQpkm-NBuO;i{uPOQW{s~p1kQ2KMUBrXQ=IHu4xqR%qbBfDSQRJPf_F-sYDR~D7E zi-OyoRtdYq&IGn{*h9Tlb?EVswpusqPUg)>a3wkGDL^x(MFRP<-F09wc``)N`!Ne8 zxqLAT_g%}9ogILZnHGx^*{7?%VV)4_8cht4Vhab)JSWeK#>$fMW*MDOmq8A==&x~{ zHFQL6)99^wp4chuCQIvP6!m1gK#4SRFkCWL;18mmDg+a?O}xg>ueip`0{0#cm~+{Z z{`Xw*b<-N5q;RHEik~O%_o=TGQFd>r<+t9ctaO;#We;j!t<-+baSJbku`=VKkXrZY zBO42m$U?mpvpRVZ1}k00oVvSfv?@A#!1a&JS7i_hch|Ugb!7^e%=X8bPz6q7;Y}V# zGLkkZ>Y7LuVSfA`%nm(;HRNzsew4ZQSA{}J+F^8% z!P_vyC>O%%Ka~c&r_laZvJ?D)DNJZ9uB-F1DT_FyR;A`Q=x+0ULx}&ViSkTZpQ2wqP>MD=ifo7) ztP0mwikDIz+z1Kzx77u%IXo%wsqia6#eeD@|9Yrb-D7)s&;uHfRCW5Nnj0_?FH&aefJ}ijq4fx=a24alPO=>iy}XMaPl)I`zcw@*v&jnE zNvq6m&>b6D5{v{h?&RQdgwxO@lvE+vyZ@getnY~#Ve2EW?)9wz`shag(WeOipOsj7 z8&hCx-X5gnl+%0w_#)xc1aKP(?e)UnNZ3%bP(b2Rk5XLN%Lh^4@;a>NYZ_Az(1I=oA0hYFRv;~Dv!3X}X;;+_4{ zXE&)s-+3_sH{r%3`;b>}#3S@%{vFi%W67^gue5p~qBm}hVQ2Pl5Z-3PM~st&EFA5|tK$ymY!c2(q4U62~hqB~-yFZ~}3 zN0aiFItzH*WM!9Z<89@ibEiwa>R)7>TyL(wA1URJ7aKw9@=v=nuUF&@iTy2+koB7c z!wr?x#cKj~K{Oq~^$N8+ng(CR-VMX&z88-7fK z5dLRL+(K$Y-Srl+)m~NkZ4+4~l2sA`Qz;;liM~M-q0ZS4vxgfBA6H?!Ty)(pyN{5h zMnDjtR;hJFLhY>yBn^LB_qQkBf)B^xR6lwD7ft@V&V31)Rb>w#Y{--+OB2wrF%%UG zBsali3!{K%IWEPUr$cFstyajZ34?Z7B9XmvC1=b0~ z1U29oJG8ncUPbEUbG(y%Z8|UeQS&1*oMECm0c7la6zMmpR-rE>38QU{0(FtdZ;&@hIAxuP66Ce^ zOBMd|s^W^mfUum`V2kI%iYy*DIsh$@9;g{wXf zqK6iPW_K*$zmp!FI+ss&y2l4{F`oEvcB*co*DMPzDg0E4uOvZBGBAGEWl>R!KvpNh z*T5#xCQUzlcO|r6bmYlmn8H&v0UYnZb{KM^5N0OFQidRF3yyv)uVsT3tez!!@fVB_ z9gGH1D1v@n*pDCOw?Uan%?3xk9(~J2GzG=_6Wo+ywnjLpvXUTw7L=emLlvXn;{<1X zCa{*n4Oo9B^lu|>4UVX3^7PB6rh3OPC?fMPn6ptFU=hdlQKw#>MhrUt2DuVqV6;R9 zj0~tI$S!8HTiI95amE;QsknUm4*JmnpQ)&2Gdc~9*UOo~wrH&>raVHEZK#Wwgrx&4 z(iu#@@^Po4WA)DhFvU-q7VHzBMz+Lg@HB!X=mpK&Bt7jhPKA11rEBsxVYJnUhmPv#h^YU0Fvo5l3yKLzknp-dFkFV1kcqV7ApfqDOAz#yo zF8?t!1+?&_OX-D|`+qkYJk|aNljkG*`tpl#z*5ObmnH*p&mRZ{dz!@JUO~XuGU1$) zu$QuWQ%j@e*BK3_|24z@rYyhY6S;m1=~U&bc}(##{)-fHJ$hyu%Kc|5vi84}r6X#F zCBE~X$b1%zC*spm%pLy9^It?r9Ee19#fTh;%cw;Er3hs8Wg=4r$$51DbTjRiCT z%SwEfe-V21CT{R`Cf*!s>t}dRo2;gsL?Hh{{q~_HOtq=Nw(XzNE_NtZKLtehcw7(Ooi>;+m(& z{1z~`6EZc+_(L;*6=*+d`M%3@qsZrub$ZZ0;RIUHob|A2+*M*B_38Eh6Va2+D(}1P z+?iX90jUiQo!48YbfO#7BZ-5m9exmh$%@b2TOwVG-{ikvqt%UL?qV}a4^h3vU_+HN z=|tu~ie*(DScniKi&pOLDN|JYA%mHH4ft!g{@Q*Ecbyp6a;wulmBaBThw9~@oPrT8 zhWeeJd?OP!7Wf^5Ag%`2OGc`>&pJuo9GLTP?d%Y zK)|b;G3vi=scY|D>w93G3H*oihbU)2qM3L(I9~fx9fLlls%OVkyQvttiZ|)<-_XKFq%zeRyZ#I|6s+pKd}oRrZQJ-&)WYS*%}2 zLHY)6>beiP`i|&NFYiD95<-;s{u9vIDEc9wv+EuZpStX_F49pJ&Pp*oQ*i;=k;JLsXZN`Wm()tT3y3Tt;;03D)jOv3raEv9GLCHi^&0 zH)wJHIi(!_iZc6CZ;kCsh@AYOm6QNe;(_GJOxU z@ZydVw34qeO=nZY4b&-+C#XirEOZ`9jv{N_s&MtL--sL1#lK--^lM*z6?AvjKN_AT zB;#~?s%ooTh5QBE(6!=1JRPDtR3Jo-HvjEy&)MexN8DRLRn>L<;s*o~6r`1w?(Xh5 zbf?l?N(%_0BHi8H-CZKx-5}B-sH9TQ-RBTK?{nY(_kHiZ(sCt#K3UjGF6xC98qG2%9%cEb#Qr#jX zanYt0Wu-3Yz#@8mBBo8|Bd>?Rj4XubaOCf$I9JzbF{{|Evy${qr08_VsodyF_&#zf zKy)SW0f{A~tYdWXkB#LERPuhY=R`h2Cn^$%po>?P;Y(hO_M%a-xMh|^5DiYR*+T#K z4$*s>EaBQ7&N&lB;{=3r^R~&I&N_j_F=NgMbvU1r62^C{IkHXtw)u`BY!quVlG+;G z8CI5he*2`|knT7|Br&(iTW@}m6C z{E>H@p}ei?^OO2j!!cPfHLpLXMYwgiPf$0xlQOjpU+-H@`05vG!&3a&e4dn;+<}kS z>CUvWsv_SG@rW!6COaCQM4cunt_8}FEr#u!=ZH7K)csjwH&@6b>|!s&%4c?^G*EA^ zZe`Wm!mt{KM^Pm2A)m0axBtBP#uVNi!}y5mcH~u_xs5u*4mscdqX=9@rv)7t&KC0d zk6ZuB^R}_YbgOwg?&<%t6bSV>(`U1o_H%ta+H9R)UB@+`;6>;U<0C;t9! zZmllXf+jH=0YIF7N>jfn#-D6x7Pr4*7Heo^7SM=$>2>mHJ`-#vul4BrK5KhmXkdA; zr_N{iw2t8{Gd~@_ZWo*ftpwsJGzW6?BqYZiUGedaMthWyq?>oSnBc{7gWA4?o@_YP}!Z^=Gixo|v=t7pJU`8ZP?h-dkIzbD>hC|&SV z1Sy3LSB8WqDbB8D5SSe=kIwOUqo~#^9RMc)+zv1Lo1A>^hwO4b`LL$;Ir0$t&x3CD zWv~58yVrt6>atINXi04ZZ?j4j22kf3=;unFL4^*20?Cl^K1BLRSTLD<4Cjyr|=O^R+2 z;Nck9bnkz565EuOUVsL<0u1gN!Q^0o45z^Bjt(FJ2SVQg_V)Ax1U3l4!zv~WdGeNV zJfyunKHXHfIrLpTrT-3%i@^VG)JVF!@c60duFU7PXTvwY#DHI0xbCg|xf9!NoJXdo zRX2ya)RG<*(OJX-o(#`M(#mbU(LjM|ohY4yitSy5nIHhfNbo-1%>|17!1YUEAS@Wl z+YBGq2~IyCI4;o^;=DV~(G{PF0ZG07WRe7G{W;BvqTYm5@Kt%GvC7BJ2LOp;?_Guo z_m*S%s1zz?OT@jWXAxPXd~{_%G$`=arW80WVg$j6b;}iGrKnD%F*9fF6=!k3CG*le zqmEN?@W}V9<+)U+OVop8mVNTq8*iCngp#k6 z=L_Ofm1F}J9BD1Idb9fLT%|}Y8Q5j+qwdot82qN82QY*Iy?4XAj9_o=cp^0(6~%eA zS$n!f5&)Ff%JM^?p*dP~!4z3xGeN=0(eJXwCvb|J{OnA#vB`|->QEs9=;#@ zG+2?X*#JvqLG4`1Rx13$E#Q!ao<#5ste$czU_oGtGr~ISU$={Qk|9r?zq$~61+^bc z1NMVKX6QD*py4DYp1UC5#su79hgcSF7{ESY3$THjHh^OyVA=qViO>VlsvpF((fHBT ziLwH8eT1X{?%}DPl-O8Vm(W(2@O`XuCvi}Lyq_&jA992w>7AsDadUTdtAtl}BE4VL zZt!08I3C`)wWglWbYYb*6)xJV;3 zyk(a!y~fEqUo~B>ntd{#tHX?-`kN8K?1NCtuZ7H?u7DURzyc}rW0jT1QtN!Z`H;&c zL|wa-mA`Sg%OxaWkdz&{GhlW^Ln2|JM! ztJaVW)7jw1!r_PfABu%V16G&ccTqF}U>?XGilD3yc!Kr%b^ijG+MLElEq}cJHcj=b z$zgI9eNM^vuW(vF5SO-A+(P6Qsp*Cv;CzL1J$1x6b>w1gP}WL8?PZ8jcp#eUA|d5M z4W~0tSgmS7J%q8qL2zQR^mv5Zig~}G>-(U+uaZF$@S51yq_GZ!yATf(Di;ExLo3e# zo(+^@D@=t9?DbpiCIHRS`q?GM;MsujABursq`dp0y`BXH!vj#?|7)$_QyU0lI|7bd zP!PW~1fH3po}a%(htME-hwrfmeG4Uu;`@qG%=kFXV*lbKRVCZ}%V^@#7(2k$b|7qx zc&BG1r;i+8(|#@7BI$K$56KsfFc)}-S8rb^_l%1bQPuRtbM+5|e9mUZ-YjqASjn66 z|LG94`WD-PK}}5IA??Pe6H>gT$p0rvh*5cWpI3P&++-*=*l8a%C3%CzfL}$7ym8zd(6_4jdht;YQ{Zr!JJ|h#}D__ zaP#W=)yM6p%t-|Mo+0T8mz(IBA258>055)5{UmJG+Vn{&VcGkwT5uem{KjjH5hZyN~!wlK*{^jlwf`f{TP3du z%>{X8jpT#^d2hKU1@L4^S9w+1v@fDwWV^4z`1Jk&Ta$vSO;zjic9DujFku%@#0PDe zjh_!}jox1#1p|QV;);5Th(_a{3S+)n|3PJPT@`X{8LTIn*dhjYYk0;wn)Nf1CR7;t0FE+T9oM3Z zsHkhEdP;nDXXtvoQsJ=(9Nz`XJf; zT`x86%vU*=8Or&Nz)@APv|IUMK5x05{mSVhHd7t|S@8jYbV{YINT(A%uk%P*&m`7K zV&3aOv0_dM!(jeCHd)#PG1nk@Pj*UGb}>`3xu4(BM3YEeg+&~aBs*=&Y~V%a7uJF^ zadaM1w(N+)-UxBrXq7O{@3c4pZ)#QSN)Gs(=UHa45XuMB_pr>?>K8^Hl)>fBtc;ul zR351An-g|p`$9ZQa~*_8+R#&t0!1Nyrz-Vn?)ch-qw1d0$%7!Mdnx^6hTvhyl8)`If0z!Ew^cDI4C$9(*0!+Ii-A2XFK0QHjI?AUgATai; zaip>P3)F`ZcOp<8kh>iMsiffFO+(GCtWN z3v}27FmhiBA_yTbQmI29?7~LQ!O=H~F5Q(q5#cVA zJ0MA&_Rx#KO();EA-%)+?88vZ&Wn;fI~;+u`(+67cRzS;3wYtS5&ZeFZJRx}` zK#hQRKIFb4?(I#YBK3%lFjmboNAGOS(J$bX_IM`&RewZLm+$fy+E-qPZJ3UDt!^$W zNoO!4wwK#q0vxtLNidSaji7B6vXHBJo?^Qx>raUt#p;k1dGG7bDtLYKP5DM1>QoC`U+uX>0aj z#HXcCztj!#>vX!=082W1MFM0XOl zGqAAqyTZ-E@B)6CrHA@B4LJLAldt<)!W>x_s|$Wcdk}95QC{Z=y7R}pZ|mb;Q?1KV znnm#F)W)*OL{G|f+tU-s7DRLmbIZ3urdwu>R%)v>XTl@H&Qpb5YDc~)v77riO-ew9 z&VPMYG!T`dsWApv`oYm6Ft}4+H;bKY30Z~<N!1A^vy~tO$2X#d^5FAhhZQPyKLJrkR$ZN1hz7kio~Sqz(!eF_rAf>gY}EnH10R;A^a3NPbJ|3D@fH>z%! zoSM#%cTgIPhAqbmLv*Lxa4#I$q~4qW63F8&u&@OE4i7JukkEJrq%(?#o4ZQ{C4>)T zLItRzg1Zb08o+4mppOTjTmPG2#%r{~3%Jj>>76$v(zK_IEvo@wIS-42aI@3*@9Qf&3CXA{O4z}eaxr0-1zM>x&dwbr zaO(;Czyp9yhJ!+SP*qo*{3``ChN!LXsj01DYUSHDb8M^jp5=dad1%VD66&ACV+cCS`Zea z``b>Zk4^ALhQi!;$mnCP0MR2*`SfWXR-zCk}dennq zHmlXASViYYl+XLXXF-}A27<1os^P01qwBeHM`vfA=jP8yJBc$uhc8X=R9=NYYaPr# z%Ip2gwb-F2IE1Z?Dyx2_+&`mW6vJ6ogX1O}GU?fN{bb|@ zJ*b4yQp937M9>&b=9T8pS_$7y;l7$LbqcxKezQj0Msd|&lUaN0$p=7sKfWu0>IF(X zXP685GWqN9s*FF@M)Kv(g)6`VL_n}FzcpI&zSUSA24D$ZR1>ro{@lDYcmP6dt=P-k zyVMI6+ipmIOq!Lz?UjZm?evv+9(G|Ep1 z1gXRMau*-y$aBJe=fv9MzgJmoEwbCZw9_}WZ|Cj>_m*wQ zAb&P#7rmxSEcTFq=xy-i%s`Fkk$yNrnB}Y zrTo~aFn?AH3_9YE!BX~6Z}7UGA7U-grM*iXpjG!~ZO2cpih*8ulYuCf`khm2k8Q8# z;;N6 zX#{G7u2@!i1!}N6bnYSV`Jef8Ru7OS+J7ZcU$keb^X9XfwT^xVjUo9$UHrOP){s9uneHx*ThLG3)hP` z&nsV7w^V-W^i4y{&8Xj|o#KTOdYENh-@!j9v7H`Ri^`UO*S67pxU@Q9?Gz z_U?gkZ?~g+Na^-M>X$HI49KM=qd+ZEyvPrvGl`JY|GgCF*V*ZB>KV#2*Lr3oA zSRT)29rjXJJ^=hr(ZE33=@%VhVvT+k*8k$+c_VH7=TgtV6OSINV^=dfEN9!V7t`2P z<U1>n)y=CZIH*r(QSj8`{AhyG2Erb7gFx9_h z*KTFe@j!B>hq3e)D#7U2?%+zs|NqF4fJhHklGO-iN8IME8fTGK)sm4KYkH?>P}>Ya zV)0_xkg7h`a>}Zv$ybh;eTzWyrZ(}sw9al^zOCG|N7AVC%TZB&%RSh^rD{&gF-= z>_SY=)ezvlB`xoFFK*@LKQ^CRKc}XgJ0o?UKGmb~&XbkW(|y-Z`pc*XGaAF-No^X^ zFJo1;c%=JIf?TCgjG{ot3wgvC#B2=Ax=xn_jU9D<+MVpBnzFE3zyU8QWkhW7BtOZCMR-0u z91TAQ=fh{;AIqu@P}iFVMFcF7VrF^lj1DsPJ4qkJ^`$NGVql!kOX9cCPozdsOEb$+ zolj=7{A%wa?2=%vNNv&9V7xNf+yeQ`vb|)tlnS^CDODy7w@%Q~d2*o2*UsZGp|kKj zS0LAH8*xrqX{1M6=F{sJ59dQ@*!pa;BHY)XJnfHQ%B9LqVoOmj)aI6_{Sm1^`M6y? z*(=mwT_d{jtpe7rpqQEE29C&-cvCi!pO^yy1Z`xRPT;G$`dhHm*m#k;<=CUkL&phf z#2@aO22jgk={4T{4KN2V9}wlQ;!WNV$gDh$6BPUcHN%TObqD+h+5jKlhzJH*4Kjp)hXy9#~ z|IOPH5%=-$%978RPA7D?lO1%3{i#Mb&@Zr^$N%f%b9?2V zM=U?$x+^R5hyDJkrd1z(IibjV(8~E*U1V^ebU))-g5sfo#Hm4|OaW)e11?*@MdVK< zh{qt*YIfrz5A`>M5rTlTLal(ywe<19pDqpYs}mfci7rrbKIzrREf7s1X6hJe8n`4a=1I|!LON`3UlVomhQql66- z2>@yR?eU!hicIBdihd3E3G6yvH~qcI+;waH1B1LxS*Is1J%1nlD@IlB?&)ya^S8#d zih;+o)^P4jd$pNkvtbF*@{h1+i>Z~hjfZKD$_GlviJuMQTDRhuE zj8#yAjSGXch>RwNxuT4GVH}9=rL1N3| zbrKSO7Dg!qq~d_W{LQEY$4u#%l?db61!GHA(%i^A837ULbcK2{-MOi7>^{Hd zXH<_S2x40Bn#xHksrV79spWN6wEE_?jZN!VRec|Yx>BQEj5o)X>?|>!#={N6TOWy0 z72)V*RJltc>UpoT2=jy@k`2HWBFY;U%+pi4Qpg3CkiP%fEj@WDidUQ;`BFPEloo5A zk*+o?gFz1w$7f#gEBJ%Xn^1CzVa1K`7@37!DP`3Rf%Oi|uhjD)(?u=Y>adw#?WS;9 zuksLh-n}5P^gOjl2N3;DvOUX@9qn7hsRiFuMYI0O0bpM4pUv4OYmm%TIf};~g{?mA z8lw0e#loK`Y(D+d&NwyAi=?c8ywSMy3cJ#Z{1<74x?Y_5r^vWPGJ45KzMAUEcniZC zd(_#~ckMqPy|Uc?3lzR@%@OFK%(@@>o#Vq#x^^ppmXGhGUsqu945?*X=s84F!-;v~ zr;(^9M~(#2NrugRFsEg3!OxQwQ8rs-VSoME(--Dk_vb2j(*i>GtW^MtxF1|(mSwk< z$%Wpp54RV7OU|iosc859^GEo)V;SuP4f?E>k)>C0K~{%xR92KmeB668=qo)wom-4P zAOKqyTYI8C$%4oDNQb1|tEQdhL&c7O6QXO?mod7~&AZU%fAyz)z}VGS*sHIgipzKO zp+DU+f0_eY4@B#?fs;gmk|ne<=iU#`yQ z*$!}4J$hX5l=p{^6EYdGe~2VkAA~jAm^K5zcQ2x6OIV z)D=N9sPE?&m_fFU=6*iB$tw83A5_WjwRhL{yTY_g|93Y!PFaT#=*0zECE{89dHB=` zx?MN%z{FLF47oTG(tNRbO-9KdCGfb@2F}NO7%#D zf@1oAH7ot9a{KE0$QWES^}x@sfkZyslVI~OKGj?7`_fNfrlWz;Z@nJ<$NDlR*Ar%^ z{iS(@G409s*MVCwCcx}5UeET28T8q=*8{I!3%Ln@zC>~c($a0aebq8Bb9&b^hgA%@ z3FCR{*W!h(Q3j-vyq?CC4cE5yyxi%naSA0$$v}c}onfvyFml$c9akTVdh~`a^Dy+q zkVzx0nt0g%%V_^C=wt*D8}k*V6}g&a1nGz+3d`Ye-MGIESD{F(+jW00z4l_SDe5!I z(=lkq%+Ho9T`+lIb!hnc^b zqky61=dc5RdWTR(a69rv`|$t6N*LdUpW;_zo?YS^_mOS=jB_E_OcH_2krQN&M4H7{ z@poTUI?#s`7xd;7Dibeb(QrfwrZN+G+g)9xn_j1!D?K{mo|V zzi*a=q8;FomMI%AmP|Esln!H|&5Zq1 z3TWo-^3jHj!gVuE&hIGn!wkyZE|LNnh3m2UL-BuKo4J3+O79OP-^}M65?gN+cyv|L zaq4i0n9OI)x-Q8Chn85^WEFo8GCJ6{ChiSpX>0VFCnrx|pR_s2sduVP9QMiaC}qpo zfp7Tr#aB+L$&ji3U#&kM9cN*CUWo`4FS5}4K5B*`gmPR^1VnxX_6Gpc@a_r?Z)hAM zzFu=sM9SMoAlzP{HSGcUM@a7f=Z|=dP1CKWoru`24;D4ItERWK1w6< zxO#SbLBa{WCv-j7(6a(mu!0;GpckaBkeo1LkD!AM^FMz8aAF84sOEv37<}gKs*53m zOs#|ypE4zU@A_rufR)~F`~c0}%1uniST_s>O9x9`Wtf_6TOyjildQM0u4lqj^3c2a z<}kpfJ|b~`8t%0J?MW+BAXM}R1U*Tpp!f9lJ9twHD&#@W4S`uX zq_)p7i$HucQ^Yv9r=;FxK}0U`{ru=oP`4aM?tS~nX-qA8K9ecz=c2q-K^pZz(mN}* zJwU>X3LLDyUe76!Cq%P|tz3aoCwzGzkI)w)>c4-hQ!mUWK+^ufcb@P&A?j?dYvwwb zS^@aR4`4XTOinWKI*2}Cd4J=87_x<~&!#{W1Ozs!*xnCtWF_2%Xwdqe0ctZqf7w)t z2-|S$$Z&g<1!~=-2XwuQVk|lsgY_EZ0TBDI@VZy*{{jh>y+TU0s+dmhm=tN1c4(fY zmT!8$ubY*nQJ=?4|AzCoq1}71YljAY?@6+B_e7u#y(-!WN@1O5FVkaewfo<2SrMYv z6!%^*x1SEFP&^0!cg`hFyi?b*Bx3iXII(hG&gK9qyWXj=0l4ik(~SbPzfO8fSM?c#J04gVyM0H6CH&>;n@>0?)EFY+h9dMj zZ76gVcK9MyoRQ$67@ymwTNeaeP<$m4VsY6+EAn<>ZmI8p_}kw`Qwu-SfX1J0V&0~a z1g7iWy4Q7Zi|ErgWw_T7FDR0q1)9|b&`4uQVD)^Gp-MHXziER0d!?HAq##^$zk+tD zxvFv9JUS7jo@)nFpK7))xJx%7$Zu!`&6;M}zS&fHJX$JW^f69){i7z|c=kn4;(Q$C z!l0@i_&1=8Sj1k2YJ#X$p0luVi{}GiOfWjz+7@_*RM(nKmUM zYLehiUo}JW>eD-QtH`M zaa1SxBT5hKYo$*=xB;qsV}5=v$;X1{rV`3VThmaIH;| zXxUv4wo&6P91M&Sw{&_$Fp>+gG`L(JQ$3)nWTiXLNjqIVeEfhuKSNM1ngT8>f=paF z-j!O3%HLv~M9G*1qX^KUZ9#NsZBJj_nS5#sG&u_9tb}y~!6vmY3^tb>_?_sosZ?^_ z@5DwN@`rr&jsv$5WLA!1Ddl&EET~e~1j~VKCXE`3RIzDTw!I2L9O4GH`SdlB+Ed2f z$!G?3v8djy}d^bNqu#3tgJoqpMj0nbE!MBC>^V4f&?28!c!S~|5{ z;x#427?V(-9H6jVe)RNO+i2e*Ci9~os`_2`)vUuQDGA%l;OenW>ct(Duaj%4Lic~V zS7qWB=@!T!gf#=V39m_2JZI97AgkSes5W)q;!N=gb^%R_NhgR z>P+*9iIfYIqpn3i!(1Px8Vj6b$g2u+<%CnKG2g1PGRVj-3d?wvD%|K0YKF+S)lgX8 zCL|sY(G%Zkr2%8~wR#y)W?w75#l%`4H5>!V><~?-z;_8d+bDCSK|b39;YAa>-t+y! zRN*1>5*qoOc@=9gFTCW-l-|1y=5kYKRgCANCUOQ(hxMtXB$Pi0Hj6roMB$Qa^GK@< zs-`LUj9hWh>y4+Z<2d-4wkIeuONdtJq59G??ARzVw`wX@REHJsFy=%|vNE_69o^JB z`P7K2$%uM3m7W!IeJVMiT|Ye|&T=cJvR!{nnslhN}Q_{_C(|X z1BGy`({M^WEa`n+*f3D?aw}Z)ywvL6*w@u4EB{knyGLw&A?IP)+)>kE2WZwNI0lk% zYW-|AXlem+8FFi%2f1S7?ZBN~V0v6>I^*Q-GFUhm%lQS6wl+Q`P2@^NT(FtzNbhVgPuSxhz+^Gw<#P^=4YD@4XzNeXIbJS)p8&#f?QsPobTdeP{^iFYn zp*FMpjO35m;{VI?G5k#eYrwGDS` zdBR_gzTLn=+FxtdWLG`OE8WziYMi>Ks1hsXy zQYNmghQCM5GWBsWIK^ylvle3=_hRS5M)4E)6SA!u@x0hLG68dhlgz6x!dzng@#8*n zPTlr3UPAu^3r`9*kcn+iEhj7&|7LiqZ-$4NSHdLF(5??Gq#x8zlpbP4fZ;sl;xV(> z-=_QwgO6ajy;?Ww2-lIF>&Uiu7}F=5h&rosvZ%-JJ1K3TIk$lZ?D09<{L6`zw9JOr z{$ZbcTr+fK!ZPJm^S<|n!8^iOO*;t8nL8d&=>~KQrC0T7hf`-a$kJ0Q(elzYTaLTJ zegsBNHkYJgK-~?oYm%Dgc}&>XGVgqerf);%`@(B5svw2APgoYJ4prX$+E1K0TvKaP z78Iha4hw%)i^5aFvDd5m+F;1CId*|3=|6kG0Uiuy@;L0%1buTbAYfa5>x7ANQ98JU z6;|m$d!Co(vhHd710?_w)Vp*T@Syz~isd_3TF|we2>HD8Grdj`4Gn*ShXE3iKW^dQ zM37(pIUqxi55x=~`#E3(`RfFJohA_w8!vQLSQ9N@l|M=%;F-?P9cvqlvN9+P(KbQe zy4vbgDV;@K$xoU2v17E<1>fD6NF6J2Dm>aeiaaW0o|ZDg8xQ%1Wbq*FQ3tCyLSBIh zT?Qpgg^GMo(jwx=Nj;MV{wj}qafeU~|gb>K1pyz%}s)m3wVcNhDuybpBzc@HZgMkR= zkeuASiHv$`%a_8hE<=N%ZmZ8XYAV>ZoU(b*V*$gt2`6b=%z?lrF?~%ig)7_|;kPG- z%+1?O7wDQ++)85VV8`EEEcE1}5z*Kx#~RQD}J6p!=&p;~7@5>+>wFB5d29MMI}svOLTW;|O5=Xr-$ zvR^8ekejL`5@&4ns;7^2Ljx~>rD2U=eeqeWP%hvot}Mv`QwP(1dyNd6450pk@n`NY zBM{tPvmrSH&f;WHe`3em>k(=o_zN?9(90GQnT_<;13(iAy?qQ2yoCz{Z)vYci=gjX zD#v0S{VjNFJ>gZ$0wi%FS&Y!IFpWIglCAQ#%7~7wqE&6d{x*7##GyP9G*}z16@KJ! z$B~0!$GE+gt=goq(0n$JO=nH)Ys`;_{FLYWu5NKB?|dKDEt(F+U+lWb3a7D|frEOD zFV|`m_h-pfcFEo|#z{qUY_3UzEkl>biX_WCFulhY_yLbqrVv`P2!p^1@*--H3;%o% z@)cI5hXp$f{&^|w8~T%9f()WuB^rnqF8Os2BruZs*Eanblez%XbrwEfUoIZMa)?VU zV3*+2AkAx8D=XOa&dMefWkO~Lg4dcW;>JlcXQaqdeWo5o>5~5To_<>OB3o~wr*wjh z$O+S(uj0pZn^-?)aBxuWEJ;&T(i0Jqr8jFuO&U+O{*#H;{ z*w*Up244j`N&AFsxws0tmU+FB)#%G{GMrzcwnk$Og_x}|X3?>mk7rbUlZ8?~JyF3W z_s+_tKc(e$bN|>HU@rK;0OBg6@Y_@YfJnGIF}~X`7lpdM8-y2sCY3cCC}EHP;tIHy zHwZJllLz3OvqG%zy}B22dyNYZaWRkJE<TS`cs-p?V}}X z(nQb##16{@jXiX`t#N2T1<9Nx8Pf<;pg{_uQ(^@97cTfc#ja5|!WSvS4i1CL&VvO{F7$YGl)csvx z(w`V37;FF;^)4{!-ptO3GZ4n~eBT^S6ISI9?)~FC`1~|9gFkKxXn%v%H`uT$$BLT@ z@Vf{QSMt$$J=m?A+iy{YR<^BMi*FwGzNuff2X3c*WY5I5)I1-vATVl&CaZUp5{#Vi zAX({{YR93ICxgygnqhxLDWQFeZIA&uGP^6~LfS1}=9pSmK+3Kw3T#oc-~=b5RBYMQ zvp<8VS6j35fu$5XSTZ6nR63TJp}>~~++g);_-S%Sz0?dT?E$KOJ0WVBuWpc{C8mYX zC;Ip&?7|6ihh5n69IrcO*Sbi0U>|=dq*cn&Fi)VAPLic-;p+5mt2t%DJ#oRP?Rf&^MK6GYvXo@Zw%aHIc&KP=AY+SUvx+-%a%+c zR{|M0`LdG3usw}q9Z|xqFl{mB@aT6JX-gb4DV&>mua-P3zbB5a~d~Oe(Nki%M@Jlk==SlQd)VI zV>UCNH%#j#f7rNTLCs3IV!W2GSl*HGw?jcrbBAQBd5w@FiNTg!M#nZ?P3A0$bPF3c z$#36~yhY*;D`+BRuO`7YQIYhdn1`KtedmGF=O_2%EbE2bW@qcza2;P43wf;d#1m9n zpeL(3<)XnEivj*dCa#3>DI*_d-%uLhCgcYMO2h{9PY#5qC{i$-F4RR?oK(~UJb4|m?Y_*Gc@(&OR(s4P+tytvAt+Raz727qLS$3*1F2 z{3VuVUJwx_KdvFQf>uoHFOV=kyPh+LgMSj$aGm5R!Ww?BH&Sy$38%cN6K-yxOk>(a zC$^OgIGjx^n<|0N1lxXbzTJ2Vhbb*?{b_z=xj*-)G_N4t`pVv>&k}VZWiyO}K8K9m zZXDP(uw2WYHouX^Zj{h6I`@}xD{9#y*Q=<@0@H72HH#?3*zsujYgHdw%}9UC{D#FB z*Qc4<@lzx5l`NoEvkI_ZQ_AJ3WuTNkN^#aKsNdu>XxlSIGQF%hJmF37? zw^3Kfi9@N8>|{YsIBEgf2CUeBfg}VweK*lDwfv{()oZMjjQZqqPOki2#r^_`#^>3rN7cB zH_q3(Y$b|Bgp?XNnvKe)kAC76uqT$VIXZ!o53_%%IloYa7i6%A=TxvjO>7nulMJek z4@bAhS=mF5sVFtWtB$b`70d24nW<2%k2I=~BPB{FBngV_BrDqI9r!+j^#TDgPVys7 zI(Gowz*SDpi|A0Hge)VbQI;j=B>6OdI2|5iil=6JQJ0s|bz6e3GBWO3df z$$iJ7xz57GqLzviD;8jbtDO$A=Uk)jg)Fpq$DA*sY%k2`MB|{VyAa9@U6PIwD5=&) z6cczAt7g}X%hP35$uVhg%**y$AgA-XpgVRmel`6xO<1UwY#FHGR7(_=#kI&`I8v2x zKus$nA%i79AIUIYqYOia)!0mvb-P=#I?9%rAYMlK@}}$F@2}x~4wx0|6Sm_sbt-X` z1@nzexzRFNYRU_NmA?+C#&r!o)ArIRBWJKEp$CmhMe!pZpd{g?zgN-LU#f>7> zBk{Be*%gX?dmpH*`7#>K%g1zK`q7y}y-nnU)p*3lV$pKbs{@7OEk;5^gA%8BzGK>7{rE^Rp$SQ+nh1yh+2RWCyxBaD#`PQk^S zmpCYPr5ZL9JEGXm;^=9PGac{=sfdNT0GrAq#0Rd`<86V_gkmnjJ=w>9GAq zVnU^U*Oc`P@zSi8dBmXk3SB$2(W#1P&S$+D;@QDwss%FljGb+YE2Wey8BHo6$iqdvPK0ZXR zeegpGzj`6hBzDQJtst?KU4wOJx&`~1vU*I3lJPig505gyC+G>zIVRCFRC|H78LdP~ z{oTefS#1c<<%z0z8SJNlU@PrRrQOh;Y_;raVlbz1o9!BTO`snsa+;^>Yy6o3_ z2{b+9*|TRR*}1%`LE}Rbq#u+L8FV?^HXNlpBX$N+C^08pjajf_5O-rv2F&tvlnb)@ zf9nQQ1T&vtXS5b{a`=PQx?H7ByI!kSVV{j{!YgM@vc_!&4VX`86?IF8taP(MiBjA2 zUVU+u=CN^4rM)egLVyC=VpMSLNHk-E1>c_TJ+uKvH67=VR_QDn>@vpb0D`{ajH|ed z0|E7w`MU29fGCFnqU5_KN~im_j@C^U%Do+U9!g75>rGW39AX;26!7cf<{iVcK354< zU`-$FX6K1Y*BYyAN#}f4(xcC#AedHbkk?mL%y-Pnog3+A#6OHY$>lm4W1peckx5Vb zha`9YE0Pp*=efKgNojKj<;&5@AOM%scC~+jwhN%iQxVO|vxkDBnRqnTRVcO|?&wG| z#<>nHd6n(-!SrbacnY>3V7;B@#Z9Q$Cgah%-j5q!_?Cjv(KE@M9_orV>Id0xnAMwy zTWvGev>S08#U_b4Iu&KhwHVL6?6;Sp^wOgF9D|9eK@)J`*L<85Q}Wd!Q&xhBT%-z} z-h`!4F>^wbGpn9NrXZ(3lmN*t_9gFvb=VkVqwI;0NU?E4Aa;gTRWw&ZxJ;PhU{<=k zSwH(Tf9hAk6sFQF{WU!8L*)Y9%oHQq)GCGQ0XcIxVo!q_R?R77D{F?iB%Bi)BH5n< zTP*ify z@@DO(&kM{AYLouF1y^SVEVYl-Ttb=Bi9A>oInsI53Ni+BbkihtJ3(hf-{zYsCkey7 zXEaK5=4Gw1@-@h+1`r%|%)@tj!%f!~q7?i9B6JO} zIYYJFk0*321%()R7R*=CzCetqFVG=>R5HWj^SO_;b1L3xGk zh@;$`6`W~uC>G}|at2Vf~xBld5)ZNO>@_4y84k6I=Miu)GsX0>ap!w(?R1+*edV+})^NVEY|_%SmEx-%+05f!$ma1))5Rg88bxY-$B zr1!`FZEGA$wnH0_ri0Mon7svA2_s}{Mg|K+ zGA5FB&{M*3%%tPaAF^nzl&sZQ*x|ZL?el*0({yh4e>*e$i*9W&`N&HzzV8dqq{P%z z&ZuQbJ-b}~glLFM2U+Akb?wVe70X6m-hE&OIGm%kE3B%jCe@ZBX=uMrP!2@Quf>D) ze2l$;uM2OhM50#4dGS;&&Z(!Nz zqSDh+0dlHpyjI{$ZYbJ!`yY81TO0b6r_+ z`s_Wirg02AGhGStdmc?C+&5$mBpupz)x?=tl(w2?Q19i4v?6SSaF|Hpt0Bin-~xdc z_ASZ-1FT2v4!PZo|z@>N;ofnUBPp;4&F^Wl)2JM!Azx16aj+JB1f-Qq&PXM4X;gAXi~ zE%v&q+w+U!VHTF6Uvh%uG{KXlFfDbt{Dw|GJ>$GDJ7%&mxa4}`ZVJ(T>;_V3kx;Bi z7_Ls%(SDp?gEYqyPt~j;yJ~+`c=V8DdHEBANKDFNe(7v0?Iq^k50Z8nIU1K{#<>zX z%qxSL-Ezc?N@`tSe5({(6%D*2{Jj7^Z4q8`2^nkr?e@b5i9AV9fI~7=Bg;vOO_mDC zOAfUYG~C`@- zO?fX=$x$m~cK>*NPJ^-_EI$r!8N-P}(IkRM!|2^=L--l_RW@=Q65pHYc^%Zfuw9)# z?&=?K%Hv6k9}rjBNN{w#+o$KrQPJXVv4__1-IX(l7rnUGt{l8L>LKpJ8<{<1+`inA zAg@GKbtzPu0KU`Sq41d=1n|P+At3e)F**C!Cla4P!%+I!-6wMYMTiA0>dyU;5yF;T z!%0o68F_MX(W;4e<{)0WB~A!+q~tgnVrc73+Zc^0j@Ir96PpyL5jO3+BpT!aUkE=m zIcGdiw~QyKZoE$B$!?3hF)ywGteux=!MA4B7IJ0pa5k_W6KE)2teF$(U^=U$;qA>Y zn+elKH%_VDKpvl4s&vqvVcIo*$JFBOhbwsp7Ox@yH!oOp(n)^L}=P}%Wdq3J^ z0HELUucz351vq@>^x*l;{{F{}e@9$;#s6ZHU$3{xXWb=xQ3AefUx z%cjkOAMO;pMAa9GEYeWtVQ)SM*vsBiFRFM-O}%0ASxmtoW?Puw9xdMstbQzTX@oL= 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%0AWNS!-PIa*YQMXv6Ii0nm5&J znz~g#V!1cupzr-lls{YwL^}3p+D2)*+~NCk9pAwBU){{1@>6c?))4urP&5nR2xuQ- z6dnV#+iqLPsnqg@-pB(gu{L1m#{@SEQBwni zNWD33F_>l5K&t}55GdZaIsRSCTm^kV;FJF{#BV+QcSMkLL+H!`n!VW>xtsU-%pFQt zPQYRBr=2zK&}rX3l*`2oYQwF2I%hCTtY$H{J|vV+=grhfUk1x$73$bHX!%3(Z*KS; zBUb{`s^(gTc5?}ijj3A!BUEn)Fg^_adBb5LYmaOb|Co;b==(3TSLS4 zHeFeSIK7<;YTJlY}*p8~_PnZHNoAka|tPcr0 z)0|L#{O7wuEowb{$b!5;Sx?@-O9KQ&;9y=Q@WI`GPF8laQynQR5Ihfj(0BI~*qWKB z$fR~CSBy-PQWVKpW8sZ?8Hd0e@uoQ3^}#LZA**Z&cPI`FHmr{f3X$yqgSB0S2)F=c z5jLYQ`K!tA^so)1r=43qXc?~{X8hYb8D|-MOrk2Iawp<&%C^$LmfwKw>qU(~HkF5Q<}Y3;;SKWs@D$ z$muP5er7P*CLcN3kZDjcUdDV*qNZHV`JL^~ncNrbI66)k<+f$E(!9#ywt+H3#F#pv zyMfx9TP*fr-D1(qc1{w`b_tC2@o7hy?bxHhLueQZ(ixGUq=>ZBNCMl(3CZ$*h z-S9JUdEmqHHa(hK_U3ddlm@yi9O*kJdGi!QNoJf&37ic{q3aF8qC|G&5Bfn0{0Ug5 zFARq-QkP8E04v4$uwiyjB31N}9Wlnz+%c$nsHF>WBGbt-$)x0zSb7{!LO;N6 zu)CsRQ6+P7+}f?IvNDboU{il$Z{XCLs=IgBI|8>(=+hj8HcdKCnh4RhJ_FHu3o@wH z>iNX-{~I@MtsWxw{K%6FpZO~rh@z6W6|_#lXB)F%1VqDJErI_M(DUq%)xDk35QT21 zxmyVQzeQ+3E$tP|9=efnr|Qov-3ZLm5c=&~!w$OLo4qpAuOVji8Z}GTDtLvu0$`Na z8Lky1{wgIA>~O?$`Swam0;;UI&Nex=e~QV#ScZ`!y2wZaa{0$CyeWwq&wp}(?{Jj7 z2V~M0B|H$>kxRh~MCaGfL$2Y29`Z#3{_B^YP~g4-$O>9(0HpH@wGRmczWuqK&j7s6 z%_R4CAmG~!P*BT%sA(ip|n}fGBU>y8 zgmYvU=MB13tVSIRCNp4`h%Itao?PxT{sMv>q5NLCng{e8l|rgPK@eynf(!82G-cM7 z-;t6dikEyw<3*lmZ)7O|fsm>!G=TDfo~V9e$@r22Mp1Vug}coT0(HFa!1*nPy;fE5CwROLcF z{-(SgX9@IoUz#W9FKMpP;MQm8Vt|(}u+#(0AghGKO2`}m} zP-#~`$WV5$lOhFJqTA*9e;t_uz^!;I{>ihsf`a|s;^of?&0oV`{=JK)U)^{kU<$qP z7XD`v<-1A!7R9-XKj?s!B`svNsSG;&Cx`L1%|(cnu-c??i}re5nTnZ1R4r=MjuF9DOpep+mUnMtJK!oerJMiuhX>a z@nRM2!tef~Gb8$Y(b>Jp2~Qz`sn?$!&nk~nLgkN%uspsrDB@$itbeX?a_=|D0PZ%C zx6aS^bA+@%A#UkG%rMtb+U`$_9RZ@0uR@~SrM{ed>}O9bs2tMCtBPWJ)br%R=4vR! zmx!>Ej4m1hMLe6K5mW@()9A;lne@+SDKf#i;;A_13JWj7AGf7jU4AJz_x36U)qkRX z6kL7wru^W}W&5A2yZfLFZuZ4JgrRP@v&Vm~9yZ(!-JJERp%ow^{3`h8is9Q;56~3Y z!2(Kp`~COPw+A1u)XaWuDfl=M6g^Y-oZKLnTih-+j?mXzmn{h@-KR?w{k^D5C{H4w zZ*+Np%i1t^#StxR!a{M(#H5EkT>Lc|fjz3+YE;Mkk$6h|^!>~(~rYuuAp zDEbDSsJ@2%oO2jW6whbPY1X4DuO%t>3r&=HE|S(&2Q@JX(Jx1Mru)L7XBHtD zODV)hy54cvhO{8&RNHN5crrs-ecx;sGAaK;@dQLzmXzG0|;<;vx}qj?^Gd_ey`%BZ@&6I`e`T7 zp6}y*G@cg5(IafRN7+SWIMKj|&ClO)(nQh#G|6l(Lx)XhxY!VhI5X*OXlWFK#?pt; zXvfkUhREnk^98DtfMWUGui>ibP&!<&dg_k;s3?FBgpE*H_E_D$EzNm+cGIGyO{3HF7sEY{Sw{1a! zoy77lj`tyhEb0uKWmO19JzY{k5J<(W>PY%SL-}gVBp~ zikIZSLCP*}i&W~g4g+ZBKi0S`gHvFeeuG|2Nvl!#m%G{}Rw*Rfw@Y!j+&w_$2eQ~T zL9*b}H`lDWOJC+xpNO`&BrJSAqPtYE@iYLa{w`4Zy0JpTepFqqM15yGP2IA(=%QVI ze)RMp@XaAds1Rmjeak&wuc9AeH!f&6ij|Vy0F;uo|3ysl)D=i`2xg*Uk&F@ z7aZ%#4^W#8aR;1T8TO~w$B)$>+~-M|9h_8!w58tARa)BPCzC$)$1ar~QSH1e=*M0! zd2}>R;GdWJ@K+}lLHkIXE*g&K5PR{Pb)$pe)<|m19~HUd5WC3~BdP^4rh)=f@7_z8 z&V0vm*oyW0v2h+7W8YD?JxCoj3Rv7?ksFKK@>%uT;u61}<7wFT99eWQU5y&MBlSb& z#z^C1+*;vBOY<8_#~-#o_wdo?+>P4Iza1wuH)9FEt$r|j7k@lG+*R$FX3HwN_*IDj z^Zw45=fV9;eT@@f7#UzP;fLtf$||c`IlV`y#byoB*Cn2GZkJ zSil~?{tX(z=G!*l`r%QOm9D5(^6f1~8n)mZMoOr_sqk?B;j?@Jz8uHo5G8gpFmbKC zQf8}FW!`}>z8#Nj<~{Qoe=#GqEx+UrCf-y2NrhZ$`Y(^QStcy9+13l@2@2*gl{3hH zC}#FPUH55cv9dEP`eBHAn%3}Pxt{0^zW4)NU=Dx-ILb*&W#suoD#y&Fj=*Fc$Z4F{ z1rYpfl?h{!bSaW&G5RpiUifqlo{ zAnufk&x7x42Sd8Xnu<^7^qO~U1F%G&ysfbiAoBY0IxSXsjt%m5LJLQG4uod-o%Mrn zkGv*U?*9g91wE^y_N8yTt|IhS%%!U;di1mSRnD2XZPDRJkLO1KFV>M+BJ-fwxb{rSA?d;Joy{`xT21%}N?y8b$4A=)}a+4t{N z9LXF8{8=+{g61fRxm));nQ3PSN6dQWkSRqYz1G8SDN<1PpS{9SsoG4XI*p(Q$ zIJeq#^8Bmq?BasM_{2itl+q;U!fWcAa#$a!uEqd!r`R$7vv0{J`}MR*(;>iAm$LDH z#{!tb~=>^N^N%7+4SJ@g* z-I??QXks129QCr~v$5aP|EVqPZvWDr%QAUqYK`;06+MYYtg#(=dUN@TmP}kQ$=9*S z>7A*d>{)3q?R7VI#2x-t-MYJImeSc9=PF*FexH)?Wl7v#-yPdy(k*j)=ND(UPsrXA z!uEI1+tTzWqIJh?&I3zkV5?{KH^Vo{o939fcgNp+T@sbm(K8*`F$n(o6<1-@XtzS6yNo=0?Yyc@V=1C}H0GXwT8X#DGe)xOM&b)hXl2Ym)Edj#*rX;}Hh zeSoPS!Lu@gl~zn2fUR{^Cty?Z!<|K!bSIvRm4p{|T$xvU6pt738?$X{k1~o9z4TM- zsk+Cz5Mk8{57x)PdHVRttE=ln!aG#bmad+REMg-hYXNHL(^j-gi{Z^NbgGzKWLxm{Mz;=dCC| zn|=1~_xgqROXnL`-$7*i`_JIK@3mdp_no$GlYqkr z;Gxw1Bfz#=EzGR`=q+Inj=xytlX-I1soBw9S@TwAdv&@_w}{sE+M}ZP@XO!b*Ol%> z@07Z2Jga`uKj}@`;qNZz>Be@hDms^W%I@*rbnrHK`z0#IB9p($DCl}cJqYvlG7jo@ z(A(=}edd$XZ3#p#AphRf3c-fJzeufpcw9HcF^JT|d5ssR!8-^Itnvt6Nh++iVtoJ^ zo_OvQAyC(x(FJz0#5jN zA65J7&cUaNnF5iTiKM4L!|21m1OLAF Date: Sun, 5 Sep 2021 05:38:11 +1000 Subject: [PATCH 24/29] update help - file transfer and groups (#98) * update help - file transfer and groups * update help * update help * update help Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- src/Simplex/Chat.hs | 16 ++++-- src/Simplex/Chat/Help.hs | 105 +++++++++++++++++++++++++++++---------- 2 files changed, 91 insertions(+), 30 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index f8023291fb..8d334181f2 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -65,6 +65,8 @@ import UnliftIO.STM data ChatCommand = ChatHelp + | FilesHelp + | GroupsHelp | MarkdownHelp | AddContact | Connect SMPQueueInfo @@ -169,6 +171,8 @@ inputSubscriber = do processChatCommand :: forall m. ChatMonad m => User -> ChatCommand -> m () processChatCommand user@User {userId, profile} = \case ChatHelp -> printToView chatHelpInfo + FilesHelp -> printToView filesHelpInfo + GroupsHelp -> printToView groupsHelpInfo MarkdownHelp -> printToView markdownInfo AddContact -> do (connId, qInfo) <- withAgent createConnection @@ -1046,7 +1050,9 @@ withStore action = chatCommandP :: Parser ChatCommand chatCommandP = - ("/help" <|> "/h") $> ChatHelp + ("/help files" <|> "/help file" <|> "/hf") $> FilesHelp + <|> ("/help groups" <|> "/help group" <|> "/hg") $> GroupsHelp + <|> ("/help" <|> "/h") $> ChatHelp <|> ("/group #" <|> "/group " <|> "/g #" <|> "/g ") *> (NewGroup <$> groupProfile) <|> ("/add #" <|> "/add " <|> "/a #" <|> "/a ") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) <|> ("/join #" <|> "/join " <|> "/j #" <|> "/j ") *> (JoinGroup <$> displayName) @@ -1061,9 +1067,9 @@ chatCommandP = <|> A.char '@' *> (SendMessage <$> displayName <*> (A.space *> A.takeByteString)) <|> ("/file #" <|> "/f #") *> (SendGroupFile <$> displayName <* A.space <*> filePath) <|> ("/file @" <|> "/file " <|> "/f @" <|> "/f ") *> (SendFile <$> displayName <* A.space <*> filePath) - <|> ("/file_receive " <|> "/fr ") *> (ReceiveFile <$> A.decimal <*> optional (A.space *> filePath)) - <|> ("/file_cancel " <|> "/fc ") *> (CancelFile <$> A.decimal) - <|> ("/file_status " <|> "/fs ") *> (FileStatus <$> A.decimal) + <|> ("/freceive " <|> "/fr ") *> (ReceiveFile <$> A.decimal <*> optional (A.space *> filePath)) + <|> ("/fcancel " <|> "/fc ") *> (CancelFile <$> A.decimal) + <|> ("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal) <|> ("/markdown" <|> "/m") $> MarkdownHelp <|> ("/profile " <|> "/p ") *> (UpdateProfile <$> userProfile) <|> ("/profile" <|> "/p") $> ShowProfile @@ -1086,5 +1092,5 @@ chatCommandP = memberRole = (" owner" $> GROwner) <|> (" admin" $> GRAdmin) - <|> (" normal" $> GRMember) + <|> (" member" $> GRMember) <|> pure GRAdmin diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index 997931e3fe..10b8ae5fe1 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -1,54 +1,109 @@ {-# LANGUAGE OverloadedStrings #-} -module Simplex.Chat.Help where +module Simplex.Chat.Help + ( chatHelpInfo, + filesHelpInfo, + groupsHelpInfo, + markdownInfo, + ) +where import Data.List (intersperse) +import Data.Text (Text) import Simplex.Chat.Markdown import Simplex.Chat.Styled import System.Console.ANSI.Types +highlight :: Text -> Markdown +highlight = Markdown (Colored Cyan) + +green :: Text -> Markdown +green = Markdown (Colored Green) + +indent :: Markdown +indent = " " + +listHighlight :: [Text] -> Markdown +listHighlight = mconcat . intersperse ", " . map highlight + chatHelpInfo :: [StyledString] chatHelpInfo = map styleMarkdown - [ highlight "Using Simplex chat prototype.", + [ highlight "Using SimpleX chat prototype", "Follow these steps to set up a connection:", "", - Markdown (Colored Green) "Step 1: " <> highlight "/connect" <> " -- Alice adds a contact.", + green "Step 1: " <> highlight "/connect" <> " - Alice adds a contact.", indent <> "Alice should send the invitation printed by the /add command", indent <> "to her contact, Bob, out-of-band, via any trusted channel.", "", - Markdown (Colored Green) "Step 2: " <> highlight "/connect " <> " -- Bob accepts the invitation.", + green "Step 2: " <> highlight "/connect " <> " - Bob accepts the invitation.", indent <> "Bob should use the invitation he received out-of-band.", "", - Markdown (Colored Green) "Step 3: " <> "Bob and Alice are notified that the connection is set up,", + green "Step 3: " <> "Bob and Alice are notified that the connection is set up,", indent <> "both can now send messages:", - indent <> highlight "@bob Hello, Bob!" <> " -- Alice messages Bob (assuming Bob has display name 'bob').", - indent <> highlight "@alice Hey, Alice!" <> " -- Bob replies to Alice.", + indent <> highlight "@bob Hello, Bob!" <> " - Alice messages Bob (assuming Bob has display name 'bob').", + indent <> highlight "@alice Hey, Alice!" <> " - Bob replies to Alice.", "", - Markdown (Colored Green) "Other commands:", - indent <> highlight "/delete " <> " -- deletes contact and all messages with them.", - indent <> highlight "/markdown" <> " -- prints the supported markdown syntax.", + green "To send file:", + indent <> highlight "/file bob ./photo.jpg" <> " - Alice sends file to Bob", + indent <> "File commands: " <> highlight "/help files", "", - "The commands may be abbreviated to a single letter: " <> listCommands ["/a", "/c", "/d", "/h", "/m"] + green "To create group:", + indent <> highlight "/group team" <> " - create group #team", + indent <> "Group commands: " <> highlight "/help groups", + "", + green "Other commands:", + indent <> highlight "/profile " <> " - show user profile", + indent <> highlight "/profile []" <> " - update user profile", + indent <> highlight "/delete " <> " - delete contact and all messages with them", + indent <> highlight "/markdown " <> " - show supported markdown syntax", + indent <> highlight "/quit " <> " - quit chat", + "", + "The commands may be abbreviated to a single letter: " <> listHighlight ["/c", "/f", "/g", "/p", "/h"] <> ", etc." + ] + +filesHelpInfo :: [StyledString] +filesHelpInfo = + map + styleMarkdown + [ green "File transfer commands:", + indent <> highlight "/file @ " <> " - send file to contact.", + indent <> highlight "/file # " <> " - send file to group.", + indent <> highlight "/freceive []" <> " - accept to receive file.", + indent <> highlight "/fcancel " <> " - cancel sending / receiving file.", + indent <> highlight "/fstatus " <> " - show file transfer status.", + "", + "The commands may be abbreviated: " <> listHighlight ["/f", "/fr", "/fc", "/fs"] + ] + +groupsHelpInfo :: [StyledString] +groupsHelpInfo = + map + styleMarkdown + [ green "Group management commands:", + indent <> highlight "/group [] " <> " - create group", + indent <> highlight "/add []" <> " - add contact to group, roles: " <> highlight "owner" <> ", " <> highlight "admin" <> " (default), " <> highlight "member", + indent <> highlight "/join " <> " - accept group invitation", + indent <> highlight "/remove " <> " - remove member from group", + indent <> highlight "/leave " <> " - leave group", + indent <> highlight "/delete " <> " - delete group", + indent <> highlight "/members " <> " - list group members", + indent <> highlight "# " <> " - send message to group", + "", + "The commands may be abbreviated: " <> listHighlight ["/g", "/a", "/j", "/rm", "/l", "/d", "/ms"] ] - where - listCommands = mconcat . intersperse ", " . map highlight - highlight = Markdown (Colored Cyan) - indent = " " markdownInfo :: [StyledString] markdownInfo = map styleMarkdown - [ "Markdown:", - " *bold* - " <> Markdown Bold "bold text", - " _italic_ - " <> Markdown Italic "italic text" <> " (shown as underlined)", - " +underlined+ - " <> Markdown Underline "underlined text", - " ~strikethrough~ - " <> Markdown StrikeThrough "strikethrough text" <> " (shown as inverse)", - " `code snippet` - " <> Markdown Snippet "a + b // no *markdown* here", - " !1 text! - " <> red "red text" <> " (1-6: red, green, blue, yellow, cyan, magenta)", - " #secret# - " <> Markdown Secret "secret text" <> " (can be copy-pasted)" + [ green "Markdown:", + indent <> highlight "*bold* " <> " - " <> Markdown Bold "bold text", + indent <> highlight "_italic_ " <> " - " <> Markdown Italic "italic text" <> " (shown as underlined)", + indent <> highlight "+underlined+ " <> " - " <> Markdown Underline "underlined text", + indent <> highlight "~strikethrough~" <> " - " <> Markdown StrikeThrough "strikethrough text" <> " (shown as inverse)", + indent <> highlight "`code snippet` " <> " - " <> Markdown Snippet "a + b // no *markdown* here", + indent <> highlight "!1 text! " <> " - " <> Markdown (Colored Red) "red text" <> " (1-6: red, green, blue, yellow, cyan, magenta)", + indent <> highlight "#secret# " <> " - " <> Markdown Secret "secret text" <> " (can be copy-pasted)" ] - where - red = Markdown (Colored Red) From 28103825fa97ef565fb0f8de829fbe26257e3094 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 5 Sep 2021 14:08:29 +0100 Subject: [PATCH 25/29] send files to groups (#97) * add sender/recipient info to file types * send file to group (WIP) * send file to group, test * show file status when sending file to group * notification when cancelled sending to group, remove chunks when file complete or canceleld --- src/Simplex/Chat.hs | 106 ++++++++++++++------- src/Simplex/Chat/Store.hs | 133 ++++++++++++++++++--------- src/Simplex/Chat/Types.hs | 5 +- src/Simplex/Chat/View.hs | 188 +++++++++++++++++++++++++------------- stack.yaml | 2 +- tests/ChatTests.hs | 77 ++++++++++++---- 6 files changed, 351 insertions(+), 160 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 8d334181f2..5427f7af97 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -51,7 +51,7 @@ import Simplex.Messaging.Agent.Protocol import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Parsers (parseAll) import qualified Simplex.Messaging.Protocol as SMP -import Simplex.Messaging.Util (bshow, raceAny_) +import Simplex.Messaging.Util (bshow, raceAny_, tryError) import System.Exit (exitFailure, exitSuccess) import System.FilePath (combine, splitExtensions, takeFileName) import System.IO (Handle, IOMode (..), SeekMode (..), hFlush, openFile, stdout) @@ -163,6 +163,8 @@ inputSubscriber = do case cmd of SendMessage c msg -> showSentMessage c msg SendGroupMessage g msg -> showSentGroupMessage g msg + SendFile c f -> showSentFileInvitation c f + SendGroupFile g f -> showSentGroupFileInvitation g f _ -> printToView [plain s] user <- readTVarIO =<< asks currentUser withAgentLock a . withLock l . void . runExceptT $ @@ -264,33 +266,47 @@ processChatCommand user@User {userId, profile} = \case sendGroupMessage members msgEvent setActive $ ActiveG gName SendFile cName f -> do - unlessM (doesFileExist f) . chatError $ CEFileNotFound f - contact@Contact {contactId} <- withStore $ \st -> getContact st userId cName + (fileSize, chSize) <- checkSndFile f + contact <- withStore $ \st -> getContact st userId cName (agentConnId, fileQInfo) <- withAgent createConnection - fileSize <- getFileSize f let fileInv = FileInvitation {fileName = takeFileName f, fileSize, fileQInfo} - chSize <- asks $ fileChunkSize . config - ft <- withStore $ \st -> createSndFileTransfer st userId contactId f fileInv agentConnId chSize + SndFileTransfer {fileId} <- withStore $ \st -> + createSndFileTransfer st userId contact f fileInv agentConnId chSize sendDirectMessage (contactConnId contact) $ XFile fileInv - showSentFileInvitation cName ft + showSentFileInfo fileId setActive $ ActiveC cName - SendGroupFile _gName _file -> pure () + SendGroupFile gName f -> do + (fileSize, chSize) <- checkSndFile f + group@Group {members, membership} <- withStore $ \st -> getGroup st user gName + unless (memberActive membership) $ chatError CEGroupMemberUserRemoved + let fileName = takeFileName f + ms <- forM (filter memberActive members) $ \m -> do + (connId, fileQInfo) <- withAgent createConnection + pure (m, connId, FileInvitation {fileName, fileSize, fileQInfo}) + fileId <- withStore $ \st -> createSndGroupFileTransfer st userId group ms f fileSize chSize + forM_ ms $ \(m, _, fileInv) -> + traverse (`sendDirectMessage` XFile fileInv) $ memberConnId m + showSentFileInfo fileId + setActive $ ActiveG gName ReceiveFile fileId filePath_ -> do - RcvFileTransfer {fileInvitation = FileInvitation {fileName, fileQInfo}, fileStatus} <- withStore $ \st -> getRcvFileTransfer st userId fileId + ft@RcvFileTransfer {fileInvitation = FileInvitation {fileName, fileQInfo}, fileStatus} <- withStore $ \st -> getRcvFileTransfer st userId fileId unless (fileStatus == RFSNew) . chatError $ CEFileAlreadyReceiving fileName - agentConnId <- withAgent $ \a -> joinConnection a fileQInfo . directMessage $ XFileAcpt fileName - filePath <- getRcvFilePath fileId filePath_ fileName - withStore $ \st -> acceptRcvFileTransfer st userId fileId agentConnId filePath - -- TODO include file sender in the message - showRcvFileAccepted fileId filePath + tryError (withAgent $ \a -> joinConnection a fileQInfo . directMessage $ XFileAcpt fileName) >>= \case + Right agentConnId -> do + filePath <- getRcvFilePath fileId filePath_ fileName + withStore $ \st -> acceptRcvFileTransfer st userId fileId agentConnId filePath + showRcvFileAccepted ft filePath + Left (ChatErrorAgent (SMP SMP.AUTH)) -> showRcvFileSndCancelled ft + Left (ChatErrorAgent (CONN DUPLICATE)) -> showRcvFileSndCancelled ft + Left e -> throwError e CancelFile fileId -> withStore (\st -> getFileTransfer st userId fileId) >>= \case FTSnd fts -> do - mapM_ cancelSndFileTransfer fts - showSndFileCancelled fileId + forM_ fts $ \ft -> cancelSndFileTransfer ft + showSndGroupFileCancelled fts FTRcv ft -> do cancelRcvFileTransfer ft - showRcvFileCancelled fileId + showRcvFileCancelled ft FileStatus fileId -> withStore (\st -> getFileTransferProgress st userId fileId) >>= showFileTransferStatus UpdateProfile p -> unless (p == profile) $ do @@ -306,6 +322,10 @@ processChatCommand user@User {userId, profile} = \case contactMember Contact {contactId} = find $ \GroupMember {memberContactId = cId, memberStatus = s} -> cId == Just contactId && s /= GSMemRemoved && s /= GSMemLeft + checkSndFile :: FilePath -> m (Integer, Integer) + checkSndFile f = do + unlessM (doesFileExist f) . chatError $ CEFileNotFound f + (,) <$> getFileSize f <*> asks (fileChunkSize . config) getRcvFilePath :: Int64 -> Maybe FilePath -> String -> m FilePath getRcvFilePath fileId filePath fileName = case filePath of Nothing -> do @@ -449,7 +469,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do ChatMessage {chatMsgEvent} <- liftEither $ parseChatMessage msgBody case chatMsgEvent of XMsgNew (MsgContent MTText [] body) -> newTextMessage c meta $ find (isSimplexContentType XCText) body - XFile fInv -> processFileInvitation ct fInv + XFile fInv -> processFileInvitation ct meta fInv XInfo p -> xInfo ct p XGrpInv gInv -> processGroupInvitation ct gInv XInfoProbe probe -> xInfoProbe ct probe @@ -567,6 +587,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do case chatMsgEvent of XMsgNew (MsgContent MTText [] body) -> newGroupTextMessage gName m meta $ find (isSimplexContentType XCText) body + XFile fInv -> processGroupFileInvitation gName m meta fInv XGrpMemNew memInfo -> xGrpMemNew gName m memInfo XGrpMemIntro memInfo -> xGrpMemIntro gName m memInfo XGrpMemInv memId introInv -> xGrpMemInv gName m memId introInv @@ -591,7 +612,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do _ -> messageError "REQ from file connection must have x.file.acpt" CON -> do withStore $ \st -> updateSndFileStatus st ft FSConnected - showSndFileStart fileId + showSndFileStart ft sendFileChunk ft SENT msgId -> do withStore $ \st -> updateSndFileChunkSent st ft msgId @@ -599,7 +620,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do MERR _ err -> do cancelSndFileTransfer ft case err of - SMP SMP.AUTH -> unless (fileStatus == FSCancelled) $ showSndFileRcvCancelled fileId + SMP SMP.AUTH -> unless (fileStatus == FSCancelled) $ showSndFileRcvCancelled ft _ -> chatError $ CEFileSend fileId err MSG meta _ -> withAckMessage agentConnId meta $ pure () @@ -610,12 +631,12 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do case agentMsg of CON -> do withStore $ \st -> updateRcvFileStatus st ft FSConnected - showRcvFileStart fileId + showRcvFileStart ft MSG meta@MsgMeta {recipient = (msgId, _), integrity} msgBody -> withAckMessage agentConnId meta $ do parseFileChunk msgBody >>= \case (0, _) -> do cancelRcvFileTransfer ft - showRcvFileSndCancelled fileId + showRcvFileSndCancelled ft (chunkNo, chunk) -> do case integrity of MsgOk -> pure () @@ -632,8 +653,10 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do then badRcvFileChunk ft "incorrect chunk size" else do appendFileChunk ft chunkNo chunk - withStore $ \st -> updateRcvFileStatus st ft FSComplete - showRcvFileComplete fileId + withStore $ \st -> do + updateRcvFileStatus st ft FSComplete + deleteRcvFileChunks st ft + showRcvFileComplete ft closeFileHandle fileId rcvFiles withAgent (`deleteConnection` agentConnId) RcvChunkDuplicate -> pure () @@ -681,7 +704,7 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do newTextMessage c meta = \case Just MsgContentBody {contentData = bs} -> do let text = safeDecodeUtf8 bs - showReceivedMessage c (snd $ broker meta) text (integrity meta) + showReceivedMessage c (snd $ broker meta) (msgPlain text) (integrity meta) showToast (c <> "> ") text setActive $ ActiveC c _ -> messageError "x.msg.new: no expected message body" @@ -690,19 +713,26 @@ processAgentMessage user@User {userId, profile} agentConnId agentMessage = do newGroupTextMessage gName GroupMember {localDisplayName = c} meta = \case Just MsgContentBody {contentData = bs} -> do let text = safeDecodeUtf8 bs - showReceivedGroupMessage gName c (snd $ broker meta) text (integrity meta) + showReceivedGroupMessage gName c (snd $ broker meta) (msgPlain text) (integrity meta) showToast ("#" <> gName <> " " <> c <> "> ") text setActive $ ActiveG gName _ -> messageError "x.msg.new: no expected message body" - processFileInvitation :: Contact -> FileInvitation -> m () - processFileInvitation Contact {contactId, localDisplayName = c} fInv = do + processFileInvitation :: Contact -> MsgMeta -> FileInvitation -> m () + processFileInvitation contact@Contact {localDisplayName = c} meta fInv = do -- TODO chunk size has to be sent as part of invitation chSize <- asks $ fileChunkSize . config - ft <- withStore $ \st -> createRcvFileTransfer st userId contactId fInv chSize - showReceivedFileInvitation c ft + ft <- withStore $ \st -> createRcvFileTransfer st userId contact fInv chSize + showReceivedMessage c (snd $ broker meta) (receivedFileInvitation ft) (integrity meta) setActive $ ActiveC c + processGroupFileInvitation :: GroupName -> GroupMember -> MsgMeta -> FileInvitation -> m () + processGroupFileInvitation gName m@GroupMember {localDisplayName = c} meta fInv = do + chSize <- asks $ fileChunkSize . config + ft <- withStore $ \st -> createRcvGroupFileTransfer st userId m fInv chSize + showReceivedGroupMessage gName c (snd $ broker meta) (receivedFileInvitation ft) (integrity meta) + setActive $ ActiveG gName + processGroupInvitation :: Contact -> GroupInvitation -> m () processGroupInvitation ct@Contact {localDisplayName} inv@(GroupInvitation (fromMemId, fromRole) (memId, memRole) _ _) = do when (fromRole < GRAdmin || fromRole < memRole) $ chatError (CEGroupContactRole localDisplayName) @@ -852,8 +882,10 @@ sendFileChunk ft@SndFileTransfer {fileId, fileStatus, agentConnId} = withStore (`createSndFileChunk` ft) >>= \case Just chunkNo -> sendFileChunkNo ft chunkNo Nothing -> do - withStore $ \st -> updateSndFileStatus st ft FSComplete - showSndFileComplete fileId + withStore $ \st -> do + updateSndFileStatus st ft FSComplete + deleteSndFileChunks st ft + showSndFileComplete ft closeFileHandle fileId sndFiles withAgent (`deleteConnection` agentConnId) @@ -915,7 +947,9 @@ isFileActive fileId files = do cancelRcvFileTransfer :: ChatMonad m => RcvFileTransfer -> m () cancelRcvFileTransfer ft@RcvFileTransfer {fileId, fileStatus} = do closeFileHandle fileId rcvFiles - withStore $ \st -> updateRcvFileStatus st ft FSCancelled + withStore $ \st -> do + updateRcvFileStatus st ft FSCancelled + deleteRcvFileChunks st ft case fileStatus of RFSAccepted RcvFileInfo {agentConnId} -> withAgent (`suspendConnection` agentConnId) RFSConnected RcvFileInfo {agentConnId} -> withAgent (`suspendConnection` agentConnId) @@ -924,9 +958,11 @@ cancelRcvFileTransfer ft@RcvFileTransfer {fileId, fileStatus} = do cancelSndFileTransfer :: ChatMonad m => SndFileTransfer -> m () cancelSndFileTransfer ft@SndFileTransfer {agentConnId, fileStatus} = unless (fileStatus == FSCancelled || fileStatus == FSComplete) $ do - withStore $ \st -> updateSndFileStatus st ft FSCancelled + withStore $ \st -> do + updateSndFileStatus st ft FSCancelled + deleteSndFileChunks st ft withAgent $ \a -> do - void $ sendMessage a agentConnId "0 " + void (sendMessage a agentConnId "0 ") `catchError` \_ -> pure () suspendConnection a agentConnId closeFileHandle :: ChatMonad m => Int64 -> (ChatController -> TVar (Map Int64 Handle)) -> m () diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 4896ea56e8..d63c31df05 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -63,21 +63,26 @@ module Simplex.Chat.Store matchSentProbe, mergeContactRecords, createSndFileTransfer, + createSndGroupFileTransfer, updateSndFileStatus, createSndFileChunk, updateSndFileChunkMsg, updateSndFileChunkSent, + deleteSndFileChunks, createRcvFileTransfer, + createRcvGroupFileTransfer, getRcvFileTransfer, acceptRcvFileTransfer, updateRcvFileStatus, createRcvFileChunk, updatedRcvFileChunkStored, + deleteRcvFileChunks, getFileTransfer, getFileTransferProgress, ) where +import Control.Applicative ((<|>)) import Control.Concurrent.STM (stateTVar) import Control.Exception (Exception) import qualified Control.Exception as E @@ -107,7 +112,7 @@ import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Util (bshow, liftIOEither, (<$$>)) -import System.FilePath (takeBaseName, takeExtension) +import System.FilePath (takeBaseName, takeExtension, takeFileName) import UnliftIO.STM -- | The list of migrations in ascending order by date @@ -635,7 +640,7 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = [sql| SELECT g.local_display_name, - m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, + m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id @@ -655,14 +660,19 @@ getConnectionChatDirection st User {userId, userContactId} agentConnId = <$> DB.query db [sql| - SELECT s.file_status, f.file_name, f.file_size, f.chunk_size, f.file_path + SELECT s.file_status, f.file_name, f.file_size, f.chunk_size, f.file_path, cs.local_display_name, m.local_display_name FROM snd_files s JOIN files f USING (file_id) + LEFT JOIN contacts cs USING (contact_id) + LEFT JOIN group_members m USING (group_member_id) WHERE f.user_id = ? AND f.file_id = ? AND s.connection_id = ? |] (userId, fileId, connId) - sndFileTransfer_ :: Int64 -> Int64 -> [(FileStatus, String, Integer, Integer, FilePath)] -> Either StoreError SndFileTransfer - sndFileTransfer_ fileId connId [(fileStatus, fileName, fileSize, chunkSize, filePath)] = Right SndFileTransfer {..} + sndFileTransfer_ :: Int64 -> Int64 -> [(FileStatus, String, Integer, Integer, FilePath, Maybe ContactName, Maybe ContactName)] -> Either StoreError SndFileTransfer + sndFileTransfer_ fileId connId [(fileStatus, fileName, fileSize, chunkSize, filePath, contactName_, memberName_)] = + case contactName_ <|> memberName_ of + Just recipientDisplayName -> Right SndFileTransfer {..} + Nothing -> Left $ SESndFileInvalid fileId sndFileTransfer_ fileId _ _ = Left $ SESndFileNotFound fileId updateConnectionStatus :: MonadUnliftIO m => SQLiteStore -> Connection -> ConnStatus -> m () @@ -738,7 +748,7 @@ getGroup_ db User {userId, userContactId} localDisplayName = do db [sql| SELECT - m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, + m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at @@ -790,13 +800,14 @@ getGroupInvitation st user localDisplayName = findFromContact (IBContact contactId) = find ((== Just contactId) . memberContactId) findFromContact _ = const Nothing -type GroupMemberRow = (Int64, ByteString, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, Maybe Int64, ContactName, Maybe Int64, ContactName, Text) +type GroupMemberRow = (Int64, Int64, ByteString, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, Maybe Int64, ContactName, Maybe Int64, ContactName, Text) toGroupMember :: Int64 -> GroupMemberRow -> GroupMember -toGroupMember userContactId (groupMemberId, memberId, memberRole, memberCategory, memberStatus, invitedById, localDisplayName, memberContactId, displayName, fullName) = +toGroupMember userContactId (groupMemberId, groupId, memberId, memberRole, memberCategory, memberStatus, invitedById, localDisplayName, memberContactId, displayName, fullName) = let memberProfile = Profile {displayName, fullName} invitedBy = toInvitedBy userContactId invitedById - in GroupMember {groupMemberId, memberId, memberRole, memberCategory, memberStatus, invitedBy, localDisplayName, memberProfile, memberContactId, activeConn = Nothing} + activeConn = Nothing + in GroupMember {..} createContactGroupMember :: StoreMonad m => SQLiteStore -> TVar ChaChaDRG -> User -> Int64 -> Contact -> GroupMemberRole -> ConnId -> m GroupMember createContactGroupMember st gVar user groupId contact memberRole agentConnId = @@ -861,6 +872,7 @@ createNewMember_ memProfileId } = do let invitedById = fromInvitedBy userContactId invitedBy + activeConn = Nothing DB.execute db [sql| @@ -870,19 +882,7 @@ createNewMember_ |] (groupId, memberId, memberRole, memberCategory, memberStatus, invitedById, userId, localDisplayName, memProfileId, memberContactId) groupMemberId <- insertedRowId db - pure $ - GroupMember - { groupMemberId, - memberId, - memberRole, - memberStatus, - memberCategory, - invitedBy, - memberProfile, - localDisplayName, - memberContactId, - activeConn = Nothing - } + pure GroupMember {..} deleteGroupMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> GroupMember -> m () deleteGroupMemberConnection st userId m = @@ -982,11 +982,11 @@ getIntroduction_ db reMember toMember = ExceptT $ do toIntro _ = Left SEIntroNotFound createIntroReMember :: StoreMonad m => SQLiteStore -> User -> Group -> GroupMember -> MemberInfo -> ConnId -> ConnId -> m GroupMember -createIntroReMember st user@User {userId} group _host@GroupMember {memberContactId, activeConn} memInfo@(MemberInfo _ _ memberProfile) groupAgentConnId directAgentConnId = +createIntroReMember st user@User {userId} group@Group {groupId} _host@GroupMember {memberContactId, activeConn} memInfo@(MemberInfo _ _ memberProfile) groupAgentConnId directAgentConnId = liftIOEither . withTransaction st $ \db -> runExceptT $ do let cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn Connection {connId = directConnId} <- liftIO $ createConnection_ db userId directAgentConnId memberContactId cLevel - (localDisplayName, contactId, memProfileId) <- ExceptT $ createContact_ db userId directConnId memberProfile (Just $ groupId group) + (localDisplayName, contactId, memProfileId) <- ExceptT $ createContact_ db userId directConnId memberProfile (Just groupId) liftIO $ do let newMember = NewGroupMember @@ -1059,7 +1059,8 @@ createContactMember_ db User {userId, userContactId} groupId userOrContact (memb let memberProfile = profile' userOrContact memberContactId = Just $ contactId' userOrContact localDisplayName = localDisplayName' userOrContact - pure GroupMember {groupMemberId, memberId, memberRole, memberCategory, memberStatus, invitedBy, localDisplayName, memberProfile, memberContactId, activeConn = Nothing} + activeConn = Nothing + pure GroupMember {..} where insertMember_ = DB.executeNamed @@ -1094,7 +1095,7 @@ getViaGroupMember st User {userId, userContactId} Contact {contactId} = [sql| SELECT g.local_display_name, - m.group_member_id, m.member_id, m.member_role, m.member_category, m.member_status, + m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.created_at @@ -1148,8 +1149,8 @@ getViaGroupContact st User {userId} GroupMember {groupMemberId} = in Just Contact {contactId, localDisplayName, profile, activeConn, viaGroup} toContact _ = Nothing -createSndFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> FilePath -> FileInvitation -> ConnId -> Integer -> m SndFileTransfer -createSndFileTransfer st userId contactId filePath FileInvitation {fileName, fileSize} agentConnId chunkSize = +createSndFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> FilePath -> FileInvitation -> ConnId -> Integer -> m SndFileTransfer +createSndFileTransfer st userId Contact {contactId, localDisplayName = recipientDisplayName} filePath FileInvitation {fileName, fileSize} agentConnId chunkSize = liftIO . withTransaction st $ \db -> do DB.execute db "INSERT INTO files (user_id, contact_id, file_name, file_path, file_size, chunk_size) VALUES (?, ?, ?, ?, ?, ?)" (userId, contactId, fileName, filePath, fileSize, chunkSize) fileId <- insertedRowId db @@ -1158,6 +1159,17 @@ createSndFileTransfer st userId contactId filePath FileInvitation {fileName, fil DB.execute db "INSERT INTO snd_files (file_id, file_status, connection_id) VALUES (?, ?, ?)" (fileId, fileStatus, connId) pure SndFileTransfer {..} +createSndGroupFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Group -> [(GroupMember, ConnId, FileInvitation)] -> FilePath -> Integer -> Integer -> m Int64 +createSndGroupFileTransfer st userId Group {groupId} ms filePath fileSize chunkSize = + liftIO . withTransaction st $ \db -> do + let fileName = takeFileName filePath + DB.execute db "INSERT INTO files (user_id, group_id, file_name, file_path, file_size, chunk_size) VALUES (?, ?, ?, ?, ?, ?)" (userId, groupId, fileName, filePath, fileSize, chunkSize) + fileId <- insertedRowId db + forM_ ms $ \(GroupMember {groupMemberId}, agentConnId, _) -> do + Connection {connId} <- createSndFileConnection_ db userId fileId agentConnId + DB.execute db "INSERT INTO snd_files (file_id, file_status, connection_id, group_member_id) VALUES (?, ?, ?, ?)" (fileId, FSNew, connId, groupMemberId) + pure fileId + createSndFileConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> IO Connection createSndFileConnection_ db userId fileId agentConnId = do createdAt <- getCurrentTime @@ -1218,13 +1230,26 @@ updateSndFileChunkSent st SndFileTransfer {fileId, connId} msgId = |] (fileId, connId, msgId) -createRcvFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Int64 -> FileInvitation -> Integer -> m RcvFileTransfer -createRcvFileTransfer st userId contactId f@FileInvitation {fileName, fileSize, fileQInfo} chunkSize = +deleteSndFileChunks :: MonadUnliftIO m => SQLiteStore -> SndFileTransfer -> m () +deleteSndFileChunks st SndFileTransfer {fileId, connId} = + liftIO . withTransaction st $ \db -> + DB.execute db "DELETE FROM snd_file_chunks WHERE file_id = ? AND connection_id = ?" (fileId, connId) + +createRcvFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> Contact -> FileInvitation -> Integer -> m RcvFileTransfer +createRcvFileTransfer st userId Contact {contactId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileQInfo} chunkSize = liftIO . withTransaction st $ \db -> do DB.execute db "INSERT INTO files (user_id, contact_id, file_name, file_size, chunk_size) VALUES (?, ?, ?, ?, ?)" (userId, contactId, fileName, fileSize, chunkSize) fileId <- insertedRowId db DB.execute db "INSERT INTO rcv_files (file_id, file_status, file_queue_info) VALUES (?, ?, ?)" (fileId, FSNew, fileQInfo) - pure RcvFileTransfer {fileId, fileInvitation = f, fileStatus = RFSNew, chunkSize} + pure RcvFileTransfer {fileId, fileInvitation = f, fileStatus = RFSNew, senderDisplayName = c, chunkSize} + +createRcvGroupFileTransfer :: MonadUnliftIO m => SQLiteStore -> UserId -> GroupMember -> FileInvitation -> Integer -> m RcvFileTransfer +createRcvGroupFileTransfer st userId GroupMember {groupId, groupMemberId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileQInfo} chunkSize = + liftIO . withTransaction st $ \db -> do + DB.execute db "INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size) VALUES (?, ?, ?, ?, ?)" (userId, groupId, fileName, fileSize, chunkSize) + fileId <- insertedRowId db + DB.execute db "INSERT INTO rcv_files (file_id, file_status, file_queue_info, group_member_id) VALUES (?, ?, ?, ?)" (fileId, FSNew, fileQInfo, groupMemberId) + pure RcvFileTransfer {fileId, fileInvitation = f, fileStatus = RFSNew, senderDisplayName = c, chunkSize} getRcvFileTransfer :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> m RcvFileTransfer getRcvFileTransfer st userId fileId = @@ -1238,28 +1263,34 @@ getRcvFileTransfer_ db userId fileId = db [sql| SELECT r.file_status, r.file_queue_info, f.file_name, - f.file_size, f.chunk_size, f.file_path, c.connection_id, c.agent_conn_id + f.file_size, f.chunk_size, cs.local_display_name, m.local_display_name, + f.file_path, c.connection_id, c.agent_conn_id FROM rcv_files r JOIN files f USING (file_id) LEFT JOIN connections c ON r.file_id = c.rcv_file_id + LEFT JOIN contacts cs USING (contact_id) + LEFT JOIN group_members m USING (group_member_id) WHERE f.user_id = ? AND f.file_id = ? |] (userId, fileId) where rcvFileTransfer :: - [(FileStatus, SMPQueueInfo, String, Integer, Integer, Maybe FilePath, Maybe Int64, Maybe ConnId)] -> + [(FileStatus, SMPQueueInfo, String, Integer, Integer, Maybe ContactName, Maybe ContactName, Maybe FilePath, Maybe Int64, Maybe ConnId)] -> Either StoreError RcvFileTransfer - rcvFileTransfer [(fileStatus', fileQInfo, fileName, fileSize, chunkSize, filePath_, connId_, agentConnId_)] = + rcvFileTransfer [(fileStatus', fileQInfo, fileName, fileSize, chunkSize, contactName_, memberName_, filePath_, connId_, agentConnId_)] = let fileInv = FileInvitation {fileName, fileSize, fileQInfo} fileInfo = (filePath_, connId_, agentConnId_) - in case fileStatus' of - FSNew -> Right RcvFileTransfer {fileId, fileInvitation = fileInv, fileStatus = RFSNew, chunkSize} - FSAccepted -> ft fileInv RFSAccepted fileInfo - FSConnected -> ft fileInv RFSConnected fileInfo - FSComplete -> ft fileInv RFSComplete fileInfo - FSCancelled -> ft fileInv RFSCancelled fileInfo + in case contactName_ <|> memberName_ of + Nothing -> Left $ SERcvFileInvalid fileId + Just name -> + case fileStatus' of + FSNew -> Right RcvFileTransfer {fileId, fileInvitation = fileInv, fileStatus = RFSNew, senderDisplayName = name, chunkSize} + FSAccepted -> ft name fileInv RFSAccepted fileInfo + FSConnected -> ft name fileInv RFSConnected fileInfo + FSComplete -> ft name fileInv RFSComplete fileInfo + FSCancelled -> ft name fileInv RFSCancelled fileInfo where - ft fileInvitation rfs = \case + ft senderDisplayName fileInvitation rfs = \case (Just filePath, Just connId, Just agentConnId) -> let fileStatus = rfs RcvFileInfo {filePath, connId, agentConnId} in Right RcvFileTransfer {..} @@ -1315,6 +1346,11 @@ updatedRcvFileChunkStored st RcvFileTransfer {fileId} chunkNo = |] (fileId, chunkNo) +deleteRcvFileChunks :: MonadUnliftIO m => SQLiteStore -> RcvFileTransfer -> m () +deleteRcvFileChunks st RcvFileTransfer {fileId} = + liftIO . withTransaction st $ \db -> + DB.execute db "DELETE FROM rcv_file_chunks WHERE file_id = ?" (Only fileId) + getFileTransfer :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> m FileTransfer getFileTransfer st userId fileId = liftIOEither . withTransaction st $ \db -> @@ -1354,18 +1390,24 @@ getSndFileTransfers_ db userId fileId = <$> DB.query db [sql| - SELECT s.file_status, f.file_name, f.file_size, f.chunk_size, f.file_path, s.connection_id, c.agent_conn_id + SELECT s.file_status, f.file_name, f.file_size, f.chunk_size, f.file_path, s.connection_id, c.agent_conn_id, + cs.local_display_name, m.local_display_name FROM snd_files s JOIN files f USING (file_id) JOIN connections c USING (connection_id) + LEFT JOIN contacts cs USING (contact_id) + LEFT JOIN group_members m USING (group_member_id) WHERE f.user_id = ? AND f.file_id = ? |] (userId, fileId) where - sndFileTransfers :: [(FileStatus, String, Integer, Integer, FilePath, Int64, ConnId)] -> Either StoreError [SndFileTransfer] + sndFileTransfers :: [(FileStatus, String, Integer, Integer, FilePath, Int64, ConnId, Maybe ContactName, Maybe ContactName)] -> Either StoreError [SndFileTransfer] sndFileTransfers [] = Left $ SESndFileNotFound fileId - sndFileTransfers fts = Right $ map sndFileTransfer fts - sndFileTransfer (fileStatus, fileName, fileSize, chunkSize, filePath, connId, agentConnId) = SndFileTransfer {..} + sndFileTransfers fts = mapM sndFileTransfer fts + sndFileTransfer (fileStatus, fileName, fileSize, chunkSize, filePath, connId, agentConnId, contactName_, memberName_) = + case contactName_ <|> memberName_ of + Just recipientDisplayName -> Right SndFileTransfer {..} + Nothing -> Left $ SESndFileInvalid fileId -- | Saves unique local display name based on passed displayName, suffixed with _N if required. -- This function should be called inside transaction. @@ -1432,6 +1474,7 @@ data StoreError | SEGroupAlreadyJoined | SEGroupInvitationNotFound | SESndFileNotFound Int64 + | SESndFileInvalid Int64 | SERcvFileNotFound Int64 | SEFileNotFound Int64 | SERcvFileInvalid Int64 diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index b73deab15b..ffd656a1ff 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -123,6 +123,7 @@ data ReceivedGroupInvitation = ReceivedGroupInvitation data GroupMember = GroupMember { groupMemberId :: Int64, + groupId :: Int64, memberId :: MemberId, memberRole :: GroupMemberRole, memberCategory :: GroupMemberCategory, @@ -305,6 +306,7 @@ data SndFileTransfer = SndFileTransfer filePath :: String, fileSize :: Integer, chunkSize :: Integer, + recipientDisplayName :: ContactName, connId :: Int64, agentConnId :: ConnId, fileStatus :: FileStatus @@ -322,6 +324,7 @@ data RcvFileTransfer = RcvFileTransfer { fileId :: Int64, fileInvitation :: FileInvitation, fileStatus :: RcvFileStatus, + senderDisplayName :: ContactName, chunkSize :: Integer } deriving (Eq, Show) @@ -343,7 +346,7 @@ data RcvFileInfo = RcvFileInfo data FileTransfer = FTSnd [SndFileTransfer] | FTRcv RcvFileTransfer -data FileStatus = FSNew | FSAccepted | FSConnected | FSComplete | FSCancelled deriving (Eq, Show) +data FileStatus = FSNew | FSAccepted | FSConnected | FSComplete | FSCancelled deriving (Eq, Ord, Show) instance FromField FileStatus where fromField = fromTextField_ fileStatusT diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 5139a94d9d..b2108eb791 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -25,11 +25,14 @@ module Simplex.Chat.View showSentMessage, showSentGroupMessage, showSentFileInvitation, + showSentGroupFileInvitation, + showSentFileInfo, showSndFileStart, showSndFileComplete, showSndFileCancelled, + showSndGroupFileCancelled, showSndFileRcvCancelled, - showReceivedFileInvitation, + receivedFileInvitation, showRcvFileAccepted, showRcvFileStart, showRcvFileComplete, @@ -58,6 +61,7 @@ module Simplex.Chat.View showContactUpdated, showMessageError, safeDecodeUtf8, + msgPlain, ) where @@ -65,7 +69,9 @@ import Control.Monad.IO.Unlift import Control.Monad.Reader import Data.ByteString.Char8 (ByteString) import Data.Composition ((.:), (.:.)) +import Data.Function (on) import Data.Int (Int64) +import Data.List (groupBy, intersperse, sortOn) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (DiffTime, UTCTime) @@ -123,13 +129,13 @@ showGroupRemoved = printToView . groupRemoved showMemberSubError :: ChatReader m => GroupName -> ContactName -> ChatError -> m () showMemberSubError = printToView .:. memberSubError -showReceivedMessage :: ChatReader m => ContactName -> UTCTime -> Text -> MsgIntegrity -> m () +showReceivedMessage :: ChatReader m => ContactName -> UTCTime -> [StyledString] -> MsgIntegrity -> m () showReceivedMessage = showReceivedMessage_ . ttyFromContact -showReceivedGroupMessage :: ChatReader m => GroupName -> ContactName -> UTCTime -> Text -> MsgIntegrity -> m () +showReceivedGroupMessage :: ChatReader m => GroupName -> ContactName -> UTCTime -> [StyledString] -> MsgIntegrity -> m () showReceivedGroupMessage = showReceivedMessage_ .: ttyFromGroup -showReceivedMessage_ :: ChatReader m => StyledString -> UTCTime -> Text -> MsgIntegrity -> m () +showReceivedMessage_ :: ChatReader m => StyledString -> UTCTime -> [StyledString] -> MsgIntegrity -> m () showReceivedMessage_ from utcTime msg mOk = printToView =<< liftIO (receivedMessage from utcTime msg mOk) showSentMessage :: ChatReader m => ContactName -> ByteString -> m () @@ -141,37 +147,46 @@ showSentGroupMessage = showSentMessage_ . ttyToGroup showSentMessage_ :: ChatReader m => StyledString -> ByteString -> m () showSentMessage_ to msg = printToView =<< liftIO (sentMessage to msg) -showSentFileInvitation :: ChatReader m => ContactName -> SndFileTransfer -> m () -showSentFileInvitation = printToView .: sentFileInvitation +showSentFileInvitation :: ChatReader m => ContactName -> FilePath -> m () +showSentFileInvitation = showSentFileInvitation_ . ttyToContact -showSndFileStart :: ChatReader m => Int64 -> m () +showSentGroupFileInvitation :: ChatReader m => GroupName -> FilePath -> m () +showSentGroupFileInvitation = showSentFileInvitation_ . ttyToGroup + +showSentFileInvitation_ :: ChatReader m => StyledString -> FilePath -> m () +showSentFileInvitation_ to filePath = printToView =<< liftIO (sentFileInvitation to filePath) + +showSentFileInfo :: ChatReader m => Int64 -> m () +showSentFileInfo = printToView . sentFileInfo + +showSndFileStart :: ChatReader m => SndFileTransfer -> m () showSndFileStart = printToView . sndFileStart -showSndFileComplete :: ChatReader m => Int64 -> m () +showSndFileComplete :: ChatReader m => SndFileTransfer -> m () showSndFileComplete = printToView . sndFileComplete -showSndFileCancelled :: ChatReader m => Int64 -> m () +showSndFileCancelled :: ChatReader m => SndFileTransfer -> m () showSndFileCancelled = printToView . sndFileCancelled -showSndFileRcvCancelled :: ChatReader m => Int64 -> m () +showSndGroupFileCancelled :: ChatReader m => [SndFileTransfer] -> m () +showSndGroupFileCancelled = printToView . sndGroupFileCancelled + +showSndFileRcvCancelled :: ChatReader m => SndFileTransfer -> m () showSndFileRcvCancelled = printToView . sndFileRcvCancelled -showReceivedFileInvitation :: ChatReader m => ContactName -> RcvFileTransfer -> m () -showReceivedFileInvitation = printToView .: receivedFileInvitation - -showRcvFileAccepted :: ChatReader m => Int64 -> FilePath -> m () +showRcvFileAccepted :: ChatReader m => RcvFileTransfer -> FilePath -> m () showRcvFileAccepted = printToView .: rcvFileAccepted -showRcvFileStart :: ChatReader m => Int64 -> m () +showRcvFileStart :: ChatReader m => RcvFileTransfer -> m () showRcvFileStart = printToView . rcvFileStart -showRcvFileComplete :: ChatReader m => Int64 -> m () +showRcvFileComplete :: ChatReader m => RcvFileTransfer -> m () showRcvFileComplete = printToView . rcvFileComplete -showRcvFileCancelled :: ChatReader m => Int64 -> m () +showRcvFileCancelled :: ChatReader m => RcvFileTransfer -> m () showRcvFileCancelled = printToView . rcvFileCancelled -showRcvFileSndCancelled :: ChatReader m => Int64 -> m () +showRcvFileSndCancelled :: ChatReader m => RcvFileTransfer -> m () showRcvFileSndCancelled = printToView . rcvFileSndCancelled showFileTransferStatus :: ChatReader m => (FileTransfer, [Integer]) -> m () @@ -409,10 +424,10 @@ contactUpdated messageError :: Text -> Text -> [StyledString] messageError prefix err = [plain prefix <> ": " <> plain err] -receivedMessage :: StyledString -> UTCTime -> Text -> MsgIntegrity -> IO [StyledString] +receivedMessage :: StyledString -> UTCTime -> [StyledString] -> MsgIntegrity -> IO [StyledString] receivedMessage from utcTime msg mOk = do t <- formatUTCTime <$> getCurrentTimeZone <*> getZonedTime - pure $ prependFirst (t <> " " <> from) (msgPlain msg) ++ showIntegrity mOk + pure $ prependFirst (t <> " " <> from) msg ++ showIntegrity mOk where formatUTCTime :: TimeZone -> ZonedTime -> StyledString formatUTCTime localTz currentTime = @@ -436,9 +451,15 @@ receivedMessage from utcTime msg mOk = do msgError s = [styled (Colored Red) s] sentMessage :: StyledString -> ByteString -> IO [StyledString] -sentMessage to msg = do +sentMessage to msg = sendWithTime_ to . msgPlain $ safeDecodeUtf8 msg + +sentFileInvitation :: StyledString -> FilePath -> IO [StyledString] +sentFileInvitation to f = sendWithTime_ ("/f " <> to) [ttyFilePath f] + +sendWithTime_ :: StyledString -> [StyledString] -> IO [StyledString] +sendWithTime_ to styledMsg = do time <- formatTime defaultTimeLocale "%H:%M" <$> getZonedTime - pure $ prependFirst (styleTime time <> " " <> to) (msgPlain $ safeDecodeUtf8 msg) + pure $ prependFirst (styleTime time <> " " <> to) styledMsg prependFirst :: StyledString -> [StyledString] -> [StyledString] prependFirst s [] = [s] @@ -447,27 +468,39 @@ prependFirst s (s' : ss) = (s <> s') : ss msgPlain :: Text -> [StyledString] msgPlain = map styleMarkdownText . T.lines -sentFileInvitation :: ContactName -> SndFileTransfer -> [StyledString] -sentFileInvitation cName SndFileTransfer {fileId, fileName} = - [ "offered to send the file " <> plain fileName <> " to " <> ttyContact cName, - "use " <> highlight ("/fc " <> show fileId) <> " to cancel sending" - ] +sentFileInfo :: Int64 -> [StyledString] +sentFileInfo fileId = + ["use " <> highlight ("/fc " <> show fileId) <> " to cancel sending"] -sndFileStart :: Int64 -> [StyledString] -sndFileStart fileId = ["started sending the file " <> sShow fileId] +sndFileStart :: SndFileTransfer -> [StyledString] +sndFileStart = sendingFile_ "started" -sndFileComplete :: Int64 -> [StyledString] -sndFileComplete fileId = ["completed sending the file " <> sShow fileId] +sndFileComplete :: SndFileTransfer -> [StyledString] +sndFileComplete = sendingFile_ "completed" -sndFileCancelled :: Int64 -> [StyledString] -sndFileCancelled fileId = ["cancelled sending the file " <> sShow fileId] +sndFileCancelled :: SndFileTransfer -> [StyledString] +sndFileCancelled = sendingFile_ "cancelled" -sndFileRcvCancelled :: Int64 -> [StyledString] -sndFileRcvCancelled fileId = ["recipient cancelled receiving the file " <> sShow fileId] +sndGroupFileCancelled :: [SndFileTransfer] -> [StyledString] +sndGroupFileCancelled fts = + case filter (\SndFileTransfer {fileStatus = s} -> s /= FSCancelled && s /= FSComplete) fts of + [] -> ["sending file can't be cancelled"] + ts@(ft : _) -> ["cancelled sending " <> sndFile ft <> " to " <> listMembers ts] -receivedFileInvitation :: ContactName -> RcvFileTransfer -> [StyledString] -receivedFileInvitation c RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName, fileSize}} = - [ ttyContact c <> " wants to send you the file " <> plain fileName <> " (" <> humanReadableSize fileSize <> " / " <> sShow fileSize <> " bytes)", +sendingFile_ :: StyledString -> SndFileTransfer -> [StyledString] +sendingFile_ status ft@SndFileTransfer {recipientDisplayName = c} = + [status <> " sending " <> sndFile ft <> " to " <> ttyContact c] + +sndFileRcvCancelled :: SndFileTransfer -> [StyledString] +sndFileRcvCancelled ft@SndFileTransfer {recipientDisplayName = c} = + [ttyContact c <> " cancelled receiving " <> sndFile ft] + +sndFile :: SndFileTransfer -> StyledString +sndFile SndFileTransfer {fileId, fileName} = fileTransfer fileId fileName + +receivedFileInvitation :: RcvFileTransfer -> [StyledString] +receivedFileInvitation RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName, fileSize}} = + [ "sends file " <> ttyFilePath fileName <> " (" <> humanReadableSize fileSize <> " / " <> sShow fileSize <> " bytes)", "use " <> highlight ("/fr " <> show fileId <> " [

/ | ]") <> " to receive it" ] @@ -483,41 +516,71 @@ humanReadableSize size mB = kB * 1024 gB = mB * 1024 -rcvFileAccepted :: Int64 -> FilePath -> [StyledString] -rcvFileAccepted fileId filePath = ["saving file " <> sShow fileId <> " to " <> plain filePath] +rcvFileAccepted :: RcvFileTransfer -> FilePath -> [StyledString] +rcvFileAccepted RcvFileTransfer {fileId, senderDisplayName = c} filePath = + ["saving file " <> sShow fileId <> " from " <> ttyContact c <> " to " <> plain filePath] -rcvFileStart :: Int64 -> [StyledString] -rcvFileStart fileId = ["started receiving the file " <> sShow fileId] +rcvFileStart :: RcvFileTransfer -> [StyledString] +rcvFileStart = receivingFile_ "started" -rcvFileComplete :: Int64 -> [StyledString] -rcvFileComplete fileId = ["completed receiving the file " <> sShow fileId] +rcvFileComplete :: RcvFileTransfer -> [StyledString] +rcvFileComplete = receivingFile_ "completed" -rcvFileCancelled :: Int64 -> [StyledString] -rcvFileCancelled fileId = ["cancelled receiving the file " <> sShow fileId] +rcvFileCancelled :: RcvFileTransfer -> [StyledString] +rcvFileCancelled = receivingFile_ "cancelled" -rcvFileSndCancelled :: Int64 -> [StyledString] -rcvFileSndCancelled fileId = ["sender cancelled sending the file " <> sShow fileId] +receivingFile_ :: StyledString -> RcvFileTransfer -> [StyledString] +receivingFile_ status ft@RcvFileTransfer {senderDisplayName = c} = + [status <> " receiving " <> rcvFile ft <> " from " <> ttyContact c] + +rcvFileSndCancelled :: RcvFileTransfer -> [StyledString] +rcvFileSndCancelled ft@RcvFileTransfer {senderDisplayName = c} = + [ttyContact c <> " cancelled sending " <> rcvFile ft] + +rcvFile :: RcvFileTransfer -> StyledString +rcvFile RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} = fileTransfer fileId fileName + +fileTransfer :: Int64 -> String -> StyledString +fileTransfer fileId fileName = "file " <> sShow fileId <> " (" <> ttyFilePath fileName <> ")" fileTransferStatus :: (FileTransfer, [Integer]) -> [StyledString] -fileTransferStatus (FTSnd [SndFileTransfer {fileStatus, fileSize, chunkSize}], chunksNum) = - ["sent file transfer " <> sndStatus] +fileTransferStatus (FTSnd [ft@SndFileTransfer {fileStatus, fileSize, chunkSize}], chunksNum) = + ["sending " <> sndFile ft <> " " <> sndStatus] where sndStatus = case fileStatus of - FSNew -> "is not accepted yet" + FSNew -> "not accepted yet" FSAccepted -> "just started" - FSConnected -> "progress: " <> fileProgress chunksNum chunkSize fileSize - FSComplete -> "is complete" - FSCancelled -> "is cancelled" -fileTransferStatus (FTSnd _fts, _chunks) = [] -- TODO group transfer -fileTransferStatus (FTRcv RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileSize}, fileStatus, chunkSize}, chunksNum) = - ["received file transfer " <> rcvStatus] + FSConnected -> "progress " <> fileProgress chunksNum chunkSize fileSize + FSComplete -> "complete" + FSCancelled -> "cancelled" +fileTransferStatus (FTSnd [], _) = ["no file transfers (empty group)"] +fileTransferStatus (FTSnd fts@(ft : _), chunksNum) = + case concatMap membersTransferStatus $ groupBy ((==) `on` fs) $ sortOn fs fts of + [membersStatus] -> ["sending " <> sndFile ft <> " " <> membersStatus] + membersStatuses -> ("sending " <> sndFile ft <> ": ") : map (" " <>) membersStatuses + where + fs = fileStatus :: SndFileTransfer -> FileStatus + membersTransferStatus [] = [] + membersTransferStatus ts@(SndFileTransfer {fileStatus, fileSize, chunkSize} : _) = [sndStatus <> ": " <> listMembers ts] + where + sndStatus = case fileStatus of + FSNew -> "not accepted" + FSAccepted -> "just started" + FSConnected -> "in progress (" <> sShow (sum chunksNum * chunkSize * 100 `div` (toInteger (length chunksNum) * fileSize)) <> "%)" + FSComplete -> "complete" + FSCancelled -> "cancelled" +fileTransferStatus (FTRcv ft@RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileSize}, fileStatus, chunkSize}, chunksNum) = + ["receiving " <> rcvFile ft <> " " <> rcvStatus] where rcvStatus = case fileStatus of - RFSNew -> "is not accepted yet, use " <> highlight ("/fr " <> show fileId) <> " to receive file" + RFSNew -> "not accepted yet, use " <> highlight ("/fr " <> show fileId) <> " to receive file" RFSAccepted _ -> "just started" - RFSConnected _ -> "progress: " <> fileProgress chunksNum chunkSize fileSize - RFSComplete RcvFileInfo {filePath} -> "is complete, path: " <> plain filePath - RFSCancelled RcvFileInfo {filePath} -> "is cancelled, received part path: " <> plain filePath + RFSConnected _ -> "progress " <> fileProgress chunksNum chunkSize fileSize + RFSComplete RcvFileInfo {filePath} -> "complete, path: " <> plain filePath + RFSCancelled RcvFileInfo {filePath} -> "cancelled, received part path: " <> plain filePath + +listMembers :: [SndFileTransfer] -> StyledString +listMembers = mconcat . intersperse ", " . map (ttyContact . recipientDisplayName) fileProgress :: [Integer] -> Integer -> Integer -> StyledString fileProgress chunksNum chunkSize fileSize = @@ -606,6 +669,9 @@ ttyFromGroup g c = styled (Colored Yellow) $ "#" <> g <> " " <> c <> "> " ttyToGroup :: GroupName -> StyledString ttyToGroup g = styled (Colored Cyan) $ "#" <> g <> " " +ttyFilePath :: FilePath -> StyledString +ttyFilePath = plain + optFullName :: ContactName -> Text -> StyledString optFullName localDisplayName fullName | T.null fullName || localDisplayName == fullName = "" diff --git a/stack.yaml b/stack.yaml index 9a58b8394f..83a08338b5 100644 --- a/stack.yaml +++ b/stack.yaml @@ -43,7 +43,7 @@ extra-deps: # - simplexmq-0.3.1@sha256:f247aaff3c16c5d3974a4ab4d5882ab50ac78073110997c0bceb05a74d10a325,6688 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 2ac903a2dd37c11a8612b19cd132cf43fe771fe4 + commit: e07bedac0e59346f076d71635ce6cd25de67ca7e # # extra-deps: [] diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 0afdf62efd..195b7227b4 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -44,6 +44,7 @@ chatTests = do it "send and receive file" testFileTransfer it "sender cancelled file transfer" testFileSndCancel it "recipient cancelled file transfer" testFileRcvCancel + it "send and receive file to group" testGroupFileTransfer testAddContact :: IO () testAddContact = @@ -410,10 +411,10 @@ testFileTransfer = concurrentlyN_ [ do bob #> "@alice receiving here..." - bob <## "completed receiving the file 1", + bob <## "completed receiving file 1 (test.jpg) from alice", do alice <# "bob> receiving here..." - alice <## "completed sending the file 1" + alice <## "completed sending file 1 (test.jpg) to bob" ] src <- B.readFile "./tests/fixtures/test.jpg" dest <- B.readFile "./tests/tmp/test.jpg" @@ -428,13 +429,13 @@ testFileSndCancel = alice ##> "/fc 1" concurrentlyN_ [ do - alice <## "cancelled sending the file 1" + alice <## "cancelled sending file 1 (test.jpg) to bob" alice ##> "/fs 1" - alice <## "sent file transfer is cancelled", + alice <## "sending file 1 (test.jpg) cancelled", do - bob <## "sender cancelled sending the file 1" + bob <## "alice cancelled sending file 1 (test.jpg)" bob ##> "/fs 1" - bob <## "received file transfer is cancelled, received part path: ./tests/tmp/test.jpg" + bob <## "receiving file 1 (test.jpg) cancelled, received part path: ./tests/tmp/test.jpg" ] checkPartialTransfer @@ -445,35 +446,77 @@ testFileRcvCancel = connectUsers alice bob startFileTransfer alice bob bob ##> "/fs 1" - getTermLine bob >>= (`shouldStartWith` "received file transfer progress:") + getTermLine bob >>= (`shouldStartWith` "receiving file 1 (test.jpg) progress") waitFileExists "./tests/tmp/test.jpg" bob ##> "/fc 1" concurrentlyN_ [ do - bob <## "cancelled receiving the file 1" + bob <## "cancelled receiving file 1 (test.jpg) from alice" bob ##> "/fs 1" - bob <## "received file transfer is cancelled, received part path: ./tests/tmp/test.jpg", + bob <## "receiving file 1 (test.jpg) cancelled, received part path: ./tests/tmp/test.jpg", do - alice <## "recipient cancelled receiving the file 1" + alice <## "bob cancelled receiving file 1 (test.jpg)" alice ##> "/fs 1" - alice <## "sent file transfer is cancelled" + alice <## "sending file 1 (test.jpg) cancelled" ] checkPartialTransfer where waitFileExists f = unlessM (doesFileExist f) $ waitFileExists f +testGroupFileTransfer :: IO () +testGroupFileTransfer = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + alice #> "/f #team ./tests/fixtures/test.jpg" + alice <## "use /fc 1 to cancel sending" + concurrentlyN_ + [ do + bob <# "#team alice> sends file test.jpg (136.5 KiB / 139737 bytes)" + bob <## "use /fr 1 [/ | ] to receive it", + do + cath <# "#team alice> sends file test.jpg (136.5 KiB / 139737 bytes)" + cath <## "use /fr 1 [/ | ] to receive it" + ] + alice ##> "/fs 1" + getTermLine alice >>= (`shouldStartWith` "sending file 1 (test.jpg) not accepted") + bob ##> "/fr 1 ./tests/tmp/" + bob <## "saving file 1 from alice to ./tests/tmp/test.jpg" + concurrentlyN_ + [ do + alice <## "started sending file 1 (test.jpg) to bob" + alice <## "completed sending file 1 (test.jpg) to bob" + alice ##> "/fs 1" + alice <## "sending file 1 (test.jpg):" + alice <### [" complete: bob", " not accepted: cath"], + do + bob <## "started receiving file 1 (test.jpg) from alice" + bob <## "completed receiving file 1 (test.jpg) from alice" + ] + cath ##> "/fr 1 ./tests/tmp/" + cath <## "saving file 1 from alice to ./tests/tmp/test_1.jpg" + concurrentlyN_ + [ do + alice <## "started sending file 1 (test.jpg) to cath" + alice <## "completed sending file 1 (test.jpg) to cath" + alice ##> "/fs 1" + getTermLine alice >>= (`shouldStartWith` "sending file 1 (test.jpg) complete"), + do + cath <## "started receiving file 1 (test.jpg) from alice" + cath <## "completed receiving file 1 (test.jpg) from alice" + ] + startFileTransfer :: TestCC -> TestCC -> IO () startFileTransfer alice bob = do - alice ##> "/f bob ./tests/fixtures/test.jpg" - alice <## "offered to send the file test.jpg to bob" + alice #> "/f @bob ./tests/fixtures/test.jpg" alice <## "use /fc 1 to cancel sending" - bob <## "alice wants to send you the file test.jpg (136.5 KiB / 139737 bytes)" + bob <# "alice> sends file test.jpg (136.5 KiB / 139737 bytes)" bob <## "use /fr 1 [/ | ] to receive it" bob ##> "/fr 1 ./tests/tmp" - bob <## "saving file 1 to ./tests/tmp/test.jpg" + bob <## "saving file 1 from alice to ./tests/tmp/test.jpg" concurrently_ - (bob <## "started receiving the file 1") - (alice <## "started sending the file 1") + (bob <## "started receiving file 1 (test.jpg) from alice") + (alice <## "started sending file 1 (test.jpg) to bob") checkPartialTransfer :: IO () checkPartialTransfer = do From fe5769156cec1e0da4c44d4e8828a646090fecde Mon Sep 17 00:00:00 2001 From: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> Date: Tue, 7 Sep 2021 01:08:29 +1000 Subject: [PATCH 26/29] correctly print both db files (#100) --- apps/simplex-chat/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index 952f77fd10..839bb873e6 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -21,7 +21,7 @@ welcomeGetOpts = do appDir <- getAppUserDataDirectory "simplex" opts@ChatOpts {dbFile} <- getChatOpts appDir putStrLn "SimpleX chat prototype v0.3.1" - putStrLn $ "db: " <> dbFile + putStrLn $ "db: " <> dbFile <> ".chat.db, " <> dbFile <> ".agent.db" putStrLn "type \"/help\" or \"/h\" for usage info" pure opts From 46cf31440382ee7284cf3c68719eb8e05b341b40 Mon Sep 17 00:00:00 2001 From: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> Date: Sat, 11 Sep 2021 22:45:22 +1000 Subject: [PATCH 27/29] update default smp servers (#101) * update default smp servers * add smp3 server --- src/Simplex/Chat/Options.hs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Simplex/Chat/Options.hs b/src/Simplex/Chat/Options.hs index 73538aa113..7d06873354 100644 --- a/src/Simplex/Chat/Options.hs +++ b/src/Simplex/Chat/Options.hs @@ -31,8 +31,17 @@ chatOpts appDir = ( long "server" <> short 's' <> metavar "SERVER" - <> help "SMP server(s) to use (smp1.simplex.im#pLdiGvm0jD1CMblnov6Edd/391OrYsShw+RgdfR0ChA=)" - <> value (L.fromList ["smp1.simplex.im#pLdiGvm0jD1CMblnov6Edd/391OrYsShw+RgdfR0ChA="]) + <> help + ( "SMP server(s) to use" + <> "\n(smp2.simplex.im#z5W2QLQ1Br3Yd6CoWg7bIq1bHdwK7Y8bEiEXBs/WfAg=," + <> "\nsmp3.simplex.im#nxc7HnrnM8dOKgkMp008ub/9o9LXJlxlMrMpR+mfMQw=)" + ) + <> value + ( L.fromList + [ "smp2.simplex.im#z5W2QLQ1Br3Yd6CoWg7bIq1bHdwK7Y8bEiEXBs/WfAg=", + "smp3.simplex.im#nxc7HnrnM8dOKgkMp008ub/9o9LXJlxlMrMpR+mfMQw=" + ] + ) ) where defaultDbFilePath = combine appDir "simplex" From 4360d348479bf0cd8c7cb68828e5c4543d233551 Mon Sep 17 00:00:00 2001 From: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> Date: Sun, 12 Sep 2021 02:00:59 +1000 Subject: [PATCH 28/29] update readme (#99) * update readme * case * fixes, roadmap * wording * features * update gifs and images * block warning * add link from announcement to groups * update default servers * update readme * update roadmap, image, etc. Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- .github/workflows/build.yml | 16 +++--- Dockerfile | 2 +- README.md | 94 +++++++++++++++++++++------------- images/connection.gif | Bin 0 -> 3490127 bytes images/file-transfer.gif | Bin 0 -> 1005555 bytes images/groups.gif | Bin 0 -> 1501748 bytes images/how-to-use-simplex.svg | 20 ++++---- images/simplex-chat.gif | Bin 6817999 -> 0 bytes images/strapline.svg | 3 -- src/Simplex/Chat/Help.hs | 10 ++-- 10 files changed, 82 insertions(+), 63 deletions(-) create mode 100644 images/connection.gif create mode 100644 images/file-transfer.gif create mode 100644 images/groups.gif delete mode 100644 images/simplex-chat.gif delete mode 100644 images/strapline.svg diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 336656be7c..e1ec517d74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,24 +50,24 @@ jobs: - os: ubuntu-20.04 cache_path: ~/.stack stack_args: "--test" - artifact_rel_path: /bin/dog-food - asset_name: dog-food-ubuntu-20_04-x86-64 + artifact_rel_path: /bin/simplex-chat + asset_name: simplex-chat-ubuntu-20_04-x86-64 - os: ubuntu-18.04 cache_path: ~/.stack stack_args: "--test" - artifact_rel_path: /bin/dog-food - asset_name: dog-food-ubuntu-18_04-x86-64 + artifact_rel_path: /bin/simplex-chat + asset_name: simplex-chat-ubuntu-18_04-x86-64 - os: macos-latest cache_path: ~/.stack stack_args: "--test" - artifact_rel_path: /bin/dog-food - asset_name: dog-food-macos-x86-64 + artifact_rel_path: /bin/simplex-chat + asset_name: simplex-chat-macos-x86-64 # TODO enable tests for windows once fixed (remove stack_args altogether) - os: windows-latest cache_path: C:/sr stack_args: "" - artifact_rel_path: /bin/dog-food.exe - asset_name: dog-food-windows-x86-64 + artifact_rel_path: /bin/simplex-chat.exe + asset_name: simplex-chat-windows-x86-64 steps: - name: Clone project uses: actions/checkout@v2 diff --git a/Dockerfile b/Dockerfile index a5d91ab2dc..9554d69fff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,4 +7,4 @@ WORKDIR /project RUN stack install FROM scratch AS export-stage -COPY --from=build-stage /root/.local/bin/dog-food / +COPY --from=build-stage /root/.local/bin/simplex-chat / diff --git a/README.md b/README.md index 93dd4f9e66..4eee8f65f3 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,21 @@ [![GitHub build](https://github.com/simplex-chat/simplex-chat/workflows/build/badge.svg)](https://github.com/simplex-chat/simplex-chat/actions?query=workflow%3Abuild) [![GitHub release](https://img.shields.io/github/v/release/simplex-chat/simplex-chat)](https://github.com/simplex-chat/simplex-chat/releases) +> **NEW in v0.4: [groups](#groups) and [sending files](#sending-files)!** + The motivation for SimpleX chat is [presented here](./simplex.md). SimpleX chat prototype is a thin terminal UI on top of [SimpleXMQ](https://github.com/simplex-chat/simplexmq) message broker that uses [SMP protocols](https://github.com/simplex-chat/simplexmq/blob/master/protocol). See [simplex.chat](https://simplex.chat) website for chat demo and the explanations of the system and how SMP protocol works. -![simplex-chat](./images/simplex-chat.gif) +![simplex-chat](./images/connection.gif) ## Table of contents - [Disclaimer](#disclaimer) - [Network topology](#network-topology) -- [Current features of the terminal chat](#current-features-of-the-terminal-chat) +- [Terminal chat features](#terminal-chat-features) - [Installation](#installation) - [Download chat client](#download-chat-client) - [Build from source](#build-from-source) @@ -28,8 +30,10 @@ See [simplex.chat](https://simplex.chat) website for chat demo and the explanati - [Usage](#usage) - [Running the chat client](#running-the-chat-client) - [How to use SimpleX chat](#how-to-use-simplex-chat) + - [Groups](#groups) + - [Sending files](#sending-files) - [Access chat history](#access-chat-history) -- [Roadmap](#roadmap) +- [Future roadmap](#future-roadmap) - [License](#license) ## Disclaimer @@ -50,11 +54,13 @@ Unlike federated networks, the participating server nodes do NOT have records of The routing of messages relies on the knowledge of client devices how user contacts and groups map at any given moment of time to these disposable queues on server nodes. -## Current features of the terminal chat +## Terminal chat features - 1-to-1 chat with multiple people in the same terminal window. +- Group messaging. +- Sending files to contacts and groups. - Auto-populated recipient name - just type your messages to reply to the sender once the connection is established. -- Demo SMP server available at `smp1.simplex.im:5223` - you can deploy your own server (`smp-server` executable in [simplexmq](https://github.com/simplex-chat/simplexmq) repo). +- Demo SMP servers available and pre-configured in the app - or you can [deploy your own server](https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent). - No global identity or any names visible to the server(s), ensuring full privacy of your contacts and conversations. - E2E encryption, with RSA public key that has to be passed out-of-band (see [How to use SimpleX chat](#how-to-use-simplex-chat)). - Message signing and verification with automatically generated RSA keys. @@ -62,7 +68,7 @@ The routing of messages relies on the knowledge of client devices how user conta - Authentication of each command/message by SMP servers with automatically generated RSA key pairs. - TCP transport encryption using SMP transport protocol. -RSA keys are not used as identity, they are randomly generated for each contact. 2048 bit keys are used, it can be changed to 4096-bit in code via [rsaKeySize setting](https://github.com/simplex-chat/simplex-chat/blob/master/apps/dog-food/Main.hs). +RSA keys are not used as identity, they are randomly generated for each contact. ## Installation @@ -74,7 +80,7 @@ Download the chat binary for your system from the [latest stable release](https: ```sh chmod +x -mv ~/.local/bin/dog-food +mv ~/.local/bin/simplex-chat ``` (or any other preferred location on PATH). @@ -84,7 +90,7 @@ On MacOS you also need to [allow Gatekeeper to run it](https://support.apple.com #### Windows ```sh -move %APPDATA%/local/bin/dog-food.exe +move %APPDATA%/local/bin/simplex-chat.exe ``` ### Build from source @@ -121,54 +127,72 @@ $ stack install ### Running the chat client -To start the chat client, run `dog-food` (as in [eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food)) from the terminal. +To start the chat client, run `simplex-chat` from the terminal. -By default, app data directory is created in the home directory (`~/.simplex`, or `%APPDATA%/simplex` on Windows), and SQLite database file `smp-chat.db` is initialized in it. +By default, app data directory is created in the home directory (`~/.simplex`, or `%APPDATA%/simplex` on Windows), and two SQLite database files `simplex.chat.db` and `simplex.agent.db` are initialized in it. -The default SMP server is `smp1.simplex.im#pLdiGvm0jD1CMblnov6Edd/391OrYsShw+RgdfR0ChA=` (base-64 encoded string after server host is the transport key digest) - it is pre-configured in the app. - -To specify a different file path for the chat database use `-d` command line option: +To specify a different file path prefix for the database files use `-d` command line option: ```shell -$ dog-food -d my-chat.db +$ simplex-chat -d alice ``` +Running above, for example, would create `alice.chat.db` and `alice.agent.db` database files in current directory. + +Default SMP servers are hosted on Linode (London, UK and Fremont, CA) - they are [pre-configured in the app](https://github.com/simplex-chat/simplex-chat/blob/master/src/Simplex/Chat/Options.hs#L40). Base-64 encoded string after server host is the transport key digest. + If you deployed your own SMP server(s) you can configure client via `-s` option: ```shell -$ dog-food -s smp.example.com:5223#KXNE1m2E1m0lm92WGKet9CL6+lO742Vy5G6nsrkvgs8= +$ simplex-chat -s smp.example.com:5223#KXNE1m2E1m0lm92WGKet9CL6+lO742Vy5G6nsrkvgs8= ``` The base-64 encoded string in server address is the digest of RSA transport handshake key that the server will generate on the first run and output its digest. You can still talk to people using default or any other server - it only affects the location of the message queue when you initiate the connection (and the reply queue can be on another server, as set by the other party's client). -Run `dog-food --help` to see all available options. +Run `simplex-chat -h` to see all available options. ### How to use SimpleX chat +Once you have started the chat, you will be prompted to specify your "display name" and an optional "full name" to create a local chat profile. Your display name is an alias for your contacts to refer to you by - it is not unique and does not serve as a global identity. In case different contacts chose the same display name, the chat client adds a numeric suffix to their local display names. + This diagram shows how to connect and message a contact:
-Once you have started the chat, use `/add ` to create a new connection and generate an invitation (`` is any name you want to use for that contact). The add command will output an invitation. Send this invitation to your contact via any other channel. +Once you've set up your local profile, enter `/c` (for `/connect`) to create a new connection and generate an invitation. Send this invitation to your contact via any other channel. The invitation has the format `smp::::::`. The invitation can only be used once and even if this is intercepted, the attacker would not be able to use it to send you the messages via this queue once your contact confirms that the connection is established. -The contact who received the invitation should use `/connect ` to accept the connection (`` is any name that the accepting contact wants to use for you). +The contact who received the invitation should enter `/c ` to accept the connection. This establishes the connection, and both parties are notified. -Once the contact has used the `/connect` command, a connection is established and both parties are notified. - -They would then use `@ ` commands to send messages. One may also press Space or just start typing a message to send a message to the contact that was the last. - -If your contact is disconnected, restart the chat client - it may happen if you lose internet connection. +They would then use `@ ` commands to send messages. You may also just start typing a message to send it to the contact that was the last. Use `/help` in chat to see the list of available commands. +### Groups + +To create a group use `/g `, the add contacts to it with `/a `and send messages with `# `. Use `/help groups` for other commands. + +![simplex-chat](./images/groups.gif) + +> **Please note**: the groups are not stored on any server, they are maintained as a list of members in the app database to whom the messages will be sent. + +### Sending files + +You can send a file to your contact with `/f @ ` - the recipient will have to accept it before it is sent. Use `/help files` for other commands. + +![simplex-chat](./images/file-transfer.gif) + +You can send files to a group with `/f # `. + ### Access chat history +> 🚧 **Section currently out of date - will be updated soon** 🏗 + SimpleX chat stores all your contacts and conversations in a local database file, making it private and portable by design, fully owned and controlled by you. You can search your chat history via SQLite database file: @@ -188,20 +212,18 @@ order by internal_id desc; > **Please note:** SQLite foreign key constraints are disabled by default, and must be **[enabled separately for each database connection](https://sqlite.org/foreignkeys.html#fk_enable)**. The latter can be achieved by running `PRAGMA foreign_keys = ON;` command on an open database connection. By running data altering queries without enabling foreign keys prior to that, you may risk putting your database in an inconsistent state. -## Roadmap +## Future roadmap -The consumer ready system will have these parts implemented: - -1. Application level chat protocol. This will allow to separate physical server connection management from logical chat contacts, and to support all common chat functions. Currently in progress in [v4 branch](https://github.com/simplex-chat/simplex-chat/tree/v4). -2. Symmetric groups support in SMP agent protocol, as a foundation for chat groups. -3. SMP queue redundancy and rotation in SMP agent protocol. -4. Message delivery confirmation in SMP agent protocol. -5. Multi-agent/device data synchronization - to use chat on multiple devices. -6. Synchronous streams support in SMP and SMP agent protocols, to support file transfer. -7. Desktop and mobile apps. -8. Scripts for simple SMP server deployment to hosting providers: Linode ([done](https://github.com/simplex-chat/simplexmq#deploy-smp-server-on-linode)), Digital Ocean and Heroku. -9. Public broadcast channels. -10. Optional public contact/group addresses using DNS-based contact addresses (like email) to establish connections, but not using it to route messages - in this way you will keep all your contacts and groups even if you lose the control of the domain. +1. Mobile and desktop apps (in progress). +2. SMP protocol improvements: + - SMP queue redundancy and rotation. + - Message delivery confirmation. + - Support multiple devices. +3. Privacy-preserving identity server for optional DNS-based contact/group addresses to simplify connection and discovery, but not used to deliver messages: + - keep all your contacts and groups even if you lose the domain. + - the server doesn't have information about your contacts and groups. +4. Media server to optimize sending large files to groups. +5. Channels server for large groups and broadcast channels. ## License diff --git a/images/connection.gif b/images/connection.gif new file mode 100644 index 0000000000000000000000000000000000000000..325925e99c95e657056436c21563518071f374fb GIT binary patch literal 3490127 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 UREB<&ubn0Q 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

y@0aQ>>lky8*nEhtDzbHA^~x+zg*Q&-cFWN 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

>-G)C%JEFFBd9G^Xu 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 z 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

`#5^m;pWS&An}-F7w*8L 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

cVAFCd-Ba;)s8$HT5^he*{uZ1pZ(%}CN$&s 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

jJ$?0~bdZ$~u|LI*T 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�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

#hAt=gO>|}-ptCf*$rJ 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

#7YetKq&Eu2uiF6@!kC91`< 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�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 z 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