mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
core: groundwork for post-quantum encryption support (#3845)
This commit is contained in:
+108
-38
@@ -243,6 +243,7 @@ newChatController
|
||||
encryptLocalFiles <- newTVarIO False
|
||||
tempDirectory <- newTVarIO Nothing
|
||||
contactMergeEnabled <- newTVarIO True
|
||||
pqExperimentalEnabled <- newTVarIO False
|
||||
pure
|
||||
ChatController
|
||||
{ firstTime,
|
||||
@@ -278,7 +279,8 @@ newChatController
|
||||
encryptLocalFiles,
|
||||
tempDirectory,
|
||||
logFilePath = logFile,
|
||||
contactMergeEnabled
|
||||
contactMergeEnabled,
|
||||
pqExperimentalEnabled
|
||||
}
|
||||
where
|
||||
configServers :: DefaultAgentServers
|
||||
@@ -589,6 +591,9 @@ processChatCommand' vr = \case
|
||||
SetContactMergeEnabled onOff -> do
|
||||
asks contactMergeEnabled >>= atomically . (`writeTVar` onOff)
|
||||
ok_
|
||||
APISetPQEnabled onOff -> do
|
||||
asks pqExperimentalEnabled >>= atomically . (`writeTVar` onOff)
|
||||
ok_
|
||||
APIExportArchive cfg -> checkChatStopped $ exportArchive cfg >> ok_
|
||||
ExportArchive -> do
|
||||
ts <- liftIO getCurrentTime
|
||||
@@ -1581,6 +1586,7 @@ processChatCommand' vr = \case
|
||||
-- [incognito] generate incognito profile for group membership
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
groupInfo <- withStore $ \db -> createNewGroup db vr gVar user gProfile incognitoProfile
|
||||
-- TODO [pq] create CISndGroupE2EEInfo (would affect tests)
|
||||
pure $ CRGroupCreated user groupInfo
|
||||
NewGroup incognito gProfile -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APINewGroup userId incognito gProfile
|
||||
@@ -1801,7 +1807,7 @@ processChatCommand' vr = \case
|
||||
case memberConn m of
|
||||
Just mConn -> do
|
||||
let msg = XGrpDirectInv cReq msgContent_
|
||||
(sndMsg, _) <- sendDirectMessage mConn msg $ GroupId groupId
|
||||
(sndMsg, _) <- sendDirectMessage mConn pqDummyFlag msg $ GroupId groupId
|
||||
withStore' $ \db -> setContactGrpInvSent db ct True
|
||||
let ct' = ct {contactGrpInvSent = True}
|
||||
forM_ msgContent_ $ \mc -> do
|
||||
@@ -2208,7 +2214,7 @@ processChatCommand' vr = \case
|
||||
ctSndMsg ChangedProfileContact {mergedProfile', conn = Connection {connId}} = (ConnectionId connId, XInfo mergedProfile')
|
||||
ctMsgReq :: ChangedProfileContact -> Either ChatError SndMessage -> Either ChatError MsgReq
|
||||
ctMsgReq ChangedProfileContact {conn} = fmap $ \SndMessage {msgId, msgBody} ->
|
||||
(conn, MsgFlags {notification = hasNotification XInfo_}, msgBody, msgId)
|
||||
(conn, pqDummyFlag, MsgFlags {notification = hasNotification XInfo_}, msgBody, msgId)
|
||||
updateContactPrefs :: User -> Contact -> Preferences -> m ChatResponse
|
||||
updateContactPrefs _ ct@Contact {activeConn = Nothing} _ = throwChatError $ CEContactNotActive ct
|
||||
updateContactPrefs user@User {userId} ct@Contact {activeConn = Just Connection {customUserProfileId}, userPreferences = contactUserPrefs} contactUserPrefs'
|
||||
@@ -2299,9 +2305,18 @@ processChatCommand' vr = \case
|
||||
groupMemberId <- getGroupMemberIdByName db user groupId groupMemberName
|
||||
pure (groupId, groupMemberId)
|
||||
sendGrpInvitation :: User -> Contact -> GroupInfo -> GroupMember -> ConnReqInvitation -> m ()
|
||||
sendGrpInvitation user ct@Contact {localDisplayName} GroupInfo {groupId, groupProfile, membership} GroupMember {groupMemberId, memberId, memberRole = memRole} cReq = do
|
||||
sendGrpInvitation user ct@Contact {localDisplayName} gInfo@GroupInfo {groupId, groupProfile, membership} GroupMember {groupMemberId, memberId, memberRole = memRole} cReq = do
|
||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
groupInv = GroupInvitation (MemberIdRole userMemberId userRole) (MemberIdRole memberId memRole) cReq groupProfile Nothing
|
||||
groupInv =
|
||||
GroupInvitation
|
||||
{ fromMember = MemberIdRole userMemberId userRole,
|
||||
invitedMember = MemberIdRole memberId memRole,
|
||||
connRequest = cReq,
|
||||
groupProfile,
|
||||
groupLinkId = Nothing,
|
||||
groupSize = Just currentMemCount
|
||||
}
|
||||
(msg, _) <- sendDirectContactMessage ct $ XGrpInv groupInv
|
||||
let content = CISndGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName, groupProfile, status = CIGISPending}) memRole
|
||||
ci <- saveSndChatItem user (CDDirectSnd ct) msg content
|
||||
@@ -2733,7 +2748,7 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, xftpRcvFile, fileI
|
||||
GroupMember {activeConn} <- withStoreCtx (Just "acceptFileReceive, getGroupMember") $ \db -> getGroupMember db user groupId memId
|
||||
case activeConn of
|
||||
Just conn -> do
|
||||
acceptFile CFCreateConnFileInvGroup $ \msg -> void $ sendDirectMessage conn msg $ GroupId groupId
|
||||
acceptFile CFCreateConnFileInvGroup $ \msg -> void $ sendDirectMessage conn pqDummyFlag msg $ GroupId groupId
|
||||
_ -> throwChatError $ CEFileInternal "member connection not active"
|
||||
_ -> throwChatError $ CEFileInternal "invalid chat ref for file transfer"
|
||||
where
|
||||
@@ -2856,9 +2871,18 @@ acceptGroupJoinRequestAsync
|
||||
incognitoProfile = do
|
||||
gVar <- asks random
|
||||
(groupMemberId, memberId) <- withStore $ \db -> createAcceptedMember db gVar user gInfo ucr gLinkMemRole
|
||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||
let Profile {displayName} = profileToSendOnAccept user incognitoProfile True
|
||||
GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
msg = XGrpLinkInv $ GroupLinkInvitation (MemberIdRole userMemberId userRole) displayName (MemberIdRole memberId gLinkMemRole) groupProfile
|
||||
msg =
|
||||
XGrpLinkInv $
|
||||
GroupLinkInvitation
|
||||
{ fromMember = MemberIdRole userMemberId userRole,
|
||||
fromMemberName = displayName,
|
||||
invitedMember = MemberIdRole memberId gLinkMemRole,
|
||||
groupProfile,
|
||||
groupSize = Just currentMemCount
|
||||
}
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
connIds <- agentAcceptContactAsync user True invId msg subMode
|
||||
withStore $ \db -> do
|
||||
@@ -3313,7 +3337,7 @@ processAgentMsgSndFile _corrId aFileId msg =
|
||||
useMember _ = Nothing
|
||||
sendToMember :: (ValidFileDescription 'FRecipient, (Connection, SndFileTransfer)) -> m ()
|
||||
sendToMember (rfd, (conn, sft)) =
|
||||
void $ sendFileDescription sft rfd sharedMsgId $ \msg' -> sendDirectMessage conn msg' $ GroupId groupId
|
||||
void $ sendFileDescription sft rfd sharedMsgId $ \msg' -> sendDirectMessage conn pqDummyFlag msg' $ GroupId groupId
|
||||
_ -> pure ()
|
||||
_ -> pure () -- TODO error?
|
||||
SFERR e
|
||||
@@ -3490,6 +3514,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
sendXGrpMemInv hostConnId (Just directConnReq) xGrpMemIntroCont
|
||||
CRContactUri _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
||||
MSG msgMeta _msgFlags msgBody -> do
|
||||
-- TODO [pq] same for other direct connection events;
|
||||
-- TODO use ct', conn' downstream;
|
||||
-- TODO pqAgentDummy - would be returned in agent event
|
||||
let pqAgentDummy = False
|
||||
(_ct', _conn') <- updateContactPQ ct conn pqAgentDummy
|
||||
checkIntegrityCreateItem (CDDirectRcv ct) msgMeta
|
||||
cmdId <- createAckCmd conn
|
||||
withAckMessage agentConnId cmdId msgMeta $ do
|
||||
@@ -3674,8 +3703,17 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
where
|
||||
sendGrpInvitation :: Contact -> GroupMember -> Maybe GroupLinkId -> m ()
|
||||
sendGrpInvitation ct GroupMember {memberId, memberRole = memRole} groupLinkId = do
|
||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
groupInv = GroupInvitation (MemberIdRole userMemberId userRole) (MemberIdRole memberId memRole) cReq groupProfile groupLinkId
|
||||
groupInv =
|
||||
GroupInvitation
|
||||
{ fromMember = MemberIdRole userMemberId userRole,
|
||||
invitedMember = MemberIdRole memberId memRole,
|
||||
connRequest = cReq,
|
||||
groupProfile,
|
||||
groupLinkId = groupLinkId,
|
||||
groupSize = Just currentMemCount
|
||||
}
|
||||
(_msg, _) <- sendDirectContactMessage ct $ XGrpInv groupInv
|
||||
-- we could link chat item with sent group invitation message (_msg)
|
||||
createInternalChatItem user (CDGroupRcv gInfo m) (CIRcvGroupEvent RGEInvitedViaGroupLink) Nothing
|
||||
@@ -3729,6 +3767,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
case memberCategory m of
|
||||
GCHostMember -> do
|
||||
toView $ CRUserJoinedGroup user gInfo {membership = membership {memberStatus = GSMemConnected}} m {memberStatus = GSMemConnected}
|
||||
-- TODO [pq] create CIRcvGroupE2EEInfo (would affect tests)
|
||||
createGroupFeatureItems gInfo m
|
||||
let GroupInfo {groupProfile = GroupProfile {description}} = gInfo
|
||||
memberConnectedChatItem gInfo m
|
||||
@@ -3750,7 +3789,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
sendXGrpLinkMem = do
|
||||
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo
|
||||
profileToSend = profileToSendOnAccept user profileMode True
|
||||
void $ sendDirectMessage conn (XGrpLinkMem profileToSend) (GroupId groupId)
|
||||
void $ sendDirectMessage conn pqDummyFlag (XGrpLinkMem profileToSend) (GroupId groupId)
|
||||
sendIntroductions members = do
|
||||
intros <- withStore' $ \db -> createIntroductions db (maxVersion vr) members m
|
||||
shuffledIntros <- liftIO $ shuffleIntros intros
|
||||
@@ -3776,7 +3815,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
isAdmin GroupMemberIntro {reMember = GroupMember {memberRole}} = memberRole >= GRAdmin
|
||||
hasPicture GroupMemberIntro {reMember = GroupMember {memberProfile = LocalProfile {image}}} = isJust image
|
||||
processIntro intro@GroupMemberIntro {introId} = do
|
||||
void $ sendDirectMessage conn (memberIntro $ reMember intro) (GroupId groupId)
|
||||
void $ sendDirectMessage conn pqDummyFlag (memberIntro $ reMember intro) (GroupId groupId)
|
||||
withStore' $ \db -> updateIntroStatus db introId GMIntroSent
|
||||
sendHistory =
|
||||
when (isCompatibleRange (memberChatVRange' m) batchSendVRange) $ do
|
||||
@@ -3875,12 +3914,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
forM_ (invitedByGroupMemberId membership) $ \hostId -> do
|
||||
host <- withStore $ \db -> getGroupMember db user groupId hostId
|
||||
forM_ (memberConn host) $ \hostConn ->
|
||||
void $ sendDirectMessage hostConn (XGrpMemCon memberId) (GroupId groupId)
|
||||
void $ sendDirectMessage hostConn pqDummyFlag (XGrpMemCon memberId) (GroupId groupId)
|
||||
GCPostMember ->
|
||||
forM_ (invitedByGroupMemberId m) $ \invitingMemberId -> do
|
||||
im <- withStore $ \db -> getGroupMember db user groupId invitingMemberId
|
||||
forM_ (memberConn im) $ \imConn ->
|
||||
void $ sendDirectMessage imConn (XGrpMemCon memberId) (GroupId groupId)
|
||||
void $ sendDirectMessage imConn pqDummyFlag (XGrpMemCon memberId) (GroupId groupId)
|
||||
_ -> messageWarning "sendXGrpMemCon: member category GCPreMember or GCPostMember is expected"
|
||||
MSG msgMeta _msgFlags msgBody -> do
|
||||
checkIntegrityCreateItem (CDGroupRcv gInfo m) msgMeta
|
||||
@@ -4113,7 +4152,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
case activeConn of
|
||||
Just gMemberConn -> do
|
||||
sharedMsgId <- withStore $ \db -> getSharedMsgIdByFileId db userId fileId
|
||||
void $ sendDirectMessage gMemberConn (XFileAcptInv sharedMsgId (Just fileInvConnReq) fileName) $ GroupId groupId
|
||||
void $ sendDirectMessage gMemberConn pqDummyFlag (XFileAcptInv sharedMsgId (Just fileInvConnReq) fileName) $ GroupId groupId
|
||||
_ -> throwChatError $ CECommandError "no GroupMember activeConn"
|
||||
_ -> throwChatError $ CECommandError "no grpMemberId"
|
||||
_ -> throwChatError $ CECommandError "unexpected cmdFunction"
|
||||
@@ -4357,7 +4396,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
else sendProbe . Probe =<< liftIO (encodedRandomBytes gVar 32)
|
||||
where
|
||||
sendProbe :: Probe -> m ()
|
||||
sendProbe probe = void $ sendDirectMessage conn (XInfoProbe probe) (GroupId groupId)
|
||||
sendProbe probe = void $ sendDirectMessage conn pqDummyFlag (XInfoProbe probe) (GroupId groupId)
|
||||
|
||||
sendProbeHashes :: [ContactOrMember] -> Probe -> Int64 -> m ()
|
||||
sendProbeHashes cgms probe probeId =
|
||||
@@ -4371,7 +4410,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
sendProbeHash (COMGroupMember GroupMember {activeConn = Nothing}) = pure ()
|
||||
sendProbeHash cgm@(COMGroupMember m@GroupMember {groupId, activeConn = Just conn}) =
|
||||
when (memberCurrent m) $ do
|
||||
void $ sendDirectMessage conn (XInfoProbeCheck probeHash) (GroupId groupId)
|
||||
void $ sendDirectMessage conn pqDummyFlag (XInfoProbeCheck probeHash) (GroupId groupId)
|
||||
withStore' $ \db -> createSentProbeHash db userId probeId cgm
|
||||
|
||||
messageWarning :: Text -> m ()
|
||||
@@ -5056,7 +5095,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
case cgm2 of
|
||||
COMContact c2@Contact {profile = p2}
|
||||
| memberCurrent m1 && isNothing memberContactId && profilesMatch p1 p2 -> do
|
||||
void $ sendDirectMessage conn (XInfoProbeOk probe) (GroupId groupId)
|
||||
void $ sendDirectMessage conn pqDummyFlag (XInfoProbeOk probe) (GroupId groupId)
|
||||
COMContact <$$> associateMemberAndContact c2 m1
|
||||
| otherwise -> messageWarning "probeMatch ignored: profiles don't match or member already has contact or member not current" >> pure Nothing
|
||||
COMGroupMember _ -> messageWarning "probeMatch ignored: members are not matched with members" >> pure Nothing
|
||||
@@ -5257,6 +5296,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
XInfo p -> do
|
||||
let contactUsed = connDirect activeConn
|
||||
ct <- withStore $ \db -> createDirectContact db user conn' p contactUsed
|
||||
-- TODO [pq] create CIRcvDirectE2EEInfo here?
|
||||
toView $ CRContactConnecting user ct
|
||||
pure conn'
|
||||
XGrpLinkInv glInv -> do
|
||||
@@ -5312,7 +5352,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
sendXGrpMemInv hostConnId directConnReq XGrpMemIntroCont {groupId, groupMemberId, memberId, groupConnReq} = do
|
||||
hostConn <- withStore $ \db -> getConnectionById db user hostConnId
|
||||
let msg = XGrpMemInv memberId IntroInvitation {groupConnReq, directConnReq}
|
||||
void $ sendDirectMessage hostConn msg (GroupId groupId)
|
||||
void $ sendDirectMessage hostConn pqDummyFlag msg (GroupId groupId)
|
||||
withStore' $ \db -> updateGroupMemberStatusById db userId groupMemberId GSMemIntroInvited
|
||||
|
||||
xGrpMemInv :: GroupInfo -> GroupMember -> MemberId -> IntroInvitation -> m ()
|
||||
@@ -5667,6 +5707,24 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
toView $ CRChatItemStatusUpdated user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) chatItem)
|
||||
_ -> pure ()
|
||||
|
||||
-- TODO [pq] track rcv and snd flags separately
|
||||
updateContactPQ :: Contact -> Connection -> PQFlag -> m (Contact, Connection)
|
||||
updateContactPQ ct conn@Connection {connId, pqEnabled} pqEnabled' =
|
||||
flip catchChatError (const $ pure (ct, conn)) $ case (pqEnabled, pqEnabled') of
|
||||
(Nothing, False) -> pure (ct, conn)
|
||||
(Nothing, True) -> updatePQ $ CIRcvDirectE2EEInfo (E2EEInfo pqEnabled')
|
||||
(Just b, b')
|
||||
| b' /= b -> updatePQ $ CIRcvConnEvent (RCEPQEnabled pqEnabled')
|
||||
| otherwise -> pure (ct, conn)
|
||||
where
|
||||
updatePQ ciContent = do
|
||||
withStore' $ \db -> updateConnPQEnabled db connId pqEnabled'
|
||||
let conn' = conn {pqEnabled = Just pqEnabled'} :: Connection
|
||||
ct' = ct {activeConn = Just conn'} :: Contact
|
||||
createInternalChatItem user (CDDirectRcv ct') ciContent Nothing
|
||||
toView $ CRContactPQEnabled user ct' pqEnabled'
|
||||
pure (ct', conn')
|
||||
|
||||
metaBrokerTs :: MsgMeta -> UTCTime
|
||||
metaBrokerTs MsgMeta {broker = (_, brokerTs)} = brokerTs
|
||||
|
||||
@@ -5705,7 +5763,7 @@ sendDirectFileInline ct ft sharedMsgId = do
|
||||
|
||||
sendMemberFileInline :: ChatMonad m => GroupMember -> Connection -> FileTransferMeta -> SharedMsgId -> m ()
|
||||
sendMemberFileInline m@GroupMember {groupId} conn ft sharedMsgId = do
|
||||
msgDeliveryId <- sendFileInline_ ft sharedMsgId $ \msg -> sendDirectMessage conn msg $ GroupId groupId
|
||||
msgDeliveryId <- sendFileInline_ ft sharedMsgId $ \msg -> sendDirectMessage conn pqDummyFlag msg $ GroupId groupId
|
||||
withStore' $ \db -> updateSndGroupFTDelivery db m conn ft msgDeliveryId
|
||||
|
||||
sendFileInline_ :: ChatMonad m => FileTransferMeta -> SharedMsgId -> (ChatMsgEvent 'Binary -> m (SndMessage, Int64)) -> m Int64
|
||||
@@ -5855,7 +5913,7 @@ cancelSndFileTransfer user@User {userId} ft@SndFileTransfer {fileId, connId, age
|
||||
when sendCancel $ case fileInline of
|
||||
Just _ -> do
|
||||
(sharedMsgId, conn) <- withStore $ \db -> (,) <$> getSharedMsgIdByFileId db userId fileId <*> getConnectionById db user connId
|
||||
void . sendDirectMessage conn (BFileChunk sharedMsgId FileChunkCancel) $ ConnectionId connId
|
||||
void . sendDirectMessage conn pqDummyFlag (BFileChunk sharedMsgId FileChunkCancel) $ ConnectionId connId
|
||||
_ -> withAgent $ \a -> void . sendMessage a acId SMP.noMsgFlags $ smpEncode FileChunkCancel
|
||||
pure fileConnId
|
||||
fileConnId = if isNothing fileInline then Just acId else Nothing
|
||||
@@ -5896,7 +5954,9 @@ deleteOrUpdateMemberRecord user@User {userId} member =
|
||||
sendDirectContactMessage :: (MsgEncodingI e, ChatMonad m) => Contact -> ChatMsgEvent e -> m (SndMessage, Int64)
|
||||
sendDirectContactMessage ct chatMsgEvent = do
|
||||
conn@Connection {connId} <- liftEither $ contactSendConn_ ct
|
||||
sendDirectMessage conn chatMsgEvent (ConnectionId connId)
|
||||
-- TODO [pq] look up pqExperimentalEnabled on every send to pass flag to agent apis
|
||||
pq <- readTVarIO =<< asks pqExperimentalEnabled
|
||||
sendDirectMessage conn pq chatMsgEvent (ConnectionId connId)
|
||||
|
||||
contactSendConn_ :: Contact -> Either ChatError Connection
|
||||
contactSendConn_ ct@Contact {activeConn} = case activeConn of
|
||||
@@ -5909,11 +5969,11 @@ contactSendConn_ ct@Contact {activeConn} = case activeConn of
|
||||
where
|
||||
err = Left . ChatError
|
||||
|
||||
sendDirectMessage :: (MsgEncodingI e, ChatMonad m) => Connection -> ChatMsgEvent e -> ConnOrGroupId -> m (SndMessage, Int64)
|
||||
sendDirectMessage conn chatMsgEvent connOrGroupId = do
|
||||
sendDirectMessage :: (MsgEncodingI e, ChatMonad m) => Connection -> PQFlag -> ChatMsgEvent e -> ConnOrGroupId -> m (SndMessage, Int64)
|
||||
sendDirectMessage conn pq chatMsgEvent connOrGroupId = do
|
||||
when (connDisabled conn) $ throwChatError (CEConnectionDisabled conn)
|
||||
msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent connOrGroupId
|
||||
(msg,) <$> deliverMessage conn (toCMEventTag chatMsgEvent) msgBody msgId
|
||||
(msg,) <$> deliverMessage conn pq (toCMEventTag chatMsgEvent) msgBody msgId
|
||||
|
||||
createSndMessage :: (MsgEncodingI e, ChatMonad m) => ChatMsgEvent e -> ConnOrGroupId -> m SndMessage
|
||||
createSndMessage chatMsgEvent connOrGroupId =
|
||||
@@ -5957,35 +6017,44 @@ directMessage chatMsgEvent = do
|
||||
ECMEncoded encodedBody -> pure encodedBody
|
||||
ECMLarge -> throwChatError $ CEException "large message"
|
||||
|
||||
deliverMessage :: ChatMonad m => Connection -> CMEventTag e -> MsgBody -> MessageId -> m Int64
|
||||
deliverMessage conn cmEventTag msgBody msgId = do
|
||||
deliverMessage :: ChatMonad m => Connection -> PQFlag -> CMEventTag e -> MsgBody -> MessageId -> m Int64
|
||||
deliverMessage conn pq cmEventTag msgBody msgId = do
|
||||
let msgFlags = MsgFlags {notification = hasNotification cmEventTag}
|
||||
deliverMessage' conn msgFlags msgBody msgId
|
||||
deliverMessage' conn pq msgFlags msgBody msgId
|
||||
|
||||
deliverMessage' :: ChatMonad m => Connection -> MsgFlags -> MsgBody -> MessageId -> m Int64
|
||||
deliverMessage' conn msgFlags msgBody msgId =
|
||||
deliverMessages [(conn, msgFlags, msgBody, msgId)] >>= \case
|
||||
deliverMessage' :: ChatMonad m => Connection -> PQFlag -> MsgFlags -> MsgBody -> MessageId -> m Int64
|
||||
deliverMessage' conn pq msgFlags msgBody msgId =
|
||||
deliverMessages [(conn, pq, msgFlags, msgBody, msgId)] >>= \case
|
||||
[r] -> liftEither r
|
||||
rs -> throwChatError $ CEInternalError $ "deliverMessage: expected 1 result, got " <> show (length rs)
|
||||
|
||||
type MsgReq = (Connection, MsgFlags, MsgBody, MessageId)
|
||||
type MsgReq = (Connection, PQFlag, MsgFlags, MsgBody, MessageId)
|
||||
|
||||
-- TODO [pq] remove, replace in all places with actual flag / pqOff in groups
|
||||
pqDummyFlag :: PQFlag
|
||||
pqDummyFlag = False
|
||||
|
||||
-- TODO remove in 5.7 (used for groups)
|
||||
pqOff :: PQFlag
|
||||
pqOff = False
|
||||
|
||||
deliverMessages :: ChatMonad' m => [MsgReq] -> m [Either ChatError Int64]
|
||||
deliverMessages = deliverMessagesB . map Right
|
||||
|
||||
deliverMessagesB :: ChatMonad' m => [Either ChatError MsgReq] -> m [Either ChatError Int64]
|
||||
deliverMessagesB msgReqs = do
|
||||
-- TODO [pq] pass _pqFlag to sendMessagesB
|
||||
sent <- zipWith prepareBatch msgReqs <$> withAgent' (`sendMessagesB` map toAgent msgReqs)
|
||||
withStoreBatch $ \db -> map (bindRight $ createDelivery db) sent
|
||||
where
|
||||
toAgent = \case
|
||||
Right (conn, msgFlags, msgBody, _msgId) -> Right (aConnId conn, msgFlags, msgBody)
|
||||
Right (conn, _pqFlag, msgFlags, msgBody, _msgId) -> Right (aConnId conn, msgFlags, msgBody)
|
||||
Left _ce -> Left (AP.INTERNAL "ChatError, skip") -- as long as it is Left, the agent batchers should just step over it
|
||||
prepareBatch (Right req) (Right ar) = Right (req, ar)
|
||||
prepareBatch (Left ce) _ = Left ce -- restore original ChatError
|
||||
prepareBatch _ (Left ae) = Left $ ChatErrorAgent ae Nothing
|
||||
createDelivery :: DB.Connection -> (MsgReq, AgentMsgId) -> IO (Either ChatError Int64)
|
||||
createDelivery db ((Connection {connId}, _, _, msgId), agentMsgId) =
|
||||
createDelivery db ((Connection {connId}, _, _, _, msgId), agentMsgId) =
|
||||
Right <$> createSndMsgDelivery db (SndMsgDelivery {connId, agentMsgId}) msgId
|
||||
|
||||
sendGroupMessage :: (MsgEncodingI e, ChatMonad m) => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> m (SndMessage, [GroupMember])
|
||||
@@ -6016,7 +6085,7 @@ sendGroupMessage' user GroupInfo {groupId} members chatMsgEvent = do
|
||||
recipientMembers <- liftIO $ shuffleMembers (filter memberCurrent members)
|
||||
let msgFlags = MsgFlags {notification = hasNotification $ toCMEventTag chatMsgEvent}
|
||||
(toSend, pending) = foldr addMember ([], []) recipientMembers
|
||||
msgReqs = map (\(_, conn) -> (conn, msgFlags, msgBody, msgId)) toSend
|
||||
msgReqs = map (\(_, conn) -> (conn, pqOff, msgFlags, msgBody, msgId)) toSend
|
||||
delivered <- deliverMessages msgReqs
|
||||
let errors = lefts delivered
|
||||
unless (null errors) $ toView $ CRChatErrors (Just user) errors
|
||||
@@ -6075,7 +6144,7 @@ sendGroupMemberMessage user m@GroupMember {groupMemberId} chatMsgEvent groupId i
|
||||
where
|
||||
messageMember :: SndMessage -> m ()
|
||||
messageMember SndMessage {msgId, msgBody} = forM_ (memberSendAction chatMsgEvent [m] m) $ \case
|
||||
MSASend conn -> deliverMessage conn (toCMEventTag chatMsgEvent) msgBody msgId >> postDeliver
|
||||
MSASend conn -> deliverMessage conn pqDummyFlag (toCMEventTag chatMsgEvent) msgBody msgId >> postDeliver
|
||||
MSAPending -> withStore' $ \db -> createPendingGroupMessage db groupMemberId msgId introId_
|
||||
|
||||
sendPendingGroupMessages :: ChatMonad m => User -> GroupMember -> Connection -> m ()
|
||||
@@ -6086,7 +6155,7 @@ sendPendingGroupMessages user GroupMember {groupMemberId, localDisplayName} conn
|
||||
processPendingMessage pgm `catchChatError` (toView . CRChatError (Just user))
|
||||
where
|
||||
processPendingMessage PendingGroupMessage {msgId, cmEventTag = ACMEventTag _ tag, msgBody, introId_} = do
|
||||
void $ deliverMessage conn tag msgBody msgId
|
||||
void $ deliverMessage conn pqDummyFlag tag msgBody msgId
|
||||
withStore' $ \db -> deletePendingGroupMessage db groupMemberId msgId
|
||||
case tag of
|
||||
XGrpMemFwd_ -> case introId_ of
|
||||
@@ -6120,7 +6189,7 @@ saveGroupRcvMsg user groupId authorMember conn@Connection {connId} agentMsgMeta
|
||||
ChatErrorStore (SEDuplicateGroupMessage _ _ _ (Just forwardedByGroupMemberId)) -> do
|
||||
fm <- withStore $ \db -> getGroupMember db user groupId forwardedByGroupMemberId
|
||||
forM_ (memberConn fm) $ \fmConn ->
|
||||
void $ sendDirectMessage fmConn (XGrpMemCon amMemId) (GroupId groupId)
|
||||
void $ sendDirectMessage fmConn pqDummyFlag (XGrpMemCon amMemId) (GroupId groupId)
|
||||
throwError e
|
||||
_ -> throwError e
|
||||
pure (am', conn', msg)
|
||||
@@ -6136,7 +6205,7 @@ saveGroupFwdRcvMsg user groupId forwardingMember refAuthorMember@GroupMember {me
|
||||
am@GroupMember {memberId = amMemberId} <- withStore $ \db -> getGroupMember db user groupId authorGroupMemberId
|
||||
if sameMemberId refMemberId am
|
||||
then forM_ (memberConn forwardingMember) $ \fmConn ->
|
||||
void $ sendDirectMessage fmConn (XGrpMemCon amMemberId) (GroupId groupId)
|
||||
void $ sendDirectMessage fmConn pqDummyFlag (XGrpMemCon amMemberId) (GroupId groupId)
|
||||
else toView $ CRMessageError user "error" "saveGroupFwdRcvMsg: referenced author member id doesn't match message member id"
|
||||
throwError e
|
||||
_ -> throwError e
|
||||
@@ -6533,6 +6602,7 @@ chatCommandP =
|
||||
"/remote_hosts_folder " *> (SetRemoteHostsFolder <$> filePath),
|
||||
"/_files_encrypt " *> (APISetEncryptLocalFiles <$> onOffP),
|
||||
"/contact_merge " *> (SetContactMergeEnabled <$> onOffP),
|
||||
"/_pq " *> (APISetPQEnabled <$> onOffP),
|
||||
"/_db export " *> (APIExportArchive <$> jsonP),
|
||||
"/db export" $> ExportArchive,
|
||||
"/_db import " *> (APIImportArchive <$> jsonP),
|
||||
|
||||
@@ -206,7 +206,8 @@ data ChatController = ChatController
|
||||
encryptLocalFiles :: TVar Bool,
|
||||
tempDirectory :: TVar (Maybe FilePath),
|
||||
logFilePath :: Maybe FilePath,
|
||||
contactMergeEnabled :: TVar Bool
|
||||
contactMergeEnabled :: TVar Bool,
|
||||
pqExperimentalEnabled :: TVar Bool -- TODO remove in 5.7
|
||||
}
|
||||
|
||||
data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSRemote | HSSettings | HSDatabase
|
||||
@@ -243,6 +244,7 @@ data ChatCommand
|
||||
| SetRemoteHostsFolder FilePath
|
||||
| APISetEncryptLocalFiles Bool
|
||||
| SetContactMergeEnabled Bool
|
||||
| APISetPQEnabled Bool
|
||||
| APIExportArchive ArchiveConfig
|
||||
| ExportArchive
|
||||
| APIImportArchive ArchiveConfig
|
||||
@@ -697,6 +699,7 @@ data ChatResponse
|
||||
| CRRemoteCtrlSessionCode {remoteCtrl_ :: Maybe RemoteCtrlInfo, sessionCode :: Text}
|
||||
| CRRemoteCtrlConnected {remoteCtrl :: RemoteCtrlInfo}
|
||||
| CRRemoteCtrlStopped {rcsState :: RemoteCtrlSessionState, rcStopReason :: RemoteCtrlStopReason}
|
||||
| CRContactPQEnabled {user :: User, contact :: Contact, pqEnabled :: Bool}
|
||||
| CRSQLResult {rows :: [Text]}
|
||||
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
|
||||
| CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks}
|
||||
|
||||
@@ -139,13 +139,23 @@ data CIContent (d :: MsgDirection) where
|
||||
CISndModerated :: CIContent 'MDSnd
|
||||
CIRcvModerated :: CIContent 'MDRcv
|
||||
CIRcvBlocked :: CIContent 'MDRcv
|
||||
CISndDirectE2EEInfo :: E2EEInfo -> CIContent 'MDSnd
|
||||
CIRcvDirectE2EEInfo :: E2EEInfo -> CIContent 'MDRcv
|
||||
CISndGroupE2EEInfo :: E2EEInfo -> CIContent 'MDSnd -- when new group is created
|
||||
CIRcvGroupE2EEInfo :: E2EEInfo -> CIContent 'MDRcv -- when enabled with some member
|
||||
CIInvalidJSON :: Text -> CIContent d -- this is also used for logical database errors, e.g. SEBadChatItem
|
||||
|
||||
-- ^ This type is used both in API and in DB, so we use different JSON encodings for the database and for the API
|
||||
-- ! ^ Nested sum types also have to use different encodings for database and API
|
||||
-- ! ^ to avoid breaking cross-platform compatibility, see RcvGroupEvent and SndGroupEvent
|
||||
|
||||
deriving instance Show (CIContent d)
|
||||
|
||||
data E2EEInfo = E2EEInfo
|
||||
{ pqEnabled :: Bool
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
ciMsgContent :: CIContent d -> Maybe MsgContent
|
||||
ciMsgContent = \case
|
||||
CISndMsgContent mc -> Just mc
|
||||
@@ -195,6 +205,8 @@ ciRequiresAttention content = case msgDirection @d of
|
||||
CIRcvGroupFeatureRejected _ -> True
|
||||
CIRcvModerated -> True
|
||||
CIRcvBlocked -> False
|
||||
CIRcvDirectE2EEInfo _ -> False
|
||||
CIRcvGroupE2EEInfo _ -> False
|
||||
CIInvalidJSON _ -> False
|
||||
|
||||
newtype DBMsgErrorType = DBME MsgErrorType
|
||||
@@ -250,8 +262,24 @@ ciContentToText = \case
|
||||
CISndModerated -> ciModeratedText
|
||||
CIRcvModerated -> ciModeratedText
|
||||
CIRcvBlocked -> "blocked"
|
||||
CISndDirectE2EEInfo e2eeInfo -> directE2EEInfoToText e2eeInfo
|
||||
CIRcvDirectE2EEInfo e2eeInfo -> directE2EEInfoToText e2eeInfo
|
||||
CISndGroupE2EEInfo e2eeInfo -> groupE2EEInfoToText e2eeInfo
|
||||
CIRcvGroupE2EEInfo e2eeInfo -> groupE2EEInfoToText e2eeInfo
|
||||
CIInvalidJSON _ -> "invalid content JSON"
|
||||
|
||||
directE2EEInfoToText :: E2EEInfo -> Text
|
||||
directE2EEInfoToText E2EEInfo {pqEnabled}
|
||||
| pqEnabled = "This conversation is protected by quantum resistant end-to-end encryption. It has perfect forward secrecy, repudiation and quantum resistant break-in recovery."
|
||||
| otherwise = e2eeInfoNoPQText
|
||||
|
||||
groupE2EEInfoToText :: E2EEInfo -> Text
|
||||
groupE2EEInfoToText _e2eeInfo = e2eeInfoNoPQText
|
||||
|
||||
e2eeInfoNoPQText :: Text
|
||||
e2eeInfoNoPQText =
|
||||
"This conversation is protected by end-to-end encryption with perfect forward secrecy, repudiation and break-in recovery."
|
||||
|
||||
ciGroupInvitationToText :: CIGroupInvitation -> GroupMemberRole -> Text
|
||||
ciGroupInvitationToText CIGroupInvitation {groupProfile = GroupProfile {displayName, fullName}} role =
|
||||
"invitation to join group " <> displayName <> optionalFullName displayName fullName <> " as " <> (decodeLatin1 . strEncode $ role)
|
||||
@@ -295,6 +323,9 @@ rcvConnEventToText = \case
|
||||
SPCompleted -> "changed address for you"
|
||||
RCERatchetSync syncStatus -> ratchetSyncStatusToText syncStatus
|
||||
RCEVerificationCodeReset -> "security code changed"
|
||||
RCEPQEnabled enabled
|
||||
| enabled -> "post-quantum encryption enabled"
|
||||
| otherwise -> "post-quantum encryption disabled"
|
||||
|
||||
ratchetSyncStatusToText :: RatchetSyncState -> Text
|
||||
ratchetSyncStatusToText = \case
|
||||
@@ -382,6 +413,10 @@ data JSONCIContent
|
||||
| JCISndModerated
|
||||
| JCIRcvModerated
|
||||
| JCIRcvBlocked
|
||||
| JCISndDirectE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| JCIRcvDirectE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| JCISndGroupE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| JCIRcvGroupE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| JCIInvalidJSON {direction :: MsgDirection, json :: Text}
|
||||
|
||||
jsonCIContent :: forall d. MsgDirectionI d => CIContent d -> JSONCIContent
|
||||
@@ -412,6 +447,10 @@ jsonCIContent = \case
|
||||
CISndModerated -> JCISndModerated
|
||||
CIRcvModerated -> JCIRcvModerated
|
||||
CIRcvBlocked -> JCIRcvBlocked
|
||||
CISndDirectE2EEInfo e2eeInfo -> JCISndDirectE2EEInfo e2eeInfo
|
||||
CIRcvDirectE2EEInfo e2eeInfo -> JCIRcvDirectE2EEInfo e2eeInfo
|
||||
CISndGroupE2EEInfo e2eeInfo -> JCISndGroupE2EEInfo e2eeInfo
|
||||
CIRcvGroupE2EEInfo e2eeInfo -> JCIRcvGroupE2EEInfo e2eeInfo
|
||||
CIInvalidJSON json -> JCIInvalidJSON (toMsgDirection $ msgDirection @d) json
|
||||
|
||||
aciContentJSON :: JSONCIContent -> ACIContent
|
||||
@@ -442,6 +481,10 @@ aciContentJSON = \case
|
||||
JCISndModerated -> ACIContent SMDSnd CISndModerated
|
||||
JCIRcvModerated -> ACIContent SMDRcv CIRcvModerated
|
||||
JCIRcvBlocked -> ACIContent SMDRcv CIRcvBlocked
|
||||
JCISndDirectE2EEInfo {e2eeInfo} -> ACIContent SMDSnd $ CISndDirectE2EEInfo e2eeInfo
|
||||
JCIRcvDirectE2EEInfo {e2eeInfo} -> ACIContent SMDRcv $ CIRcvDirectE2EEInfo e2eeInfo
|
||||
JCISndGroupE2EEInfo {e2eeInfo} -> ACIContent SMDSnd $ CISndGroupE2EEInfo e2eeInfo
|
||||
JCIRcvGroupE2EEInfo {e2eeInfo} -> ACIContent SMDRcv $ CIRcvGroupE2EEInfo e2eeInfo
|
||||
JCIInvalidJSON dir json -> case fromMsgDirection dir of
|
||||
AMsgDirection d -> ACIContent d $ CIInvalidJSON json
|
||||
|
||||
@@ -473,6 +516,10 @@ data DBJSONCIContent
|
||||
| DBJCISndModerated
|
||||
| DBJCIRcvModerated
|
||||
| DBJCIRcvBlocked
|
||||
| DBJCISndDirectE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| DBJCIRcvDirectE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| DBJCISndGroupE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| DBJCIRcvGroupE2EEInfo {e2eeInfo :: E2EEInfo}
|
||||
| DBJCIInvalidJSON {direction :: MsgDirection, json :: Text}
|
||||
|
||||
dbJsonCIContent :: forall d. MsgDirectionI d => CIContent d -> DBJSONCIContent
|
||||
@@ -503,6 +550,10 @@ dbJsonCIContent = \case
|
||||
CISndModerated -> DBJCISndModerated
|
||||
CIRcvModerated -> DBJCIRcvModerated
|
||||
CIRcvBlocked -> DBJCIRcvBlocked
|
||||
CISndDirectE2EEInfo e2eeInfo -> DBJCISndDirectE2EEInfo e2eeInfo
|
||||
CIRcvDirectE2EEInfo e2eeInfo -> DBJCIRcvDirectE2EEInfo e2eeInfo
|
||||
CISndGroupE2EEInfo e2eeInfo -> DBJCISndGroupE2EEInfo e2eeInfo
|
||||
CIRcvGroupE2EEInfo e2eeInfo -> DBJCIRcvGroupE2EEInfo e2eeInfo
|
||||
CIInvalidJSON json -> DBJCIInvalidJSON (toMsgDirection $ msgDirection @d) json
|
||||
|
||||
aciContentDBJSON :: DBJSONCIContent -> ACIContent
|
||||
@@ -533,6 +584,10 @@ aciContentDBJSON = \case
|
||||
DBJCISndModerated -> ACIContent SMDSnd CISndModerated
|
||||
DBJCIRcvModerated -> ACIContent SMDRcv CIRcvModerated
|
||||
DBJCIRcvBlocked -> ACIContent SMDRcv CIRcvBlocked
|
||||
DBJCISndDirectE2EEInfo e2eeInfo -> ACIContent SMDSnd $ CISndDirectE2EEInfo e2eeInfo
|
||||
DBJCIRcvDirectE2EEInfo e2eeInfo -> ACIContent SMDRcv $ CIRcvDirectE2EEInfo e2eeInfo
|
||||
DBJCISndGroupE2EEInfo e2eeInfo -> ACIContent SMDSnd $ CISndGroupE2EEInfo e2eeInfo
|
||||
DBJCIRcvGroupE2EEInfo e2eeInfo -> ACIContent SMDRcv $ CIRcvGroupE2EEInfo e2eeInfo
|
||||
DBJCIInvalidJSON dir json -> case fromMsgDirection dir of
|
||||
AMsgDirection d -> ACIContent d $ CIInvalidJSON json
|
||||
|
||||
@@ -558,6 +613,8 @@ ciCallInfoText status duration = case status of
|
||||
CISCallEnded -> "ended " <> durationText duration
|
||||
CISCallError -> "error"
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''E2EEInfo)
|
||||
|
||||
$(JQ.deriveJSON (enumJSON $ dropPrefix "MDE") ''MsgDecryptError)
|
||||
|
||||
$(JQ.deriveJSON (enumJSON $ dropPrefix "CIGIS") ''CIGroupInvitationStatus)
|
||||
@@ -626,4 +683,8 @@ toCIContentTag ciContent = case ciContent of
|
||||
CISndModerated -> "sndModerated"
|
||||
CIRcvModerated -> "rcvModerated"
|
||||
CIRcvBlocked -> "rcvBlocked"
|
||||
CISndDirectE2EEInfo _ -> "sndDirectE2EEInfo"
|
||||
CIRcvDirectE2EEInfo _ -> "rcvDirectE2EEInfo"
|
||||
CISndGroupE2EEInfo _ -> "sndGroupE2EEInfo"
|
||||
CIRcvGroupE2EEInfo _ -> "rcvGroupE2EEInfo"
|
||||
CIInvalidJSON _ -> "invalidJSON"
|
||||
|
||||
@@ -42,6 +42,7 @@ data RcvConnEvent
|
||||
= RCESwitchQueue {phase :: SwitchPhase}
|
||||
| RCERatchetSync {syncStatus :: RatchetSyncState}
|
||||
| RCEVerificationCodeReset
|
||||
| RCEPQEnabled {enabled :: Bool}
|
||||
deriving (Show)
|
||||
|
||||
data SndConnEvent
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Migrations.M20240228_pq where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20240228_pq :: Query
|
||||
m20240228_pq =
|
||||
[sql|
|
||||
ALTER TABLE connections ADD COLUMN pq_enabled INTEGER;
|
||||
|]
|
||||
|
||||
down_m20240228_pq :: Query
|
||||
down_m20240228_pq =
|
||||
[sql|
|
||||
ALTER TABLE connections DROP COLUMN pq_enabled;
|
||||
|]
|
||||
@@ -277,6 +277,7 @@ CREATE TABLE connections(
|
||||
peer_chat_max_version INTEGER NOT NULL DEFAULT 1,
|
||||
to_subscribe INTEGER DEFAULT 0 NOT NULL,
|
||||
contact_conn_initiated INTEGER NOT NULL DEFAULT 0,
|
||||
pq_enabled INTEGER,
|
||||
FOREIGN KEY(snd_file_id, connection_id)
|
||||
REFERENCES snd_files(file_id, connection_id)
|
||||
ON DELETE CASCADE
|
||||
|
||||
@@ -60,7 +60,8 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
db
|
||||
[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,
|
||||
conn_status, conn_type, contact_conn_initiated, 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, auth_err_counter,
|
||||
conn_status, conn_type, contact_conn_initiated, 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, pq_enabled, auth_err_counter,
|
||||
peer_chat_min_version, peer_chat_max_version
|
||||
FROM connections
|
||||
WHERE user_id = ? AND agent_conn_id = ?
|
||||
|
||||
@@ -173,7 +173,7 @@ getContactByConnReqHash db user@User {userId} cReqHash =
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent,
|
||||
-- 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.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
@@ -569,7 +569,7 @@ createOrUpdateContactRequest db user@User {userId} userContactLinkId invId (Vers
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent,
|
||||
-- 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.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
@@ -734,7 +734,7 @@ getContact_ db user@User {userId} contactId deleted =
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent,
|
||||
-- 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.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
@@ -787,7 +787,8 @@ getContactConnections db userId Contact {contactId} =
|
||||
db
|
||||
[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,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN contacts ct ON ct.contact_id = c.contact_id
|
||||
@@ -804,7 +805,8 @@ getConnectionById db User {userId} connId = ExceptT $ do
|
||||
db
|
||||
[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,
|
||||
conn_status, conn_type, contact_conn_initiated, 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, auth_err_counter,
|
||||
conn_status, conn_type, contact_conn_initiated, 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, pq_enabled, auth_err_counter,
|
||||
peer_chat_min_version, peer_chat_max_version
|
||||
FROM connections
|
||||
WHERE user_id = ? AND connection_id = ?
|
||||
|
||||
@@ -193,7 +193,8 @@ getGroupLinkConnection db User {userId} groupInfo@GroupInfo {groupId} =
|
||||
db
|
||||
[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,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
@@ -278,7 +279,8 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr =
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, 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.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
@@ -685,7 +687,8 @@ groupMemberQuery =
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, 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.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
@@ -1289,7 +1292,8 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} =
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, 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.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contacts ct ON ct.contact_id = m.contact_id
|
||||
@@ -1909,7 +1913,27 @@ createMemberContact
|
||||
:. (minV, maxV, currentTs, currentTs, subMode == SMOnlyCreate)
|
||||
)
|
||||
connId <- insertedRowId db
|
||||
let ctConn = Connection {connId, agentConnId = AgentConnId acId, peerChatVRange, connType = ConnContact, contactConnInitiated = True, entityId = Just contactId, viaContact = Nothing, viaUserContactLink = Nothing, viaGroupLink = False, groupLinkId = Nothing, customUserProfileId, connLevel, connStatus = ConnNew, localAlias = "", createdAt = currentTs, connectionCode = Nothing, authErrCounter = 0}
|
||||
let ctConn =
|
||||
Connection
|
||||
{ connId,
|
||||
agentConnId = AgentConnId acId,
|
||||
peerChatVRange,
|
||||
connType = ConnContact,
|
||||
contactConnInitiated = True,
|
||||
entityId = Just contactId,
|
||||
viaContact = Nothing,
|
||||
viaUserContactLink = Nothing,
|
||||
viaGroupLink = False,
|
||||
groupLinkId = Nothing,
|
||||
customUserProfileId,
|
||||
connLevel,
|
||||
connStatus = ConnNew,
|
||||
localAlias = "",
|
||||
createdAt = currentTs,
|
||||
connectionCode = Nothing,
|
||||
pqEnabled = Nothing,
|
||||
authErrCounter = 0
|
||||
}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False}
|
||||
|
||||
@@ -2018,7 +2042,27 @@ createMemberContactConn_
|
||||
)
|
||||
connId <- insertedRowId db
|
||||
setCommandConnId db user cmdId connId
|
||||
pure Connection {connId, agentConnId = AgentConnId acId, peerChatVRange, connType = ConnContact, contactConnInitiated = False, entityId = Just contactId, viaContact = Nothing, viaUserContactLink = Nothing, viaGroupLink = False, groupLinkId = Nothing, customUserProfileId, connLevel, connStatus = ConnJoined, localAlias = "", createdAt = currentTs, connectionCode = Nothing, authErrCounter = 0}
|
||||
pure
|
||||
Connection
|
||||
{ connId,
|
||||
agentConnId = AgentConnId acId,
|
||||
peerChatVRange,
|
||||
connType = ConnContact,
|
||||
contactConnInitiated = False,
|
||||
entityId = Just contactId,
|
||||
viaContact = Nothing,
|
||||
viaUserContactLink = Nothing,
|
||||
viaGroupLink = False,
|
||||
groupLinkId = Nothing,
|
||||
customUserProfileId,
|
||||
connLevel,
|
||||
connStatus = ConnJoined,
|
||||
localAlias = "",
|
||||
createdAt = currentTs,
|
||||
connectionCode = Nothing,
|
||||
pqEnabled = Nothing,
|
||||
authErrCounter = 0
|
||||
}
|
||||
|
||||
updateMemberProfile :: DB.Connection -> User -> GroupMember -> Profile -> ExceptT StoreError IO GroupMember
|
||||
updateMemberProfile db user@User {userId} m p'
|
||||
|
||||
@@ -101,6 +101,7 @@ import Simplex.Chat.Migrations.M20240122_indexes
|
||||
import Simplex.Chat.Migrations.M20240214_redirect_file_id
|
||||
import Simplex.Chat.Migrations.M20240222_app_settings
|
||||
import Simplex.Chat.Migrations.M20240226_users_restrict
|
||||
import Simplex.Chat.Migrations.M20240228_pq
|
||||
import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -201,7 +202,8 @@ schemaMigrations =
|
||||
("20240122_indexes", m20240122_indexes, Just down_m20240122_indexes),
|
||||
("20240214_redirect_file_id", m20240214_redirect_file_id, Just down_m20240214_redirect_file_id),
|
||||
("20240222_app_settings", m20240222_app_settings, Just down_m20240222_app_settings),
|
||||
("20240226_users_restrict", m20240226_users_restrict, Just down_m20240226_users_restrict)
|
||||
("20240226_users_restrict", m20240226_users_restrict, Just down_m20240226_users_restrict),
|
||||
("20240228_pq", m20240228_pq, Just down_m20240228_pq)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -337,7 +337,8 @@ getUserAddressConnections db User {userId} = do
|
||||
db
|
||||
[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,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
@@ -352,7 +353,8 @@ getUserContactLinks db User {userId} =
|
||||
db
|
||||
[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,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.auth_err_counter,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, 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, c.pq_enabled, c.auth_err_counter,
|
||||
c.peer_chat_min_version, c.peer_chat_max_version,
|
||||
uc.user_contact_link_id, uc.conn_req_contact, uc.group_id
|
||||
FROM connections c
|
||||
|
||||
@@ -148,16 +148,32 @@ toFileInfo (fileId, fileStatus, filePath) = CIFileInfo {fileId, fileStatus, file
|
||||
|
||||
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, Bool, LocalAlias) :. EntityIdsRow :. (UTCTime, Maybe Text, Maybe UTCTime, Int, Version, Version)
|
||||
type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, Maybe Int64, Bool, Maybe GroupLinkId, Maybe Int64, ConnStatus, ConnType, Bool, LocalAlias) :. EntityIdsRow :. (UTCTime, Maybe Text, Maybe UTCTime, Maybe Bool, Int, Version, Version)
|
||||
|
||||
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe Bool, Maybe GroupLinkId, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Bool, Maybe LocalAlias) :. EntityIdsRow :. (Maybe UTCTime, Maybe Text, Maybe UTCTime, Maybe Int, Maybe Version, Maybe Version)
|
||||
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe Bool, Maybe GroupLinkId, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Bool, Maybe LocalAlias) :. EntityIdsRow :. (Maybe UTCTime, Maybe Text, Maybe UTCTime, Maybe Bool, Maybe Int, Maybe Version, Maybe Version)
|
||||
|
||||
toConnection :: ConnectionRow -> Connection
|
||||
toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, authErrCounter, minVer, maxVer)) =
|
||||
let entityId = entityId_ connType
|
||||
connectionCode = SecurityCode <$> code_ <*> verifiedAt_
|
||||
peerChatVRange = JVersionRange $ fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
||||
in Connection {connId, agentConnId = AgentConnId acId, peerChatVRange, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias, entityId, connectionCode, authErrCounter, createdAt}
|
||||
toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, pqEnabled, authErrCounter, minVer, maxVer)) =
|
||||
Connection
|
||||
{ connId,
|
||||
agentConnId = AgentConnId acId,
|
||||
peerChatVRange = JVersionRange $ fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer,
|
||||
connLevel,
|
||||
viaContact,
|
||||
viaUserContactLink,
|
||||
viaGroupLink,
|
||||
groupLinkId,
|
||||
customUserProfileId,
|
||||
connStatus,
|
||||
connType,
|
||||
contactConnInitiated,
|
||||
localAlias,
|
||||
entityId = entityId_ connType,
|
||||
connectionCode = SecurityCode <$> code_ <*> verifiedAt_,
|
||||
pqEnabled,
|
||||
authErrCounter,
|
||||
createdAt
|
||||
}
|
||||
where
|
||||
entityId_ :: ConnType -> Maybe Int64
|
||||
entityId_ ConnContact = contactId
|
||||
@@ -167,8 +183,8 @@ toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroup
|
||||
entityId_ ConnUserContact = userContactLinkId
|
||||
|
||||
toMaybeConnection :: MaybeConnectionRow -> Maybe Connection
|
||||
toMaybeConnection ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just viaGroupLink, groupLinkId, customUserProfileId, Just connStatus, Just connType, Just contactConnInitiated, Just localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (Just createdAt, code_, verifiedAt_, Just authErrCounter, Just minVer, Just maxVer)) =
|
||||
Just $ toConnection ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, authErrCounter, minVer, maxVer))
|
||||
toMaybeConnection ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just viaGroupLink, groupLinkId, customUserProfileId, Just connStatus, Just connType, Just contactConnInitiated, Just localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (Just createdAt, code_, verifiedAt_, pqEnabled_, Just authErrCounter, Just minVer, Just maxVer)) =
|
||||
Just $ toConnection ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, pqEnabled_, authErrCounter, minVer, maxVer))
|
||||
toMaybeConnection _ = Nothing
|
||||
|
||||
createConnection_ :: DB.Connection -> UserId -> ConnType -> Maybe Int64 -> ConnId -> VersionRange -> Maybe ContactId -> Maybe Int64 -> Maybe ProfileId -> Int -> UTCTime -> SubscriptionMode -> IO Connection
|
||||
@@ -190,7 +206,27 @@ createConnection_ db userId connType entityId acId peerChatVRange@(VersionRange
|
||||
:. (minV, maxV, subMode == SMOnlyCreate)
|
||||
)
|
||||
connId <- insertedRowId db
|
||||
pure Connection {connId, agentConnId = AgentConnId acId, peerChatVRange = JVersionRange peerChatVRange, connType, contactConnInitiated = False, entityId, viaContact, viaUserContactLink, viaGroupLink, groupLinkId = Nothing, customUserProfileId, connLevel, connStatus = ConnNew, localAlias = "", createdAt = currentTs, connectionCode = Nothing, authErrCounter = 0}
|
||||
pure
|
||||
Connection
|
||||
{ connId,
|
||||
agentConnId = AgentConnId acId,
|
||||
peerChatVRange = JVersionRange peerChatVRange,
|
||||
connType,
|
||||
contactConnInitiated = False,
|
||||
entityId,
|
||||
viaContact,
|
||||
viaUserContactLink,
|
||||
viaGroupLink,
|
||||
groupLinkId = Nothing,
|
||||
customUserProfileId,
|
||||
connLevel,
|
||||
connStatus = ConnNew,
|
||||
localAlias = "",
|
||||
createdAt = currentTs,
|
||||
connectionCode = Nothing,
|
||||
pqEnabled = Nothing,
|
||||
authErrCounter = 0
|
||||
}
|
||||
where
|
||||
ent ct = if connType == ct then entityId else Nothing
|
||||
|
||||
@@ -205,6 +241,17 @@ createIncognitoProfile_ db userId createdAt Profile {displayName, fullName, imag
|
||||
(displayName, fullName, image, userId, Just True, createdAt, createdAt)
|
||||
insertedRowId db
|
||||
|
||||
updateConnPQEnabled :: DB.Connection -> Int64 -> Bool -> IO ()
|
||||
updateConnPQEnabled db connId pqEnabled =
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE connections
|
||||
SET pq_enabled = ?
|
||||
WHERE connection_id = ?
|
||||
|]
|
||||
(pqEnabled, connId)
|
||||
|
||||
setPeerChatVRange :: DB.Connection -> Int64 -> VersionRange -> IO ()
|
||||
setPeerChatVRange db connId (VersionRange minVer maxVer) =
|
||||
DB.execute
|
||||
|
||||
@@ -211,6 +211,11 @@ contactDeleted Contact {contactStatus} = contactStatus == CSDeleted
|
||||
contactSecurityCode :: Contact -> Maybe SecurityCode
|
||||
contactSecurityCode Contact {activeConn} = connectionCode =<< activeConn
|
||||
|
||||
contactPQEnabled :: Contact -> Bool
|
||||
contactPQEnabled Contact {activeConn} = case activeConn of
|
||||
Just Connection {pqEnabled} -> pqEnabled == Just True
|
||||
Nothing -> False
|
||||
|
||||
data ContactStatus
|
||||
= CSActive
|
||||
| CSDeleted -- contact deleted by contact
|
||||
@@ -563,7 +568,8 @@ data GroupInvitation = GroupInvitation
|
||||
invitedMember :: MemberIdRole,
|
||||
connRequest :: ConnReqInvitation,
|
||||
groupProfile :: GroupProfile,
|
||||
groupLinkId :: Maybe GroupLinkId
|
||||
groupLinkId :: Maybe GroupLinkId,
|
||||
groupSize :: Maybe Int
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -571,7 +577,8 @@ data GroupLinkInvitation = GroupLinkInvitation
|
||||
{ fromMember :: MemberIdRole,
|
||||
fromMemberName :: ContactName,
|
||||
invitedMember :: MemberIdRole,
|
||||
groupProfile :: GroupProfile
|
||||
groupProfile :: GroupProfile,
|
||||
groupSize :: Maybe Int
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -1277,6 +1284,8 @@ type ConnReqInvitation = ConnectionRequestUri 'CMInvitation
|
||||
|
||||
type ConnReqContact = ConnectionRequestUri 'CMContact
|
||||
|
||||
type PQFlag = Bool
|
||||
|
||||
data Connection = Connection
|
||||
{ connId :: Int64,
|
||||
agentConnId :: AgentConnId,
|
||||
@@ -1293,6 +1302,7 @@ data Connection = Connection
|
||||
localAlias :: Text,
|
||||
entityId :: Maybe Int64, -- contact, group member, file ID or user contact ID
|
||||
connectionCode :: Maybe SecurityCode,
|
||||
pqEnabled :: Maybe PQFlag,
|
||||
authErrCounter :: Int,
|
||||
createdAt :: UTCTime
|
||||
}
|
||||
|
||||
@@ -340,6 +340,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} ->
|
||||
["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName]
|
||||
CRRemoteCtrlStopped {} -> ["remote controller stopped"]
|
||||
CRContactPQEnabled u c pqOn -> ttyUser u [ttyContact' c <> ": post-quantum encryption " <> (if pqOn then "enabled" else "disabled")]
|
||||
CRSQLResult rows -> map plain rows
|
||||
CRSlowSQLQueries {chatQueries, agentQueries} ->
|
||||
let viewQuery SlowSQLQuery {query, queryStats = SlowQueryStats {count, timeMax, timeAvg}} =
|
||||
@@ -1173,6 +1174,7 @@ viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, conta
|
||||
incognitoProfile
|
||||
<> ["alias: " <> plain localAlias | localAlias /= ""]
|
||||
<> [viewConnectionVerified (contactSecurityCode ct)]
|
||||
<> ["post-quantum encryption enabled" | contactPQEnabled ct]
|
||||
<> maybe [] (\ac -> [viewPeerChatVRange (peerChatVRange ac)]) activeConn
|
||||
|
||||
viewGroupInfo :: GroupInfo -> GroupSummary -> [StyledString]
|
||||
|
||||
Reference in New Issue
Block a user