mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 04:39:53 +00:00
core: verify connection (#1530)
* core: verify connection * update commands * api to get/set verification code/status * add migration * refactor * change command / response names * reset verified status if code from agent doesn't match
This commit is contained in:
parent
ab5ae2d2cb
commit
95cc9e1e55
12 changed files with 245 additions and 60 deletions
|
@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/simplex-chat/simplexmq.git
|
location: https://github.com/simplex-chat/simplexmq.git
|
||||||
tag: e4842f4f47fb60ef8843dbce6fd43dec96f157d2
|
tag: fb21d9836e07706c7498baa967f932cb11b818e5
|
||||||
|
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"https://github.com/simplex-chat/simplexmq.git"."e4842f4f47fb60ef8843dbce6fd43dec96f157d2" = "191h2v5jcn51haj0mi5y5pm8x8fi9pz49ydxwwzqr3m6zjp21ngg";
|
"https://github.com/simplex-chat/simplexmq.git"."fb21d9836e07706c7498baa967f932cb11b818e5" = "0dl08ag38d1azzil1xxi6xrzqwfcv550wi5kjdmxn4h820icl2ja";
|
||||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
|
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
|
||||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
|
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
|
||||||
"https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";
|
"https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";
|
||||||
|
|
|
@ -65,6 +65,7 @@ library
|
||||||
Simplex.Chat.Migrations.M20221115_server_cfg
|
Simplex.Chat.Migrations.M20221115_server_cfg
|
||||||
Simplex.Chat.Migrations.M20221129_delete_group_feature_items
|
Simplex.Chat.Migrations.M20221129_delete_group_feature_items
|
||||||
Simplex.Chat.Migrations.M20221130_delete_item_deleted
|
Simplex.Chat.Migrations.M20221130_delete_item_deleted
|
||||||
|
Simplex.Chat.Migrations.M20221209_verified_connection
|
||||||
Simplex.Chat.Mobile
|
Simplex.Chat.Mobile
|
||||||
Simplex.Chat.Options
|
Simplex.Chat.Options
|
||||||
Simplex.Chat.ProfileGenerator
|
Simplex.Chat.ProfileGenerator
|
||||||
|
|
|
@ -758,24 +758,53 @@ processChatCommand = \case
|
||||||
case memberConnId m of
|
case memberConnId m of
|
||||||
Just connId -> withAgent (\a -> switchConnectionAsync a "" connId) $> CRCmdOk
|
Just connId -> withAgent (\a -> switchConnectionAsync a "" connId) $> CRCmdOk
|
||||||
_ -> throwChatError CEGroupMemberNotActive
|
_ -> throwChatError CEGroupMemberNotActive
|
||||||
|
APIGetContactCode contactId -> withUser $ \user -> do
|
||||||
|
ct@Contact {activeConn = conn@Connection {connId}} <- withStore $ \db -> getContact db user contactId
|
||||||
|
code <- getConnectionCode (contactConnId ct)
|
||||||
|
ct' <- case contactSecurityCode ct of
|
||||||
|
Just SecurityCode {securityCode}
|
||||||
|
| sameVerificationCode code securityCode -> pure ct
|
||||||
|
| otherwise -> do
|
||||||
|
withStore' $ \db -> setConnectionVerified db user connId Nothing
|
||||||
|
pure (ct :: Contact) {activeConn = conn {connectionCode = Nothing}}
|
||||||
|
_ -> pure ct
|
||||||
|
pure $ CRContactCode ct' code
|
||||||
|
APIGetGroupMemberCode gId gMemberId -> withUser $ \user -> do
|
||||||
|
(g, m@GroupMember {activeConn}) <- withStore $ \db -> (,) <$> getGroupInfo db user gId <*> getGroupMember db user gId gMemberId
|
||||||
|
case activeConn of
|
||||||
|
Just conn@Connection {connId} -> do
|
||||||
|
code <- getConnectionCode $ aConnId conn
|
||||||
|
m' <- case memberSecurityCode m of
|
||||||
|
Just SecurityCode {securityCode}
|
||||||
|
| sameVerificationCode code securityCode -> pure m
|
||||||
|
| otherwise -> do
|
||||||
|
withStore' $ \db -> setConnectionVerified db user connId Nothing
|
||||||
|
pure (m :: GroupMember) {activeConn = Just $ (conn :: Connection) {connectionCode = Nothing}}
|
||||||
|
_ -> pure m
|
||||||
|
pure $ CRGroupMemberCode g m' code
|
||||||
|
_ -> throwChatError CEGroupMemberNotActive
|
||||||
|
APIVerifyContact contactId code -> withUser $ \user -> do
|
||||||
|
Contact {activeConn} <- withStore $ \db -> getContact db user contactId
|
||||||
|
verifyConnectionCode user activeConn code
|
||||||
|
APIVerifyGroupMember gId gMemberId code -> withUser $ \user -> do
|
||||||
|
GroupMember {activeConn} <- withStore $ \db -> getGroupMember db user gId gMemberId
|
||||||
|
case activeConn of
|
||||||
|
Just conn -> verifyConnectionCode user conn code
|
||||||
|
_ -> throwChatError CEGroupMemberNotActive
|
||||||
ShowMessages (ChatName cType name) ntfOn -> withUser $ \user -> do
|
ShowMessages (ChatName cType name) ntfOn -> withUser $ \user -> do
|
||||||
chatId <- case cType of
|
chatId <- case cType of
|
||||||
CTDirect -> withStore $ \db -> getContactIdByName db user name
|
CTDirect -> withStore $ \db -> getContactIdByName db user name
|
||||||
CTGroup -> withStore $ \db -> getGroupIdByName db user name
|
CTGroup -> withStore $ \db -> getGroupIdByName db user name
|
||||||
_ -> throwChatError $ CECommandError "not supported"
|
_ -> throwChatError $ CECommandError "not supported"
|
||||||
processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ ChatSettings ntfOn
|
processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ ChatSettings ntfOn
|
||||||
ContactInfo cName -> withUser $ \user -> do
|
ContactInfo cName -> withContactName cName APIContactInfo
|
||||||
contactId <- withStore $ \db -> getContactIdByName db user cName
|
GroupMemberInfo gName mName -> withMemberName gName mName APIGroupMemberInfo
|
||||||
processChatCommand $ APIContactInfo contactId
|
SwitchContact cName -> withContactName cName APISwitchContact
|
||||||
GroupMemberInfo gName mName -> withUser $ \user -> do
|
SwitchGroupMember gName mName -> withMemberName gName mName APISwitchGroupMember
|
||||||
(gId, mId) <- withStore $ \db -> getGroupIdByName db user gName >>= \gId -> (gId,) <$> getGroupMemberIdByName db user gId mName
|
GetContactCode cName -> withContactName cName APIGetContactCode
|
||||||
processChatCommand $ APIGroupMemberInfo gId mId
|
GetGroupMemberCode gName mName -> withMemberName gName mName APIGetGroupMemberCode
|
||||||
SwitchContact cName -> withUser $ \user -> do
|
VerifyContact cName code -> withContactName cName (`APIVerifyContact` code)
|
||||||
contactId <- withStore $ \db -> getContactIdByName db user cName
|
VerifyGroupMember gName mName code -> withMemberName gName mName $ \gId mId -> APIVerifyGroupMember gId mId code
|
||||||
processChatCommand $ APISwitchContact contactId
|
|
||||||
SwitchGroupMember gName mName -> withUser $ \user -> do
|
|
||||||
(gId, mId) <- withStore $ \db -> getGroupIdByName db user gName >>= \gId -> (gId,) <$> getGroupMemberIdByName db user gId mName
|
|
||||||
processChatCommand $ APISwitchGroupMember gId mId
|
|
||||||
ChatHelp section -> pure $ CRChatHelp section
|
ChatHelp section -> pure $ CRChatHelp section
|
||||||
Welcome -> withUser $ pure . CRWelcome
|
Welcome -> withUser $ pure . CRWelcome
|
||||||
AddContact -> withUser $ \User {userId} -> withChatLock "addContact" . procCmd $ do
|
AddContact -> withUser $ \User {userId} -> withChatLock "addContact" . procCmd $ do
|
||||||
|
@ -802,12 +831,8 @@ processChatCommand = \case
|
||||||
ConnectSimplex -> withUser $ \user ->
|
ConnectSimplex -> withUser $ \user ->
|
||||||
-- [incognito] generate profile to send
|
-- [incognito] generate profile to send
|
||||||
connectViaContact user adminContactReq
|
connectViaContact user adminContactReq
|
||||||
DeleteContact cName -> withUser $ \user -> do
|
DeleteContact cName -> withContactName cName $ APIDeleteChat . ChatRef CTDirect
|
||||||
contactId <- withStore $ \db -> getContactIdByName db user cName
|
ClearContact cName -> withContactName cName $ APIClearChat . ChatRef CTDirect
|
||||||
processChatCommand $ APIDeleteChat (ChatRef CTDirect contactId)
|
|
||||||
ClearContact cName -> withUser $ \user -> do
|
|
||||||
contactId <- withStore $ \db -> getContactIdByName db user cName
|
|
||||||
processChatCommand $ APIClearChat (ChatRef CTDirect contactId)
|
|
||||||
ListContacts -> withUser $ \user -> CRContactsList <$> withStore' (`getUserContacts` user)
|
ListContacts -> withUser $ \user -> CRContactsList <$> withStore' (`getUserContacts` user)
|
||||||
CreateMyAddress -> withUser $ \User {userId} -> withChatLock "createMyAddress" . procCmd $ do
|
CreateMyAddress -> withUser $ \User {userId} -> withChatLock "createMyAddress" . procCmd $ do
|
||||||
(connId, cReq) <- withAgent $ \a -> createConnection a True SCMContact Nothing
|
(connId, cReq) <- withAgent $ \a -> createConnection a True SCMContact Nothing
|
||||||
|
@ -977,12 +1002,8 @@ processChatCommand = \case
|
||||||
JoinGroup gName -> withUser $ \user -> do
|
JoinGroup gName -> withUser $ \user -> do
|
||||||
groupId <- withStore $ \db -> getGroupIdByName db user gName
|
groupId <- withStore $ \db -> getGroupIdByName db user gName
|
||||||
processChatCommand $ APIJoinGroup groupId
|
processChatCommand $ APIJoinGroup groupId
|
||||||
MemberRole gName groupMemberName memRole -> do
|
MemberRole gName gMemberName memRole -> withMemberName gName gMemberName $ \gId gMemberId -> APIMemberRole gId gMemberId memRole
|
||||||
(groupId, groupMemberId) <- getGroupAndMemberId gName groupMemberName
|
RemoveMember gName gMemberName -> withMemberName gName gMemberName APIRemoveMember
|
||||||
processChatCommand $ APIMemberRole groupId groupMemberId memRole
|
|
||||||
RemoveMember gName groupMemberName -> do
|
|
||||||
(groupId, groupMemberId) <- getGroupAndMemberId gName groupMemberName
|
|
||||||
processChatCommand $ APIRemoveMember groupId groupMemberId
|
|
||||||
LeaveGroup gName -> withUser $ \user -> do
|
LeaveGroup gName -> withUser $ \user -> do
|
||||||
groupId <- withStore $ \db -> getGroupIdByName db user gName
|
groupId <- withStore $ \db -> getGroupIdByName db user gName
|
||||||
processChatCommand $ APILeaveGroup groupId
|
processChatCommand $ APILeaveGroup groupId
|
||||||
|
@ -1135,6 +1156,20 @@ processChatCommand = \case
|
||||||
withStoreChanged a = checkChatStopped $ a >> setStoreChanged $> CRCmdOk
|
withStoreChanged a = checkChatStopped $ a >> setStoreChanged $> CRCmdOk
|
||||||
checkStoreNotChanged :: m ChatResponse -> m ChatResponse
|
checkStoreNotChanged :: m ChatResponse -> m ChatResponse
|
||||||
checkStoreNotChanged = ifM (asks chatStoreChanged >>= readTVarIO) (throwChatError CEChatStoreChanged)
|
checkStoreNotChanged = ifM (asks chatStoreChanged >>= readTVarIO) (throwChatError CEChatStoreChanged)
|
||||||
|
withContactName :: ContactName -> (ContactId -> ChatCommand) -> m ChatResponse
|
||||||
|
withContactName cName cmd = withUser $ \user ->
|
||||||
|
withStore (\db -> getContactIdByName db user cName) >>= processChatCommand . cmd
|
||||||
|
withMemberName :: GroupName -> ContactName -> (GroupId -> GroupMemberId -> ChatCommand) -> m ChatResponse
|
||||||
|
withMemberName gName mName cmd = withUser $ \user ->
|
||||||
|
getGroupAndMemberId user gName mName >>= processChatCommand . uncurry cmd
|
||||||
|
getConnectionCode :: ConnId -> m Text
|
||||||
|
getConnectionCode connId = verificationCode <$> withAgent (`getConnectionRatchetAdHash` connId)
|
||||||
|
verifyConnectionCode :: User -> Connection -> Text -> m ChatResponse
|
||||||
|
verifyConnectionCode user conn@Connection {connId} code = do
|
||||||
|
code' <- getConnectionCode $ aConnId conn
|
||||||
|
let verified = sameVerificationCode code code'
|
||||||
|
when verified . withStore' $ \db -> setConnectionVerified db user connId $ Just code'
|
||||||
|
pure $ CRCodeVerification verified code'
|
||||||
getSentChatItemIdByText :: User -> ChatRef -> ByteString -> m Int64
|
getSentChatItemIdByText :: User -> ChatRef -> ByteString -> m Int64
|
||||||
getSentChatItemIdByText user@User {userId, localDisplayName} (ChatRef cType cId) msg = case cType of
|
getSentChatItemIdByText user@User {userId, localDisplayName} (ChatRef cType cId) msg = case cType of
|
||||||
CTDirect -> withStore $ \db -> getDirectChatItemIdByText db userId cId SMDSnd (safeDecodeUtf8 msg)
|
CTDirect -> withStore $ \db -> getDirectChatItemIdByText db userId cId SMDSnd (safeDecodeUtf8 msg)
|
||||||
|
@ -1253,8 +1288,8 @@ processChatCommand = \case
|
||||||
_ -> throwChatError CEFileNotReceived {fileId}
|
_ -> throwChatError CEFileNotReceived {fileId}
|
||||||
where
|
where
|
||||||
forward = processChatCommand . sendCommand chatName
|
forward = processChatCommand . sendCommand chatName
|
||||||
getGroupAndMemberId :: GroupName -> ContactName -> m (GroupId, GroupMemberId)
|
getGroupAndMemberId :: User -> GroupName -> ContactName -> m (GroupId, GroupMemberId)
|
||||||
getGroupAndMemberId gName groupMemberName = withUser $ \user -> do
|
getGroupAndMemberId user gName groupMemberName =
|
||||||
withStore $ \db -> do
|
withStore $ \db -> do
|
||||||
groupId <- getGroupIdByName db user gName
|
groupId <- getGroupIdByName db user gName
|
||||||
groupMemberId <- getGroupMemberIdByName db user groupId groupMemberName
|
groupMemberId <- getGroupMemberIdByName db user groupId groupMemberName
|
||||||
|
@ -3391,6 +3426,14 @@ chatCommandP =
|
||||||
"/_switch @" *> (APISwitchContact <$> A.decimal),
|
"/_switch @" *> (APISwitchContact <$> A.decimal),
|
||||||
"/switch #" *> (SwitchGroupMember <$> displayName <* A.space <* optional (A.char '@') <*> displayName),
|
"/switch #" *> (SwitchGroupMember <$> displayName <* A.space <* optional (A.char '@') <*> displayName),
|
||||||
("/switch @" <|> "/switch ") *> (SwitchContact <$> displayName),
|
("/switch @" <|> "/switch ") *> (SwitchContact <$> displayName),
|
||||||
|
"/_get code @" *> (APIGetContactCode <$> A.decimal),
|
||||||
|
"/_get code #" *> (APIGetGroupMemberCode <$> A.decimal <* A.space <*> A.decimal),
|
||||||
|
"/_verify code @" *> (APIVerifyContact <$> A.decimal <* A.space <*> textP),
|
||||||
|
"/_verify code @" *> (APIVerifyGroupMember <$> A.decimal <* A.space <*> A.decimal <* A.space <*> textP),
|
||||||
|
("/code @" <|> "/code ") *> (GetContactCode <$> displayName),
|
||||||
|
"/code #" *> (GetGroupMemberCode <$> displayName <* A.space <* optional (A.char '@') <*> displayName),
|
||||||
|
("/verify @" <|> "/verify ") *> (VerifyContact <$> displayName <* A.space <*> textP),
|
||||||
|
"/verify #" *> (VerifyGroupMember <$> displayName <* A.space <* optional (A.char '@') <*> displayName <* A.space <*> textP),
|
||||||
("/help files" <|> "/help file" <|> "/hf") $> ChatHelp HSFiles,
|
("/help files" <|> "/help file" <|> "/hf") $> ChatHelp HSFiles,
|
||||||
("/help groups" <|> "/help group" <|> "/hg") $> ChatHelp HSGroups,
|
("/help groups" <|> "/help group" <|> "/hg") $> ChatHelp HSGroups,
|
||||||
("/help address" <|> "/ha") $> ChatHelp HSMyAddress,
|
("/help address" <|> "/ha") $> ChatHelp HSMyAddress,
|
||||||
|
|
|
@ -200,11 +200,19 @@ data ChatCommand
|
||||||
| APIGroupMemberInfo GroupId GroupMemberId
|
| APIGroupMemberInfo GroupId GroupMemberId
|
||||||
| APISwitchContact ContactId
|
| APISwitchContact ContactId
|
||||||
| APISwitchGroupMember GroupId GroupMemberId
|
| APISwitchGroupMember GroupId GroupMemberId
|
||||||
|
| APIGetContactCode ContactId
|
||||||
|
| APIGetGroupMemberCode GroupId GroupMemberId
|
||||||
|
| APIVerifyContact ContactId Text
|
||||||
|
| APIVerifyGroupMember GroupId GroupMemberId Text
|
||||||
| ShowMessages ChatName Bool
|
| ShowMessages ChatName Bool
|
||||||
| ContactInfo ContactName
|
| ContactInfo ContactName
|
||||||
| GroupMemberInfo GroupName ContactName
|
| GroupMemberInfo GroupName ContactName
|
||||||
| SwitchContact ContactName
|
| SwitchContact ContactName
|
||||||
| SwitchGroupMember GroupName ContactName
|
| SwitchGroupMember GroupName ContactName
|
||||||
|
| GetContactCode ContactName
|
||||||
|
| GetGroupMemberCode GroupName ContactName
|
||||||
|
| VerifyContact ContactName Text
|
||||||
|
| VerifyGroupMember GroupName ContactName Text
|
||||||
| ChatHelp HelpSection
|
| ChatHelp HelpSection
|
||||||
| Welcome
|
| Welcome
|
||||||
| AddContact
|
| AddContact
|
||||||
|
@ -276,6 +284,9 @@ data ChatResponse
|
||||||
| CRGroupMemberInfo {groupInfo :: GroupInfo, member :: GroupMember, connectionStats_ :: Maybe ConnectionStats}
|
| CRGroupMemberInfo {groupInfo :: GroupInfo, member :: GroupMember, connectionStats_ :: Maybe ConnectionStats}
|
||||||
| CRContactSwitch {contact :: Contact, switchProgress :: SwitchProgress}
|
| CRContactSwitch {contact :: Contact, switchProgress :: SwitchProgress}
|
||||||
| CRGroupMemberSwitch {groupInfo :: GroupInfo, member :: GroupMember, switchProgress :: SwitchProgress}
|
| CRGroupMemberSwitch {groupInfo :: GroupInfo, member :: GroupMember, switchProgress :: SwitchProgress}
|
||||||
|
| CRContactCode {contact :: Contact, connectionCode :: Text}
|
||||||
|
| CRGroupMemberCode {groupInfo :: GroupInfo, member :: GroupMember, connectionCode :: Text}
|
||||||
|
| CRCodeVerification {verified :: Bool, expectedCode :: Text}
|
||||||
| CRNewChatItem {chatItem :: AChatItem}
|
| CRNewChatItem {chatItem :: AChatItem}
|
||||||
| CRChatItemStatusUpdated {chatItem :: AChatItem}
|
| CRChatItemStatusUpdated {chatItem :: AChatItem}
|
||||||
| CRChatItemUpdated {chatItem :: AChatItem}
|
| CRChatItemUpdated {chatItem :: AChatItem}
|
||||||
|
|
13
src/Simplex/Chat/Migrations/M20221209_verified_connection.hs
Normal file
13
src/Simplex/Chat/Migrations/M20221209_verified_connection.hs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
|
module Simplex.Chat.Migrations.M20221209_verified_connection where
|
||||||
|
|
||||||
|
import Database.SQLite.Simple (Query)
|
||||||
|
import Database.SQLite.Simple.QQ (sql)
|
||||||
|
|
||||||
|
m20221209_verified_connection :: Query
|
||||||
|
m20221209_verified_connection =
|
||||||
|
[sql|
|
||||||
|
ALTER TABLE connections ADD COLUMN security_code TEXT NULL;
|
||||||
|
ALTER TABLE connections ADD COLUMN security_code_verified_at TEXT NULL;
|
||||||
|
|]
|
|
@ -258,6 +258,8 @@ CREATE TABLE connections(
|
||||||
local_alias DEFAULT '' CHECK(local_alias NOT NULL),
|
local_alias DEFAULT '' CHECK(local_alias NOT NULL),
|
||||||
via_group_link INTEGER DEFAULT 0 CHECK(via_group_link NOT NULL),
|
via_group_link INTEGER DEFAULT 0 CHECK(via_group_link NOT NULL),
|
||||||
group_link_id BLOB,
|
group_link_id BLOB,
|
||||||
|
security_code TEXT NULL,
|
||||||
|
security_code_verified_at TEXT NULL,
|
||||||
FOREIGN KEY(snd_file_id, connection_id)
|
FOREIGN KEY(snd_file_id, connection_id)
|
||||||
REFERENCES snd_files(file_id, connection_id)
|
REFERENCES snd_files(file_id, connection_id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
|
|
|
@ -47,6 +47,7 @@ module Simplex.Chat.Store
|
||||||
updateContactUsed,
|
updateContactUsed,
|
||||||
updateContactUnreadChat,
|
updateContactUnreadChat,
|
||||||
updateGroupUnreadChat,
|
updateGroupUnreadChat,
|
||||||
|
setConnectionVerified,
|
||||||
getUserContacts,
|
getUserContacts,
|
||||||
getUserContactProfiles,
|
getUserContactProfiles,
|
||||||
createUserContactLink,
|
createUserContactLink,
|
||||||
|
@ -304,6 +305,7 @@ import Simplex.Chat.Migrations.M20221112_server_password
|
||||||
import Simplex.Chat.Migrations.M20221115_server_cfg
|
import Simplex.Chat.Migrations.M20221115_server_cfg
|
||||||
import Simplex.Chat.Migrations.M20221129_delete_group_feature_items
|
import Simplex.Chat.Migrations.M20221129_delete_group_feature_items
|
||||||
import Simplex.Chat.Migrations.M20221130_delete_item_deleted
|
import Simplex.Chat.Migrations.M20221130_delete_item_deleted
|
||||||
|
import Simplex.Chat.Migrations.M20221209_verified_connection
|
||||||
import Simplex.Chat.Protocol
|
import Simplex.Chat.Protocol
|
||||||
import Simplex.Chat.Types
|
import Simplex.Chat.Types
|
||||||
import Simplex.Messaging.Agent.Protocol (ACorrId, AgentMsgId, ConnId, InvitationId, MsgMeta (..))
|
import Simplex.Messaging.Agent.Protocol (ACorrId, AgentMsgId, ConnId, InvitationId, MsgMeta (..))
|
||||||
|
@ -354,7 +356,8 @@ schemaMigrations =
|
||||||
("20221112_server_password", m20221112_server_password),
|
("20221112_server_password", m20221112_server_password),
|
||||||
("20221115_server_cfg", m20221115_server_cfg),
|
("20221115_server_cfg", m20221115_server_cfg),
|
||||||
("20221129_delete_group_feature_items", m20221129_delete_group_feature_items),
|
("20221129_delete_group_feature_items", m20221129_delete_group_feature_items),
|
||||||
("20221130_delete_item_deleted", m20221130_delete_item_deleted)
|
("20221130_delete_item_deleted", m20221130_delete_item_deleted),
|
||||||
|
("20221209_verified_connection", m20221209_verified_connection)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | The list of migrations in ascending order by date
|
-- | The list of migrations in ascending order by date
|
||||||
|
@ -465,7 +468,7 @@ getConnReqContactXContactId db user@User {userId} cReqHash = do
|
||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
||||||
-- Connection
|
-- Connection
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
||||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM contacts ct
|
FROM contacts ct
|
||||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||||
JOIN connections c ON c.contact_id = ct.contact_id
|
JOIN connections c ON c.contact_id = ct.contact_id
|
||||||
|
@ -540,7 +543,7 @@ createConnection_ db userId connType entityId acId viaContact viaUserContactLink
|
||||||
:. (ent ConnContact, ent ConnMember, ent ConnSndFile, ent ConnRcvFile, ent ConnUserContact, currentTs, currentTs)
|
:. (ent ConnContact, ent ConnMember, ent ConnSndFile, ent ConnRcvFile, ent ConnUserContact, currentTs, currentTs)
|
||||||
)
|
)
|
||||||
connId <- insertedRowId db
|
connId <- insertedRowId db
|
||||||
pure Connection {connId, agentConnId = AgentConnId acId, connType, entityId, viaContact, viaUserContactLink, viaGroupLink, groupLinkId = Nothing, customUserProfileId, connLevel, connStatus = ConnNew, localAlias = "", createdAt = currentTs}
|
pure Connection {connId, agentConnId = AgentConnId acId, connType, entityId, viaContact, viaUserContactLink, viaGroupLink, groupLinkId = Nothing, customUserProfileId, connLevel, connStatus = ConnNew, localAlias = "", createdAt = currentTs, connectionCode = Nothing}
|
||||||
where
|
where
|
||||||
ent ct = if connType == ct then entityId else Nothing
|
ent ct = if connType == ct then entityId else Nothing
|
||||||
|
|
||||||
|
@ -722,6 +725,11 @@ updateGroupUnreadChat db User {userId} GroupInfo {groupId} unreadChat = do
|
||||||
updatedAt <- getCurrentTime
|
updatedAt <- getCurrentTime
|
||||||
DB.execute db "UPDATE groups SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND group_id = ?" (unreadChat, updatedAt, userId, groupId)
|
DB.execute db "UPDATE groups SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND group_id = ?" (unreadChat, updatedAt, userId, groupId)
|
||||||
|
|
||||||
|
setConnectionVerified :: DB.Connection -> User -> Int64 -> Maybe Text -> IO ()
|
||||||
|
setConnectionVerified db User {userId} connId code = do
|
||||||
|
updatedAt <- getCurrentTime
|
||||||
|
DB.execute db "UPDATE connections SET security_code = ?, security_code_verified_at = ?, updated_at = ? WHERE user_id = ? AND connection_id = ?" (code, code $> updatedAt, updatedAt, userId, connId)
|
||||||
|
|
||||||
updateContactProfile_ :: DB.Connection -> UserId -> ProfileId -> Profile -> IO ()
|
updateContactProfile_ :: DB.Connection -> UserId -> ProfileId -> Profile -> IO ()
|
||||||
updateContactProfile_ db userId profileId profile = do
|
updateContactProfile_ db userId profileId profile = do
|
||||||
currentTs <- getCurrentTime
|
currentTs <- getCurrentTime
|
||||||
|
@ -821,7 +829,7 @@ getUserAddressConnections db User {userId} = do
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM connections c
|
FROM connections c
|
||||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||||
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||||
|
@ -835,7 +843,7 @@ getUserContactLinks db User {userId} =
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at,
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at,
|
||||||
uc.user_contact_link_id, uc.conn_req_contact, uc.group_id
|
uc.user_contact_link_id, uc.conn_req_contact, uc.group_id
|
||||||
FROM connections c
|
FROM connections c
|
||||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||||
|
@ -968,7 +976,7 @@ getGroupLinkConnection db User {userId} groupInfo@GroupInfo {groupId} =
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM connections c
|
FROM connections c
|
||||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||||
WHERE c.user_id = ? AND uc.user_id = ? AND uc.group_id = ?
|
WHERE c.user_id = ? AND uc.user_id = ? AND uc.group_id = ?
|
||||||
|
@ -1071,7 +1079,7 @@ createOrUpdateContactRequest db user@User {userId} userContactLinkId invId Profi
|
||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
||||||
-- Connection
|
-- Connection
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
||||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM contacts ct
|
FROM contacts ct
|
||||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||||
|
@ -1266,7 +1274,7 @@ getContactConnections db userId Contact {contactId} =
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM connections c
|
FROM connections c
|
||||||
JOIN contacts ct ON ct.contact_id = c.contact_id
|
JOIN contacts ct ON ct.contact_id = c.contact_id
|
||||||
WHERE c.user_id = ? AND ct.user_id = ? AND ct.contact_id = ?
|
WHERE c.user_id = ? AND ct.user_id = ? AND ct.contact_id = ?
|
||||||
|
@ -1277,14 +1285,15 @@ getContactConnections db userId Contact {contactId} =
|
||||||
|
|
||||||
type EntityIdsRow = (Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64)
|
type EntityIdsRow = (Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||||
|
|
||||||
type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, Maybe Int64, Bool, Maybe GroupLinkId, Maybe Int64, ConnStatus, ConnType, LocalAlias) :. EntityIdsRow :. Only UTCTime
|
type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, Maybe Int64, Bool, Maybe GroupLinkId, Maybe Int64, ConnStatus, ConnType, LocalAlias) :. EntityIdsRow :. (UTCTime, Maybe Text, Maybe UTCTime)
|
||||||
|
|
||||||
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe Bool, Maybe GroupLinkId, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe LocalAlias) :. EntityIdsRow :. Only (Maybe UTCTime)
|
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe Bool, Maybe GroupLinkId, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe LocalAlias) :. EntityIdsRow :. (Maybe UTCTime, Maybe Text, Maybe UTCTime)
|
||||||
|
|
||||||
toConnection :: ConnectionRow -> Connection
|
toConnection :: ConnectionRow -> Connection
|
||||||
toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. Only createdAt) =
|
toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_)) =
|
||||||
let entityId = entityId_ connType
|
let entityId = entityId_ connType
|
||||||
in Connection {connId, agentConnId = AgentConnId acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, localAlias, entityId, createdAt}
|
connectionCode = SecurityCode <$> code_ <*> verifiedAt_
|
||||||
|
in Connection {connId, agentConnId = AgentConnId acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, localAlias, entityId, connectionCode, createdAt}
|
||||||
where
|
where
|
||||||
entityId_ :: ConnType -> Maybe Int64
|
entityId_ :: ConnType -> Maybe Int64
|
||||||
entityId_ ConnContact = contactId
|
entityId_ ConnContact = contactId
|
||||||
|
@ -1294,8 +1303,8 @@ toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroup
|
||||||
entityId_ ConnUserContact = userContactLinkId
|
entityId_ ConnUserContact = userContactLinkId
|
||||||
|
|
||||||
toMaybeConnection :: MaybeConnectionRow -> Maybe Connection
|
toMaybeConnection :: MaybeConnectionRow -> Maybe Connection
|
||||||
toMaybeConnection ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just viaGroupLink, groupLinkId, customUserProfileId, Just connStatus, Just connType, Just localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. Only (Just createdAt)) =
|
toMaybeConnection ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just viaGroupLink, groupLinkId, customUserProfileId, Just connStatus, Just connType, Just localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (Just createdAt, code_, verifiedAt_)) =
|
||||||
Just $ toConnection ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. Only createdAt)
|
Just $ toConnection ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_))
|
||||||
toMaybeConnection _ = Nothing
|
toMaybeConnection _ = Nothing
|
||||||
|
|
||||||
getMatchingContacts :: DB.Connection -> User -> Contact -> IO [Contact]
|
getMatchingContacts :: DB.Connection -> User -> Contact -> IO [Contact]
|
||||||
|
@ -1466,7 +1475,7 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, via_group_link, group_link_id, custom_user_profile_id,
|
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, via_group_link, group_link_id, custom_user_profile_id,
|
||||||
conn_status, conn_type, local_alias, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at
|
conn_status, conn_type, local_alias, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at, security_code, security_code_verified_at
|
||||||
FROM connections
|
FROM connections
|
||||||
WHERE user_id = ? AND agent_conn_id = ?
|
WHERE user_id = ? AND agent_conn_id = ?
|
||||||
|]
|
|]
|
||||||
|
@ -1564,7 +1573,7 @@ getConnectionById db User {userId} connId = ExceptT $ do
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, via_group_link, group_link_id, custom_user_profile_id,
|
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, via_group_link, group_link_id, custom_user_profile_id,
|
||||||
conn_status, conn_type, local_alias, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at
|
conn_status, conn_type, local_alias, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at, security_code, security_code_verified_at
|
||||||
FROM connections
|
FROM connections
|
||||||
WHERE user_id = ? AND connection_id = ?
|
WHERE user_id = ? AND connection_id = ?
|
||||||
|]
|
|]
|
||||||
|
@ -1609,7 +1618,7 @@ getGroupAndMember db User {userId, userContactId} groupMemberId =
|
||||||
m.group_member_id, m.group_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, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||||
JOIN groups g ON g.group_id = m.group_id
|
JOIN groups g ON g.group_id = m.group_id
|
||||||
|
@ -1891,7 +1900,7 @@ getGroupMember db user@User {userId} groupId groupMemberId =
|
||||||
m.group_member_id, m.group_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, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||||
LEFT JOIN connections c ON c.connection_id = (
|
LEFT JOIN connections c ON c.connection_id = (
|
||||||
|
@ -1913,7 +1922,7 @@ getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do
|
||||||
m.group_member_id, m.group_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, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||||
LEFT JOIN connections c ON c.connection_id = (
|
LEFT JOIN connections c ON c.connection_id = (
|
||||||
|
@ -1935,7 +1944,7 @@ getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {gro
|
||||||
m.group_member_id, m.group_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, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias,
|
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias,
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||||
LEFT JOIN connections c ON c.connection_id = (
|
LEFT JOIN connections c ON c.connection_id = (
|
||||||
|
@ -2060,7 +2069,7 @@ getContactViaMember db user@User {userId} GroupMember {groupMemberId} =
|
||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
||||||
-- Connection
|
-- Connection
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
||||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM contacts ct
|
FROM contacts ct
|
||||||
JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id
|
JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id
|
||||||
JOIN connections c ON c.connection_id = (
|
JOIN connections c ON c.connection_id = (
|
||||||
|
@ -2365,7 +2374,7 @@ getViaGroupMember db User {userId, userContactId} Contact {contactId} =
|
||||||
m.group_member_id, m.group_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, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias, p.preferences,
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
JOIN contacts ct ON ct.contact_id = m.contact_id
|
JOIN contacts ct ON ct.contact_id = m.contact_id
|
||||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||||
|
@ -2397,7 +2406,7 @@ getViaGroupContact db user@User {userId} GroupMember {groupMemberId} =
|
||||||
SELECT
|
SELECT
|
||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, p.display_name, p.full_name, p.image, p.local_alias, ct.via_group, ct.contact_used, ct.enable_ntfs, p.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, p.display_name, p.full_name, p.image, p.local_alias, ct.via_group, ct.contact_used, ct.enable_ntfs, p.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM contacts ct
|
FROM contacts ct
|
||||||
JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id
|
JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id
|
||||||
JOIN connections c ON c.connection_id = (
|
JOIN connections c ON c.connection_id = (
|
||||||
|
@ -3276,7 +3285,7 @@ getDirectChatPreviews_ db user@User {userId} = do
|
||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
||||||
-- Connection
|
-- Connection
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
||||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at,
|
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at,
|
||||||
-- ChatStats
|
-- ChatStats
|
||||||
COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), ct.unread_chat,
|
COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), ct.unread_chat,
|
||||||
-- ChatItem
|
-- ChatItem
|
||||||
|
@ -3598,7 +3607,7 @@ getContact db user@User {userId} contactId =
|
||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at,
|
||||||
-- Connection
|
-- Connection
|
||||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.local_alias,
|
||||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at
|
||||||
FROM contacts ct
|
FROM contacts ct
|
||||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
module Simplex.Chat.Types where
|
module Simplex.Chat.Types where
|
||||||
|
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
|
import Crypto.Number.Serialize (os2ip)
|
||||||
import Data.Aeson (FromJSON, ToJSON)
|
import Data.Aeson (FromJSON, ToJSON)
|
||||||
import qualified Data.Aeson as J
|
import qualified Data.Aeson as J
|
||||||
import qualified Data.Aeson.Encoding as JE
|
import qualified Data.Aeson.Encoding as JE
|
||||||
|
@ -126,6 +127,9 @@ directContact Contact {contactUsed, activeConn = Connection {connLevel, viaGroup
|
||||||
anyDirectContact :: Contact -> Bool
|
anyDirectContact :: Contact -> Bool
|
||||||
anyDirectContact Contact {contactUsed, activeConn = Connection {connLevel}} = connLevel == 0 || contactUsed
|
anyDirectContact Contact {contactUsed, activeConn = Connection {connLevel}} = connLevel == 0 || contactUsed
|
||||||
|
|
||||||
|
contactSecurityCode :: Contact -> Maybe SecurityCode
|
||||||
|
contactSecurityCode Contact {activeConn} = connectionCode activeConn
|
||||||
|
|
||||||
data ContactRef = ContactRef
|
data ContactRef = ContactRef
|
||||||
{ contactId :: ContactId,
|
{ contactId :: ContactId,
|
||||||
localDisplayName :: ContactName
|
localDisplayName :: ContactName
|
||||||
|
@ -866,6 +870,9 @@ groupMemberId' GroupMember {groupMemberId} = groupMemberId
|
||||||
memberIncognito :: GroupMember -> Bool
|
memberIncognito :: GroupMember -> Bool
|
||||||
memberIncognito GroupMember {memberProfile, memberContactProfileId} = localProfileId memberProfile /= memberContactProfileId
|
memberIncognito GroupMember {memberProfile, memberContactProfileId} = localProfileId memberProfile /= memberContactProfileId
|
||||||
|
|
||||||
|
memberSecurityCode :: GroupMember -> Maybe SecurityCode
|
||||||
|
memberSecurityCode GroupMember {activeConn} = connectionCode =<< activeConn
|
||||||
|
|
||||||
data NewGroupMember = NewGroupMember
|
data NewGroupMember = NewGroupMember
|
||||||
{ memInfo :: MemberInfo,
|
{ memInfo :: MemberInfo,
|
||||||
memCategory :: GroupMemberCategory,
|
memCategory :: GroupMemberCategory,
|
||||||
|
@ -1302,10 +1309,29 @@ data Connection = Connection
|
||||||
connStatus :: ConnStatus,
|
connStatus :: ConnStatus,
|
||||||
localAlias :: Text,
|
localAlias :: Text,
|
||||||
entityId :: Maybe Int64, -- contact, group member, file ID or user contact ID
|
entityId :: Maybe Int64, -- contact, group member, file ID or user contact ID
|
||||||
|
connectionCode :: Maybe SecurityCode,
|
||||||
createdAt :: UTCTime
|
createdAt :: UTCTime
|
||||||
}
|
}
|
||||||
deriving (Eq, Show, Generic)
|
deriving (Eq, Show, Generic)
|
||||||
|
|
||||||
|
data SecurityCode = SecurityCode {securityCode :: Text, verifiedAt :: UTCTime}
|
||||||
|
deriving (Eq, Show, Generic)
|
||||||
|
|
||||||
|
instance ToJSON SecurityCode where
|
||||||
|
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
|
||||||
|
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
|
||||||
|
|
||||||
|
verificationCode :: ByteString -> Text
|
||||||
|
verificationCode = T.pack . unwords . chunks 5 . show . os2ip
|
||||||
|
where
|
||||||
|
chunks _ [] = []
|
||||||
|
chunks n xs = let (h, t) = splitAt n xs in h : chunks n t
|
||||||
|
|
||||||
|
sameVerificationCode :: Text -> Text -> Bool
|
||||||
|
sameVerificationCode c1 c2 = noSpaces c1 == noSpaces c2
|
||||||
|
where
|
||||||
|
noSpaces = T.filter (/= ' ')
|
||||||
|
|
||||||
aConnId :: Connection -> ConnId
|
aConnId :: Connection -> ConnId
|
||||||
aConnId Connection {agentConnId = AgentConnId cId} = cId
|
aConnId Connection {agentConnId = AgentConnId cId} = cId
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,9 @@ responseToView user_ testView ts = \case
|
||||||
CRGroupMemberInfo g m cStats -> viewGroupMemberInfo g m cStats
|
CRGroupMemberInfo g m cStats -> viewGroupMemberInfo g m cStats
|
||||||
CRContactSwitch ct progress -> viewContactSwitch ct progress
|
CRContactSwitch ct progress -> viewContactSwitch ct progress
|
||||||
CRGroupMemberSwitch g m progress -> viewGroupMemberSwitch g m progress
|
CRGroupMemberSwitch g m progress -> viewGroupMemberSwitch g m progress
|
||||||
|
CRCodeVerification verified code -> [plain $ if verified then "connection verified" else "error: current connection code is " <> code]
|
||||||
|
CRContactCode ct code -> viewContactCode ct code testView
|
||||||
|
CRGroupMemberCode g m code -> viewGroupMemberCode g m code testView
|
||||||
CRNewChatItem (AChatItem _ _ chat item) -> unmuted chat item $ viewChatItem chat item False ts
|
CRNewChatItem (AChatItem _ _ chat item) -> unmuted chat item $ viewChatItem chat item False ts
|
||||||
CRLastMessages chatItems -> concatMap (\(AChatItem _ _ chat item) -> viewChatItem chat item True ts) chatItems
|
CRLastMessages chatItems -> concatMap (\(AChatItem _ _ chat item) -> viewChatItem chat item True ts) chatItems
|
||||||
CRChatItemStatusUpdated _ -> []
|
CRChatItemStatusUpdated _ -> []
|
||||||
|
@ -497,8 +500,8 @@ viewCannotResendInvitation GroupInfo {localDisplayName = gn} c =
|
||||||
]
|
]
|
||||||
|
|
||||||
viewDirectMessagesProhibited :: MsgDirection -> Contact -> [StyledString]
|
viewDirectMessagesProhibited :: MsgDirection -> Contact -> [StyledString]
|
||||||
viewDirectMessagesProhibited MDSnd c = [ "direct messages to indirect contact " <> ttyContact' c <> " are prohibited"]
|
viewDirectMessagesProhibited MDSnd c = ["direct messages to indirect contact " <> ttyContact' c <> " are prohibited"]
|
||||||
viewDirectMessagesProhibited MDRcv c = [ "received prohibited direct message from indirect contact " <> ttyContact' c <> " (discarded)"]
|
viewDirectMessagesProhibited MDRcv c = ["received prohibited direct message from indirect contact " <> ttyContact' c <> " (discarded)"]
|
||||||
|
|
||||||
viewUserJoinedGroup :: GroupInfo -> [StyledString]
|
viewUserJoinedGroup :: GroupInfo -> [StyledString]
|
||||||
viewUserJoinedGroup g@GroupInfo {membership = membership@GroupMember {memberProfile}} =
|
viewUserJoinedGroup g@GroupInfo {membership = membership@GroupMember {memberProfile}} =
|
||||||
|
@ -681,21 +684,27 @@ viewNetworkConfig NetworkConfig {socksProxy, tcpTimeout} =
|
||||||
]
|
]
|
||||||
|
|
||||||
viewContactInfo :: Contact -> ConnectionStats -> Maybe Profile -> [StyledString]
|
viewContactInfo :: Contact -> ConnectionStats -> Maybe Profile -> [StyledString]
|
||||||
viewContactInfo Contact {contactId, profile = LocalProfile {localAlias}} stats incognitoProfile =
|
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias}} stats incognitoProfile =
|
||||||
["contact ID: " <> sShow contactId] <> viewConnectionStats stats
|
["contact ID: " <> sShow contactId] <> viewConnectionStats stats
|
||||||
<> maybe
|
<> maybe
|
||||||
["you've shared main profile with this contact"]
|
["you've shared main profile with this contact"]
|
||||||
(\p -> ["you've shared incognito profile with this contact: " <> incognitoProfile' p])
|
(\p -> ["you've shared incognito profile with this contact: " <> incognitoProfile' p])
|
||||||
incognitoProfile
|
incognitoProfile
|
||||||
<> if localAlias /= "" then ["alias: " <> plain localAlias] else ["alias not set"]
|
<> ["alias: " <> plain localAlias | localAlias /= ""]
|
||||||
|
<> [viewConnectionVerified (contactSecurityCode ct)]
|
||||||
|
|
||||||
viewGroupMemberInfo :: GroupInfo -> GroupMember -> Maybe ConnectionStats -> [StyledString]
|
viewGroupMemberInfo :: GroupInfo -> GroupMember -> Maybe ConnectionStats -> [StyledString]
|
||||||
viewGroupMemberInfo GroupInfo {groupId} GroupMember {groupMemberId, memberProfile = LocalProfile {localAlias}} stats =
|
viewGroupMemberInfo GroupInfo {groupId} m@GroupMember {groupMemberId, memberProfile = LocalProfile {localAlias}} stats =
|
||||||
[ "group ID: " <> sShow groupId,
|
[ "group ID: " <> sShow groupId,
|
||||||
"member ID: " <> sShow groupMemberId
|
"member ID: " <> sShow groupMemberId
|
||||||
]
|
]
|
||||||
<> maybe ["member not connected"] viewConnectionStats stats
|
<> maybe ["member not connected"] viewConnectionStats stats
|
||||||
<> if localAlias /= "" then ["alias: " <> plain localAlias] else ["no alias for contact"]
|
<> ["alias: " <> plain localAlias | localAlias /= ""]
|
||||||
|
<> [viewConnectionVerified (memberSecurityCode m) | isJust stats]
|
||||||
|
|
||||||
|
viewConnectionVerified :: Maybe SecurityCode -> StyledString
|
||||||
|
viewConnectionVerified (Just _) = "connection verified" -- TODO show verification time?
|
||||||
|
viewConnectionVerified _ = "connection not verified, use " <> highlight' "/code" <> " command to see security code"
|
||||||
|
|
||||||
viewConnectionStats :: ConnectionStats -> [StyledString]
|
viewConnectionStats :: ConnectionStats -> [StyledString]
|
||||||
viewConnectionStats ConnectionStats {rcvServers, sndServers} =
|
viewConnectionStats ConnectionStats {rcvServers, sndServers} =
|
||||||
|
@ -720,6 +729,17 @@ viewGroupMemberSwitch g m (SwitchProgress qd phase _) = case qd of
|
||||||
QDRcv -> [ttyGroup' g <> ": you " <> viewSwitchPhase phase <> " for " <> ttyMember m]
|
QDRcv -> [ttyGroup' g <> ": you " <> viewSwitchPhase phase <> " for " <> ttyMember m]
|
||||||
QDSnd -> [ttyGroup' g <> ": " <> ttyMember m <> " " <> viewSwitchPhase phase <> " for you"]
|
QDSnd -> [ttyGroup' g <> ": " <> ttyMember m <> " " <> viewSwitchPhase phase <> " for you"]
|
||||||
|
|
||||||
|
viewContactCode :: Contact -> Text -> Bool -> [StyledString]
|
||||||
|
viewContactCode ct@Contact {localDisplayName = c} = viewSecurityCode (ttyContact' ct) ("/verify " <> c <> " <code from your contact>")
|
||||||
|
|
||||||
|
viewGroupMemberCode :: GroupInfo -> GroupMember -> Text -> Bool -> [StyledString]
|
||||||
|
viewGroupMemberCode g m@GroupMember {localDisplayName = n} = viewSecurityCode (ttyGroup' g <> " " <> ttyMember m) ("/verify #" <> groupName' g <> " " <> n <> " <code from your contact>")
|
||||||
|
|
||||||
|
viewSecurityCode :: StyledString -> Text -> Text -> Bool -> [StyledString]
|
||||||
|
viewSecurityCode name cmd code testView
|
||||||
|
| testView = [plain code]
|
||||||
|
| otherwise = [name <> " security code:", plain code, "pass this code to your contact and use " <> highlight cmd <> " to verify"]
|
||||||
|
|
||||||
viewSwitchPhase :: SwitchPhase -> StyledString
|
viewSwitchPhase :: SwitchPhase -> StyledString
|
||||||
viewSwitchPhase SPCompleted = "changed address"
|
viewSwitchPhase SPCompleted = "changed address"
|
||||||
viewSwitchPhase phase = plain (strEncode phase) <> " changing address"
|
viewSwitchPhase phase = plain (strEncode phase) <> " changing address"
|
||||||
|
|
|
@ -49,7 +49,7 @@ extra-deps:
|
||||||
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
|
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
|
||||||
# - ../simplexmq
|
# - ../simplexmq
|
||||||
- github: simplex-chat/simplexmq
|
- github: simplex-chat/simplexmq
|
||||||
commit: e4842f4f47fb60ef8843dbce6fd43dec96f157d2
|
commit: fb21d9836e07706c7498baa967f932cb11b818e5
|
||||||
# - ../direct-sqlcipher
|
# - ../direct-sqlcipher
|
||||||
- github: simplex-chat/direct-sqlcipher
|
- github: simplex-chat/direct-sqlcipher
|
||||||
commit: 34309410eb2069b029b8fc1872deb1e0db123294
|
commit: 34309410eb2069b029b8fc1872deb1e0db123294
|
||||||
|
|
|
@ -27,6 +27,7 @@ import Simplex.Chat.Options (ChatOpts (..))
|
||||||
import Simplex.Chat.Store (getUserContactProfiles)
|
import Simplex.Chat.Store (getUserContactProfiles)
|
||||||
import Simplex.Chat.Types
|
import Simplex.Chat.Types
|
||||||
import Simplex.Messaging.Agent.Store.SQLite (withTransaction)
|
import Simplex.Messaging.Agent.Store.SQLite (withTransaction)
|
||||||
|
import qualified Simplex.Messaging.Crypto as C
|
||||||
import Simplex.Messaging.Encoding.String
|
import Simplex.Messaging.Encoding.String
|
||||||
import Simplex.Messaging.Util (unlessM)
|
import Simplex.Messaging.Util (unlessM)
|
||||||
import System.Directory (copyFile, createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
|
import System.Directory (copyFile, createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
|
||||||
|
@ -168,6 +169,13 @@ chatTests = do
|
||||||
describe "queue rotation" $ do
|
describe "queue rotation" $ do
|
||||||
it "switch contact to a different queue" testSwitchContact
|
it "switch contact to a different queue" testSwitchContact
|
||||||
it "switch group member to a different queue" testSwitchGroupMember
|
it "switch group member to a different queue" testSwitchGroupMember
|
||||||
|
describe "connection verification code" $ do
|
||||||
|
it "verificationCode function converts ByteString to series of digits" $
|
||||||
|
verificationCode (C.sha256Hash "abcd") `shouldBe` "61889 38426 63934 09576 96390 79389 84124 85253 63658 69469 70853 37788 95900 68296 20156 25"
|
||||||
|
it "sameVerificationCode function should ignore spaces" $
|
||||||
|
sameVerificationCode "123 456 789" "12345 6789" `shouldBe` True
|
||||||
|
it "mark contact verified" testMarkContactVerified
|
||||||
|
it "mark group member verified" testMarkGroupMemberVerified
|
||||||
|
|
||||||
versionTestMatrix2 :: (TestCC -> TestCC -> IO ()) -> Spec
|
versionTestMatrix2 :: (TestCC -> TestCC -> IO ()) -> Spec
|
||||||
versionTestMatrix2 runTest = do
|
versionTestMatrix2 runTest = do
|
||||||
|
@ -4551,6 +4559,58 @@ testSwitchGroupMember =
|
||||||
bob #> "#team hi"
|
bob #> "#team hi"
|
||||||
alice <# "#team bob> hi"
|
alice <# "#team bob> hi"
|
||||||
|
|
||||||
|
testMarkContactVerified :: IO ()
|
||||||
|
testMarkContactVerified =
|
||||||
|
testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||||
|
connectUsers alice bob
|
||||||
|
alice ##> "/i bob"
|
||||||
|
bobInfo alice
|
||||||
|
alice <## "connection not verified, use /code command to see security code"
|
||||||
|
alice ##> "/code bob"
|
||||||
|
bCode <- getTermLine alice
|
||||||
|
bob ##> "/code alice"
|
||||||
|
aCode <- getTermLine bob
|
||||||
|
bCode `shouldBe` aCode
|
||||||
|
alice ##> "/verify bob 123"
|
||||||
|
alice <##. "error: current connection code is "
|
||||||
|
alice ##> ("/verify bob " <> aCode)
|
||||||
|
alice <## "connection verified"
|
||||||
|
alice ##> "/i bob"
|
||||||
|
bobInfo alice
|
||||||
|
alice <## "connection verified"
|
||||||
|
where
|
||||||
|
bobInfo alice = do
|
||||||
|
alice <## "contact ID: 2"
|
||||||
|
alice <## "receiving messages via: localhost"
|
||||||
|
alice <## "sending messages via: localhost"
|
||||||
|
alice <## "you've shared main profile with this contact"
|
||||||
|
|
||||||
|
testMarkGroupMemberVerified :: IO ()
|
||||||
|
testMarkGroupMemberVerified =
|
||||||
|
testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||||
|
createGroup2 "team" alice bob
|
||||||
|
alice ##> "/i #team bob"
|
||||||
|
bobInfo alice
|
||||||
|
alice <## "connection not verified, use /code command to see security code"
|
||||||
|
alice ##> "/code #team bob"
|
||||||
|
bCode <- getTermLine alice
|
||||||
|
bob ##> "/code #team alice"
|
||||||
|
aCode <- getTermLine bob
|
||||||
|
bCode `shouldBe` aCode
|
||||||
|
alice ##> "/verify #team bob 123"
|
||||||
|
alice <##. "error: current connection code is "
|
||||||
|
alice ##> ("/verify #team bob " <> aCode)
|
||||||
|
alice <## "connection verified"
|
||||||
|
alice ##> "/i #team bob"
|
||||||
|
bobInfo alice
|
||||||
|
alice <## "connection verified"
|
||||||
|
where
|
||||||
|
bobInfo alice = do
|
||||||
|
alice <## "group ID: 1"
|
||||||
|
alice <## "member ID: 2"
|
||||||
|
alice <## "receiving messages via: localhost"
|
||||||
|
alice <## "sending messages via: localhost"
|
||||||
|
|
||||||
withTestChatContactConnected :: String -> (TestCC -> IO a) -> IO a
|
withTestChatContactConnected :: String -> (TestCC -> IO a) -> IO a
|
||||||
withTestChatContactConnected dbPrefix action =
|
withTestChatContactConnected dbPrefix action =
|
||||||
withTestChat dbPrefix $ \cc -> do
|
withTestChat dbPrefix $ \cc -> do
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue