mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 00:44:27 +00:00
core: support contact addresses with DR keys, service requests (#7310)
* core: use double ratchet keys in contact address (#7278) * core: use double ratchet keys in contact address * use PQ from the first message * query plans * update simplexmq * api to rotate keys, option to show full links in CLI * shorter description * ui: add error parameters * disable DR in addresses * core: parameter for create address command to configure ratchet keys * add pqRatchet param to address-related commands * query plan * fix parser * fix kotlin --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> * core: contact request rejection and service requests (#7292) * update simplexmq * implement service requests and rejections * tests * migration * fix migration * add api event and response * bot api, postgres migration * nix shas * bot types, rename property * update bot type * sign service requests * update bots api * query plan * update plan * update simplexmq * fix test, update bot api * fix bot api * resolve name for service request * refactor --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> * update simplexmq * update simplexmq * test delays --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
co-authored by
Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
parent
61012d208e
commit
e1a349b90f
+5
-2
@@ -116,6 +116,7 @@ defaultChatConfig =
|
||||
inlineFiles = defaultInlineFilesConfig,
|
||||
autoAcceptFileSize = 0,
|
||||
showReactions = False,
|
||||
showFullLinks = False,
|
||||
showReceipts = False,
|
||||
logLevel = CLLImportant,
|
||||
subscriptionEvents = False,
|
||||
@@ -153,11 +154,11 @@ newChatController
|
||||
ChatDatabase {chatStore, agentStore}
|
||||
user
|
||||
cfg@ChatConfig {agentConfig = aCfg, presetServers, inlineFiles, deviceNameForRemote, confirmMigrations}
|
||||
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, deviceName, webPreviewConfig, highlyAvailable, yesToUpMigrations}, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize}
|
||||
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, deviceName, webPreviewConfig, highlyAvailable, yesToUpMigrations}, optFilesFolder, optTempDirectory, showReactions, showFullLinks, allowInstantFiles, autoAcceptFileSize}
|
||||
backgroundMode = do
|
||||
let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
|
||||
confirmMigrations' = if confirmMigrations == MCConsole && yesToUpMigrations then MCYesUp else confirmMigrations
|
||||
config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, presetServers = presetServers', inlineFiles = inlineFiles', autoAcceptFileSize, webPreviewConfig, highlyAvailable, confirmMigrations = confirmMigrations'}
|
||||
config = cfg {logLevel, showReactions, showFullLinks, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, presetServers = presetServers', inlineFiles = inlineFiles', autoAcceptFileSize, webPreviewConfig, highlyAvailable, confirmMigrations = confirmMigrations'}
|
||||
randomPresetServers <- chooseRandomServers presetServers'
|
||||
let rndSrvs = L.toList randomPresetServers
|
||||
operatorWithId (i, op) = (\o -> o {operatorId = DBEntityId i}) <$> pOperator op
|
||||
@@ -178,6 +179,7 @@ newChatController
|
||||
inputQ <- newTBQueueIO tbqSize
|
||||
outputQ <- newTBQueueIO tbqSize
|
||||
subscriptionMode <- newTVarIO SMSubscribe
|
||||
processServiceRequests <- newTVarIO False
|
||||
chatLock <- newEmptyTMVarIO
|
||||
entityLocks <- TM.emptyIO
|
||||
sndFiles <- newTVarIO M.empty
|
||||
@@ -223,6 +225,7 @@ newChatController
|
||||
inputQ,
|
||||
outputQ,
|
||||
subscriptionMode,
|
||||
processServiceRequests,
|
||||
chatLock,
|
||||
entityLocks,
|
||||
sndFiles,
|
||||
|
||||
@@ -56,7 +56,7 @@ initializeBotAddress' logAddress cc = do
|
||||
Left (ChatErrorStore SEUserContactLinkNotFound) -> do
|
||||
when logAddress $ putStrLn "No bot address, creating..."
|
||||
-- TODO [short links] create short link by default
|
||||
sendChatCmd cc CreateMyAddress >>= \case
|
||||
sendChatCmd cc (CreateMyAddress Nothing) >>= \case
|
||||
Right (CRUserContactLinkCreated _ ccLink) -> showBotAddress ccLink
|
||||
_ -> putStrLn "can't create bot address" >> exitFailure
|
||||
_ -> putStrLn "unexpected response" >> exitFailure
|
||||
@@ -66,7 +66,7 @@ initializeBotAddress' logAddress cc = do
|
||||
putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri)
|
||||
when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri)
|
||||
let settings = AddressSettings {businessAddress = False, autoAccept = Just AutoAccept {acceptIncognito = False}, autoReply = Nothing}
|
||||
void $ sendChatCmd cc $ SetAddressSettings settings
|
||||
void $ sendChatCmd cc $ SetAddressSettings Nothing settings
|
||||
|
||||
sendMessage :: ChatController -> Contact -> Text -> IO ()
|
||||
sendMessage cc ct = sendComposedMessage cc ct Nothing . MCText
|
||||
|
||||
@@ -90,7 +90,7 @@ import Simplex.Messaging.Crypto.Ratchet (PQEncryption)
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), QueueId, SMPMsgMeta (..), SMPServerWithAuth, SubscriptionMode (..), XFTPServer)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), QueueId, SMPMsgMeta (..), SubscriptionMode (..), XFTPServer)
|
||||
import Simplex.Messaging.TMap (TMap)
|
||||
import Simplex.Messaging.Transport (TLS, TransportPeer (..), simplexMQVersion)
|
||||
import Simplex.Messaging.Transport.Client (SocksProxyWithAuth, TransportHost)
|
||||
@@ -152,6 +152,7 @@ data ChatConfig = ChatConfig
|
||||
inlineFiles :: InlineFilesConfig,
|
||||
autoAcceptFileSize :: Integer,
|
||||
showReactions :: Bool,
|
||||
showFullLinks :: Bool,
|
||||
showReceipts :: Bool,
|
||||
subscriptionEvents :: Bool,
|
||||
hostEvents :: Bool,
|
||||
@@ -288,6 +289,7 @@ data ChatController = ChatController
|
||||
inputQ :: TBQueue String,
|
||||
outputQ :: TBQueue (Maybe RemoteHostId, Either ChatError ChatEvent),
|
||||
subscriptionMode :: TVar SubscriptionMode,
|
||||
processServiceRequests :: TVar Bool,
|
||||
chatLock :: Lock,
|
||||
entityLocks :: TMap ChatLockEntity Lock,
|
||||
sndFiles :: TVar (Map Int64 Handle),
|
||||
@@ -347,7 +349,7 @@ data ChatCommand
|
||||
| SetClientService UserId ContactName Bool
|
||||
| APIDeleteUser {userId :: UserId, delSMPQueues :: Bool, viewPwd :: Maybe UserPwd}
|
||||
| DeleteUser UserName Bool (Maybe UserPwd)
|
||||
| StartChat {mainApp :: Bool, enableSndFiles :: Bool} -- enableSndFiles has no effect when mainApp is True
|
||||
| StartChat {mainApp :: Bool, enableSndFiles :: Bool, serviceRequests :: Bool} -- enableSndFiles has no effect when mainApp is True
|
||||
| CheckChatRunning
|
||||
| APIStopChat
|
||||
| APIActivateChat {restoreChat :: Bool}
|
||||
@@ -407,7 +409,9 @@ data ChatCommand
|
||||
| APIDeleteChat {chatRef :: ChatRef, chatDeleteMode :: ChatDeleteMode} -- currently delete mode settings are only applied to direct chats
|
||||
| APIClearChat {chatRef :: ChatRef}
|
||||
| APIAcceptContact {incognito :: IncognitoEnabled, contactReqId :: Int64}
|
||||
| APIRejectContact {contactReqId :: Int64}
|
||||
| APIRejectContact {contactReqId :: Int64, notify :: Bool}
|
||||
| APISendServiceRequest {userId :: UserId, sendTarget :: ConnectTarget 'CMContact, requestTimeout :: Maybe NominalDiffTime, signKey :: Maybe (C.StoredPrivateKey 'C.Ed25519), request :: J.Object}
|
||||
| APISendServiceResponse {userId :: UserId, requestId :: AgentInvId, responseData :: J.Object}
|
||||
| APISendCallInvitation ContactId CallType
|
||||
| SendCallInvitation ContactName CallType
|
||||
| APIRejectCall ContactId
|
||||
@@ -547,19 +551,20 @@ data ChatCommand
|
||||
| ClearContact ContactName
|
||||
| APIListContacts {userId :: UserId}
|
||||
| ListContacts
|
||||
| APICreateMyAddress {userId :: UserId, server_ :: Maybe SMPServerWithAuth}
|
||||
| CreateMyAddress
|
||||
| APICreateMyAddress {userId :: UserId, server_ :: Maybe SMPServerWithAuth, pqRatchet :: Maybe Bool}
|
||||
| CreateMyAddress {pqRatchet :: Maybe Bool}
|
||||
| APIDeleteMyAddress {userId :: UserId}
|
||||
| DeleteMyAddress
|
||||
| APIShowMyAddress {userId :: UserId}
|
||||
| ShowMyAddress
|
||||
| APIAddMyAddressShortLink UserId
|
||||
| APIAddMyAddressShortLink {userId :: UserId, pqRatchet :: Maybe Bool}
|
||||
| APIRotateAddressRatchetKeys UserId
|
||||
| APISetProfileAddress {userId :: UserId, enable :: Bool}
|
||||
| SetProfileAddress Bool
|
||||
| APISetAddressSettings {userId :: UserId, settings :: AddressSettings}
|
||||
| SetAddressSettings AddressSettings
|
||||
| APISetAddressSettings {userId :: UserId, pqRatchet :: Maybe Bool, settings :: AddressSettings}
|
||||
| SetAddressSettings {pqRatchet :: Maybe Bool, settings :: AddressSettings}
|
||||
| AcceptContact IncognitoEnabled ContactName
|
||||
| RejectContact ContactName
|
||||
| RejectContact ContactName Bool
|
||||
| ForwardMessage {toChatName :: ChatName, fromContactName :: ContactName, forwardedMsg :: Text}
|
||||
| ForwardGroupMessage {toChatName :: ChatName, fromGroupName :: GroupName, fromMemberName_ :: Maybe ContactName, forwardedMsg :: Text}
|
||||
| ForwardLocalMessage {toChatName :: ChatName, forwardedMsg :: Text}
|
||||
@@ -826,6 +831,8 @@ data ChatResponse
|
||||
| CRUserContactLink {user :: User, contactLink :: UserContactLink}
|
||||
| CRUserContactLinkUpdated {user :: User, contactLink :: UserContactLink}
|
||||
| CRContactRequestRejected {user :: User, contactRequest :: UserContactRequest, contact_ :: Maybe Contact}
|
||||
| CRServiceResponse {user :: User, responseData :: J.Object}
|
||||
| CRServiceReplyAccepted {user :: User, connectionId :: AgentConnId}
|
||||
| CRUserAcceptedGroupSent {user :: User, groupInfo :: GroupInfo, hostContact :: Maybe Contact}
|
||||
| CRUserDeletedMembers {user :: User, groupInfo :: GroupInfo, members :: [GroupMember], withMessages :: Bool, msgSigned :: Bool}
|
||||
| CRGroupsList {user :: User, groups :: [GroupInfo]}
|
||||
@@ -941,6 +948,9 @@ data ChatEvent
|
||||
| CEvtGroupMemberUpdated {user :: User, groupInfo :: GroupInfo, fromMember :: GroupMember, toMember :: GroupMember}
|
||||
| CEvtContactDeletedByContact {user :: User, contact :: Contact}
|
||||
| CEvtReceivedContactRequest {user :: User, contactRequest :: UserContactRequest, chat_ :: Maybe AChat}
|
||||
| CEvtServiceRequest {user :: User, requestId :: AgentInvId, signerKey :: Maybe C.PublicKeyEd25519, requestData :: J.Object}
|
||||
| CEvtServiceReplySent {connectionId :: AgentConnId}
|
||||
| CEvtContactRequestRejected {user :: User, contact :: Contact, rejectionReason :: Maybe ContactRejectionReason}
|
||||
| CEvtAcceptingContactRequest {user :: User, contact :: Contact} -- there is the same command response
|
||||
| CEvtAcceptingBusinessRequest {user :: User, groupInfo :: GroupInfo}
|
||||
| CEvtContactRequestAlreadyAccepted {user :: User, contact :: Contact}
|
||||
|
||||
@@ -90,7 +90,7 @@ runSimplexChat :: ChatConfig -> ChatOpts -> User -> ChatController -> (User -> C
|
||||
runSimplexChat ChatConfig {testView} ChatOpts {coreOptions = CoreChatOpts {chatRelay, chatRelayServer, headless, maintenance}} u cc@ChatController {config = ChatConfig {chatHooks}} chat
|
||||
| maintenance = wait =<< async (chat u cc)
|
||||
| otherwise = do
|
||||
a1 <- runReaderT (startChatController True True) cc
|
||||
a1 <- runReaderT (startChatController True True False) cc
|
||||
when (chatRelay && not testView) $ askCreateRelayAddress cc u chatRelayServer headless
|
||||
forM_ (postStartHook chatHooks) ($ cc)
|
||||
a2 <- async $ chat u cc
|
||||
@@ -175,7 +175,7 @@ askCreateRelayAddress cc@ChatController {chatStore} user@User {userId} server_ h
|
||||
promptCreate = do
|
||||
ok <- if headless then pure True else onOffPrompt "Create relay address" True
|
||||
when ok $
|
||||
execChatCommand' (APICreateMyAddress userId server_) 0 `runReaderT` cc >>= \case
|
||||
execChatCommand' (APICreateMyAddress userId server_ Nothing) 0 `runReaderT` cc >>= \case
|
||||
Right (CRUserContactLinkCreated _ address) -> do
|
||||
putStrLn "Chat relay address is created:"
|
||||
putStrLn $ addressStr address
|
||||
|
||||
@@ -102,12 +102,12 @@ import Simplex.Messaging.Agent.Store.Interface (execSQL)
|
||||
import Simplex.Messaging.Agent.Store.Shared (upMigration)
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.Interface (getCurrentMigrations)
|
||||
import Simplex.Messaging.Client (NetworkConfig (..), NetworkRequestMode (..), NetworkTimeout (..), SMPWebPortServers (..), SocksMode (SMAlways), textToHostMode)
|
||||
import Simplex.Messaging.Client (NetworkConfig (..), NetworkRequestMode (..), NetworkTimeout (..), SMPWebPortServers (..), SocksMode (SMAlways), pattern NRMInteractive, textToHostMode)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import qualified Simplex.Messaging.Crypto.ShortLink as SL
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import Simplex.Messaging.Crypto.Ratchet (E2ERatchetParamsUri (..), InitialKeys (..), PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import Simplex.Messaging.Encoding
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (base64P)
|
||||
@@ -216,9 +216,10 @@ videoFilePrefix :: String
|
||||
videoFilePrefix = "video_"
|
||||
|
||||
-- enableSndFiles has no effect when mainApp is True
|
||||
startChatController :: Bool -> Bool -> CM' (Async ())
|
||||
startChatController mainApp enableSndFiles = do
|
||||
startChatController :: Bool -> Bool -> Bool -> CM' (Async ())
|
||||
startChatController mainApp enableSndFiles serviceRequests = do
|
||||
asks smpAgent >>= liftIO . resumeAgentClient
|
||||
chatWriteVar' processServiceRequests serviceRequests
|
||||
unless mainApp $ chatWriteVar' subscriptionMode SMOnlyCreate
|
||||
users <- fromRight [] <$> runExceptT (withFastStore' getUsers)
|
||||
runExceptT (syncConnections' users) >>= \case
|
||||
@@ -548,10 +549,10 @@ processChatCommand cxt nm = \case
|
||||
checkDeleteChatUser user'
|
||||
withChatLock "deleteUser" $ deleteChatUser user' delSMPQueues
|
||||
DeleteUser uName delSMPQueues viewPwd_ -> withUserName uName $ \userId -> APIDeleteUser userId delSMPQueues viewPwd_
|
||||
StartChat {mainApp, enableSndFiles} -> withUser' $ \_ ->
|
||||
StartChat {mainApp, enableSndFiles, serviceRequests} -> withUser' $ \_ ->
|
||||
asks agentAsync >>= readTVarIO >>= \case
|
||||
Just _ -> pure CRChatRunning
|
||||
_ -> checkStoreNotChanged . lift $ startChatController mainApp enableSndFiles $> CRChatStarted
|
||||
_ -> checkStoreNotChanged . lift $ startChatController mainApp enableSndFiles serviceRequests $> CRChatStarted
|
||||
CheckChatRunning -> maybe CRChatStopped (const CRChatRunning) <$> chatReadVar agentAsync
|
||||
APIStopChat -> do
|
||||
ask >>= liftIO . stopChatController
|
||||
@@ -1429,24 +1430,49 @@ processChatCommand cxt nm = \case
|
||||
(msg, _) <- sendDirectContactMessage user ct $ XMsgNew $ mcSimple mc
|
||||
ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndMsgContent mc)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci]
|
||||
APIRejectContact connReqId -> withUser $ \user -> do
|
||||
APIRejectContact connReqId notify -> withUser $ \user -> do
|
||||
uclId_ <- withFastStore $ \db -> getUserContactLinkIdByCReq db connReqId
|
||||
withContactRequestLock "rejectContact" connReqId $ case uclId_ of
|
||||
Nothing -> rejectCReq user -- address was deleted
|
||||
Just uclId -> withUserContactLock "rejectContact" uclId $ rejectCReq user
|
||||
where
|
||||
rejectCReq user = do
|
||||
(cReq@UserContactRequest {agentInvitationId = AgentInvId invId}, ct_) <-
|
||||
cReq@UserContactRequest {agentInvitationId = AgentInvId invId, contactId_} <-
|
||||
withFastStore $ \db -> getContactRequest db user connReqId
|
||||
withAgent $ \a -> rejectContact a NRMInteractive (aUserId user) invId (if notify then Just (strEncode CRRUserRejected) else Nothing)
|
||||
ct_ <-
|
||||
withFastStore $ \db -> do
|
||||
cReq@UserContactRequest {contactId_} <- getContactRequest db user connReqId
|
||||
ct_ <- forM contactId_ $ \contactId -> do
|
||||
ct <- getContact db cxt user contactId
|
||||
deleteContact db user ct
|
||||
pure ct
|
||||
liftIO $ deleteContactRequest db user connReqId
|
||||
pure (cReq, ct_)
|
||||
withAgent (`rejectContact` invId)
|
||||
pure ct_
|
||||
pure $ CRContactRequestRejected user cReq ct_
|
||||
APISendServiceRequest userId sendTarget requestTimeout signKey request -> withUserId userId $ \user -> do
|
||||
cReq <- resolveServiceTarget user sendTarget
|
||||
respData <- withAgent $ \a -> sendServiceRequestAsync a (aUserId user) cReq requestTimeout (C.unStored <$> signKey) (LB.toStrict $ J.encode request)
|
||||
resp <- either (const $ throwCmdError "invalid service response") pure $ J.eitherDecodeStrict' respData
|
||||
pure $ CRServiceResponse user resp
|
||||
where
|
||||
resolveServiceTarget user = \case
|
||||
CTFullContact cReq -> pure cReq
|
||||
CTShortContact (CTLink sLnk) -> resolveShortLink sLnk
|
||||
CTShortContact (CTName SimplexNameInfo {nameType, nameDomain}) -> case nameType of
|
||||
NTContact -> resolveDomain nameDomain
|
||||
_ -> throwCmdError "service request target must be a contact"
|
||||
CTDomain d -> resolveDomain d
|
||||
where
|
||||
resolveDomain d = do
|
||||
nr <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) d
|
||||
case firstNameLink CCTContact (nrSimplexContact nr) of
|
||||
Just sLnk -> resolveShortLink sLnk
|
||||
Nothing -> throwChatError $ CESimplexDomainNotReady d SDENoValidLink
|
||||
resolveShortLink sLnk = (\(_, _, cReq) -> cReq) <$> getShortLinkConnReq nm user sLnk
|
||||
APISendServiceResponse userId requestId responseData -> withUserId userId $ \user -> do
|
||||
let AgentInvId invId = requestId
|
||||
connId <- withAgent $ \a -> sendServiceReplyAsync a "" (aUserId user) invId (LB.toStrict $ J.encode responseData)
|
||||
pure $ CRServiceReplyAccepted user (AgentConnId connId)
|
||||
APISendCallInvitation contactId callType -> withUser $ \user -> do
|
||||
-- party initiating call
|
||||
ct <- withFastStore $ \db -> getContact db cxt user contactId
|
||||
@@ -1658,7 +1684,7 @@ processChatCommand cxt nm = \case
|
||||
r <- tryAllErrors $ getShortLinkConnReq nm user address
|
||||
case r of
|
||||
Left e -> failAt RTSGetLink e
|
||||
Right (FixedLinkData {rootKey, linkConnReq = cReq}, cData) -> do
|
||||
Right (FixedLinkData {rootKey}, cData, cReq) -> do
|
||||
relayProfile_ <- liftIO $ decodeLinkUserData cData
|
||||
case relayProfile_ of
|
||||
Nothing -> failAt RTSDecodeLink (ChatError $ CERelayTestError "no relay address link data")
|
||||
@@ -1903,7 +1929,7 @@ processChatCommand cxt nm = \case
|
||||
gInfo@GroupInfo {groupProfile = p} <- withFastStore $ \db -> getGroupInfo db cxt user groupId
|
||||
case p of
|
||||
GroupProfile {publicGroup = Just PublicGroupProfile {groupLink = sLnk}} | useRelays' gInfo -> do
|
||||
(_, cData@(ContactLinkData _ UserContactData {relays = currentRelayLinks})) <- getShortLinkConnReq' nm user sLnk
|
||||
(_, cData@(ContactLinkData _ UserContactData {relays = currentRelayLinks}), _) <- getShortLinkConnReq' nm user sLnk
|
||||
groupSLinkData_ <- liftIO $ decodeLinkUserData cData
|
||||
gInfo' <- case groupSLinkData_ of
|
||||
Just sLinkData -> fst <$> updateGroupFromLinkData user gInfo sLinkData Nothing
|
||||
@@ -2071,7 +2097,7 @@ processChatCommand cxt nm = \case
|
||||
linkProfile <- presentUserBadge user incognitoProfile $ userProfileDirect user incognitoProfile Nothing True
|
||||
let userData = contactShortLinkData linkProfile {contactDomain = Nothing} Nothing
|
||||
userLinkData = UserInvLinkData userData
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation (Just userLinkData) Nothing IKPQOn subMode
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation (Just userLinkData) Nothing IKUsePQ True subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
-- TODO PQ pass minVersion from the current range
|
||||
conn <- withFastStore' $ \db -> createDirectConnection db user connId ccLink' Nothing ConnNew incognitoProfile subMode initialChatVersion PQSupportOn
|
||||
@@ -2113,7 +2139,7 @@ processChatCommand cxt nm = \case
|
||||
if short
|
||||
then Just . UserInvLinkData . (`contactShortLinkData` Nothing) <$> presentUserBadge newUser Nothing (userProfileDirect newUser Nothing Nothing True)
|
||||
else pure Nothing
|
||||
(agConnId, ccLink) <- withAgent $ \a -> createConnection a nm (aUserId newUser) True False SCMInvitation userLinkData_ Nothing IKPQOn subMode
|
||||
(agConnId, ccLink) <- withAgent $ \a -> createConnection a nm (aUserId newUser) True False SCMInvitation userLinkData_ Nothing IKPQOn True subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
conn' <- withFastStore' $ \db -> do
|
||||
deleteConnectionRecord db user connId
|
||||
@@ -2249,7 +2275,7 @@ processChatCommand cxt nm = \case
|
||||
sLnk <- case connShortLink' connLinkToConnect of
|
||||
Just sl -> pure sl
|
||||
Nothing -> throwChatError $ CEException "failed to retrieve relays: no short link"
|
||||
(FixedLinkData {linkConnReq = mainCReq@(CRContactUri crData), linkEntityId, rootKey}, cData@(ContactLinkData _ UserContactData {owners, relays})) <- getShortLinkConnReq nm user sLnk
|
||||
(FixedLinkData {linkEntityId, rootKey}, cData@(ContactLinkData _ UserContactData {owners, relays}), mainCReq@(CRContactUri crData e2e)) <- getShortLinkConnReq nm user sLnk
|
||||
groupSLinkData_ <- liftIO $ decodeLinkUserData cData
|
||||
-- Validate link entity ID matches group profile's publicGroupId (relay groups must have both)
|
||||
case groupSLinkData_ of
|
||||
@@ -2261,7 +2287,7 @@ processChatCommand cxt nm = \case
|
||||
-- Prepare group record once before connecting to relays (updatePreparedRelayedGroup):
|
||||
-- set group link info and incognito profile, generate and store membership keys
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
let cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex}
|
||||
let cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex} e2e
|
||||
gVar <- asks random
|
||||
(_, memberPrivKey) <- liftIO $ atomically $ C.generateKeyPair gVar
|
||||
gInfo' <- withFastStore $ \db -> do
|
||||
@@ -2381,7 +2407,7 @@ processChatCommand cxt nm = \case
|
||||
ccLink <- case contactLink of
|
||||
Just (CLFull cReq) -> pure $ CCLink cReq Nothing
|
||||
Just (CLShort sLnk) -> do
|
||||
(FixedLinkData {linkConnReq = cReq}, _cData) <- getShortLinkConnReq nm user sLnk
|
||||
(_, _, cReq) <- getShortLinkConnReq nm user sLnk
|
||||
pure $ CCLink cReq $ Just sLnk
|
||||
Nothing -> throwCmdError "no address in contact profile"
|
||||
connectContactViaAddress user incognito ct ccLink `catchAllErrors` \e -> do
|
||||
@@ -2399,7 +2425,7 @@ processChatCommand cxt nm = \case
|
||||
CRContactsList user <$> withFastStore' (\db -> getUserContacts db cxt user)
|
||||
ListContacts -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APIListContacts userId
|
||||
APICreateMyAddress userId server_ -> withUserId userId $ \user@User {userChatRelay} -> do
|
||||
APICreateMyAddress userId server_ pqRatchet_ -> withUserId userId $ \user@User {userChatRelay} -> do
|
||||
withFastStore' (\db -> runExceptT $ getUserAddress db user) >>= \case
|
||||
Left SEUserContactLinkNotFound -> pure ()
|
||||
Left e -> throwError $ ChatErrorStore e
|
||||
@@ -2408,20 +2434,25 @@ processChatCommand cxt nm = \case
|
||||
gVar <- asks random
|
||||
rootKey@(rootPubKey, rootPrivKey) <- liftIO $ atomically $ C.generateKeyPair gVar
|
||||
let entityId = C.sha256Hash $ C.pubKeyBytes rootPubKey
|
||||
(ccLink, preparedParams) <- withAgent $ \a -> prepareConnectionLink a (aUserId user) rootKey entityId True Nothing server_
|
||||
-- TODO [address DR] remove this option and switch to IKUsePQ True
|
||||
let (pqInitKeys, useDR) = case pqRatchet_ of
|
||||
Just True -> (IKUsePQ, True)
|
||||
Just False -> (IKPQOn, True)
|
||||
Nothing -> (IKPQOn, False)
|
||||
(ccLink, preparedParams) <- withAgent $ \a -> prepareConnectionLink a (aUserId user) rootKey entityId True Nothing pqInitKeys useDR server_
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
-- TODO [relays] relay: add identity, key to link data?
|
||||
userData <-
|
||||
if isTrue userChatRelay
|
||||
then pure $ relayShortLinkData (userProfileDirect user Nothing Nothing True)
|
||||
else (`contactShortLinkData` Nothing) <$> presentUserBadge user Nothing (userProfileDirect user Nothing Nothing True)
|
||||
let userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
|
||||
connId <- withAgent $ \a -> createConnectionForLink a nm (aUserId user) True ccLink preparedParams userLinkData IKPQOn subMode
|
||||
let userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData, ratchetKeys = Nothing}
|
||||
connId <- withAgent $ \a -> createConnectionForLink a nm (aUserId user) True ccLink preparedParams userLinkData subMode
|
||||
let ccLink'' = if isTrue userChatRelay then setShortLinkType CCTRelay ccLink' else ccLink'
|
||||
withFastStore $ \db -> createUserContactLink db user connId ccLink'' subMode rootPrivKey
|
||||
pure $ CRUserContactLinkCreated user ccLink''
|
||||
CreateMyAddress -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APICreateMyAddress userId Nothing
|
||||
CreateMyAddress ratchetKeys_ -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APICreateMyAddress userId Nothing ratchetKeys_
|
||||
APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do
|
||||
conn <- withFastStore $ \db -> getUserAddressConnection db cxt user
|
||||
withChatLock "deleteMyAddress" $ do
|
||||
@@ -2439,8 +2470,12 @@ processChatCommand cxt nm = \case
|
||||
CRUserContactLink user <$> withFastStore (`getUserAddress` user)
|
||||
ShowMyAddress -> withUser' $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APIShowMyAddress userId
|
||||
APIAddMyAddressShortLink userId -> withUserId' userId $ \user ->
|
||||
CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData user)
|
||||
APIAddMyAddressShortLink userId pqRatchet_ -> withUserId' userId $ \user -> do
|
||||
-- TODO [address DR] remove the option, and use IKUsePQ
|
||||
let pqInitKeys = (\case True -> IKUsePQ; False -> IKPQOn) <$> pqRatchet_
|
||||
CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData False pqInitKeys user)
|
||||
APIRotateAddressRatchetKeys userId -> withUserId' userId $ \user ->
|
||||
CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData True (Just IKUsePQ) user)
|
||||
APISetProfileAddress userId False -> withUserId userId $ \user@User {profile = p} -> do
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
|
||||
updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
|
||||
@@ -2451,7 +2486,7 @@ processChatCommand cxt nm = \case
|
||||
updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user $ Just ucl
|
||||
SetProfileAddress onOff -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APISetProfileAddress userId onOff
|
||||
APISetAddressSettings userId settings@AddressSettings {businessAddress, autoAccept} -> withUserId userId $ \user -> do
|
||||
APISetAddressSettings userId pqRatchet_ settings@AddressSettings {businessAddress, autoAccept} -> withUserId userId $ \user -> do
|
||||
ucl@UserContactLink {userContactLinkId, shortLinkDataSet, addressSettings} <- withFastStore (`getUserAddress` user)
|
||||
forM_ autoAccept $ \AutoAccept {acceptIncognito} -> do
|
||||
when (shortLinkDataSet && acceptIncognito) $ throwCmdError "incognito not allowed for address with short link data"
|
||||
@@ -2460,17 +2495,18 @@ processChatCommand cxt nm = \case
|
||||
then pure $ CRUserContactLinkUpdated user ucl
|
||||
else do
|
||||
let ucl' = ucl {addressSettings = settings}
|
||||
ucl'' <- if shortLinkDataSet then setMyAddressData user ucl' else pure ucl'
|
||||
pqInitKeys = (\case True -> IKUsePQ; False -> IKPQOn) <$> pqRatchet_
|
||||
ucl'' <- if shortLinkDataSet then setMyAddressData False pqInitKeys user ucl' else pure ucl'
|
||||
withFastStore' $ \db -> updateUserAddressSettings db userContactLinkId settings
|
||||
pure $ CRUserContactLinkUpdated user ucl''
|
||||
SetAddressSettings settings -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APISetAddressSettings userId settings
|
||||
SetAddressSettings pqRatchet_ settings -> withUser $ \User {userId} ->
|
||||
processChatCommand cxt nm $ APISetAddressSettings userId pqRatchet_ settings
|
||||
AcceptContact incognito cName -> withUser $ \User {userId} -> do
|
||||
connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName
|
||||
processChatCommand cxt nm $ APIAcceptContact incognito connReqId
|
||||
RejectContact cName -> withUser $ \User {userId} -> do
|
||||
RejectContact cName notify -> withUser $ \User {userId} -> do
|
||||
connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName
|
||||
processChatCommand cxt nm $ APIRejectContact connReqId
|
||||
processChatCommand cxt nm $ APIRejectContact connReqId notify
|
||||
ForwardMessage toChatName fromContactName forwardedMsg -> withUser $ \user -> do
|
||||
contactId <- withFastStore $ \db -> getContactIdByName db user fromContactName
|
||||
forwardedItemId <- withFastStore $ \db -> getDirectChatItemIdByText' db user contactId forwardedMsg
|
||||
@@ -2679,7 +2715,7 @@ processChatCommand cxt nm = \case
|
||||
let entityId = C.sha256Hash $ C.pubKeyBytes rootPubKey
|
||||
crClientData = encodeJSON $ CRDataGroup groupLinkId
|
||||
-- prepare link with entityId as linkEntityId (no server request)
|
||||
(ccLink, preparedParams) <- withAgent $ \a -> prepareConnectionLink a (aUserId user) rootKey entityId True (Just crClientData) Nothing
|
||||
(ccLink, preparedParams) <- withAgent $ \a -> prepareConnectionLink a (aUserId user) rootKey entityId True (Just crClientData) IKPQOff False Nothing
|
||||
ccLink' <- setShortLinkType CCTChannel <$> shortenCreatedLink ccLink
|
||||
sLnk <- case connShortLink' ccLink' of
|
||||
Just sl -> pure sl
|
||||
@@ -2690,9 +2726,9 @@ processChatCommand cxt nm = \case
|
||||
-- TODO [channel web] pass publicGroupAccess from owner's profile
|
||||
let groupProfile' = (groupProfile :: GroupProfile) {publicGroup = Just PublicGroupProfile {groupType = GTChannel, groupLink = sLnk, publicGroupId = B64UrlByteString entityId, publicGroupAccess = Nothing}}
|
||||
userData = encodeShortLinkData $ GroupShortLinkData {groupProfile = groupProfile', publicGroupData = Just (PublicGroupData 1)}
|
||||
userLinkData = UserContactLinkData UserContactData {direct = False, owners = [ownerAuth], relays = [], userData}
|
||||
userLinkData = UserContactLinkData UserContactData {direct = False, owners = [ownerAuth], relays = [], userData, ratchetKeys = Nothing}
|
||||
-- create connection with prepared link (single network call)
|
||||
connId <- withAgent $ \a -> createConnectionForLink a nm (aUserId user) True ccLink preparedParams userLinkData IKPQOff subMode
|
||||
connId <- withAgent $ \a -> createConnectionForLink a nm (aUserId user) True ccLink preparedParams userLinkData subMode
|
||||
let groupKeys = GroupKeys {publicGroupId = B64UrlByteString entityId, groupRootKey = GRKPrivate rootPrivKey, memberPrivKey}
|
||||
setupLink gInfo = do
|
||||
-- TODO [relays] starting role should be communicated in protocol from owner to relays
|
||||
@@ -2758,7 +2794,7 @@ processChatCommand cxt nm = \case
|
||||
Nothing -> do
|
||||
gVar <- asks random
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(agentConnId, CCLink cReq _) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
(agentConnId, CCLink cReq _) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff True subMode
|
||||
member <- withFastStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode
|
||||
sendInvitation member cReq
|
||||
pure $ CRSentGroupInvitation user gInfo contact member
|
||||
@@ -3245,9 +3281,9 @@ processChatCommand cxt nm = \case
|
||||
groupLinkId <- GroupLinkId <$> drgRandomBytes 16
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let userData = encodeShortLinkData $ GroupShortLinkData {groupProfile, publicGroupData = Nothing}
|
||||
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
|
||||
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData, ratchetKeys = Nothing}
|
||||
crClientData = encodeJSON $ CRDataGroup groupLinkId
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) (Just crClientData) IKPQOff subMode
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) (Just crClientData) IKPQOff False subMode
|
||||
ccLink' <- setShortLinkType CCTGroup <$> shortenCreatedLink ccLink
|
||||
gVar <- asks random
|
||||
gLink <- withFastStore $ \db -> createGroupLink db gVar user gInfo connId ccLink' groupLinkId mRole subMode
|
||||
@@ -3287,7 +3323,7 @@ processChatCommand cxt nm = \case
|
||||
when (isJust $ memberContactId m) $ throwCmdError "member contact already exists"
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- TODO PQ should negotitate contact connection with PQSupportOn?
|
||||
(connId, CCLink cReq _) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
(connId, CCLink cReq _) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff True subMode
|
||||
-- [incognito] reuse membership incognito profile
|
||||
ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode
|
||||
void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing Nothing (Just epochStart)
|
||||
@@ -3757,7 +3793,7 @@ processChatCommand cxt nm = \case
|
||||
CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||
)
|
||||
connectViaContact :: User -> Maybe PreparedChatEntity -> IncognitoEnabled -> CreatedLinkContact -> Maybe SharedMsgId -> Maybe (SharedMsgId, MsgContent) -> CM ConnectViaContactResult
|
||||
connectViaContact user@User {userId} preparedEntity_ incognito (CCLink cReq@(CRContactUri crData@ConnReqUriData {crClientData}) sLnk) welcomeSharedMsgId msg_ = withInvitationLock "connectViaContact" (strEncode cReq) $ do
|
||||
connectViaContact user@User {userId} preparedEntity_ incognito (CCLink cReq@(CRContactUri crData@ConnReqUriData {crClientData} e2e) sLnk) welcomeSharedMsgId msg_ = withInvitationLock "connectViaContact" (strEncode cReq) $ do
|
||||
let groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||
-- groupLinkId is Nothing for business chats
|
||||
when (isJust msg_ && isJust groupLinkId) $ throwChatError CEConnReqMessageProhibited
|
||||
@@ -3789,8 +3825,8 @@ processChatCommand cxt nm = \case
|
||||
Just Connection {xContactId} -> connect' groupLinkId xContactId (groupLinkId $> Nothing)
|
||||
Nothing -> connect' groupLinkId Nothing (groupLinkId $> Nothing)
|
||||
where
|
||||
cReqHash1 = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex}
|
||||
cReqHash2 = contactCReqHash $ CRContactUri crData {crScheme = simplexChat}
|
||||
cReqHash1 = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex} e2e
|
||||
cReqHash2 = contactCReqHash $ CRContactUri crData {crScheme = simplexChat} e2e
|
||||
-- relay-group joins (only via connectToRelay) carry the target relay member in preparedEntity_;
|
||||
-- its memberId binds the join signature so a sibling relay can't replay it
|
||||
relayMemberId_ = case preparedEntity_ of
|
||||
@@ -3830,7 +3866,7 @@ processChatCommand cxt nm = \case
|
||||
-- [incognito] generate profile to send
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
|
||||
let cReqHash = contactCReqHash cReq
|
||||
conn <- withFastStore' $ \db -> createConnReqConnection db userId connId (Just $ PCEContact ct) cReq cReqHash shortLink newXContactId (NewIncognito <$> incognitoProfile) Nothing subMode chatV pqSup
|
||||
void $ joinContact user conn cReq incognitoProfile newXContactId Nothing Nothing Nothing Nothing pqSup
|
||||
ct' <- withStore $ \db -> getContact db cxt user contactId
|
||||
@@ -3851,15 +3887,14 @@ processChatCommand cxt nm = \case
|
||||
-- Save relayLink to re-use relay member record on retry (check by relayLink)
|
||||
relayMember <- withFastStore $ \db -> getCreateRelayForMember db cxt gVar user gInfo relayLink
|
||||
r <- tryAllErrors $ do
|
||||
(fd@FixedLinkData {rootKey = relayKey, linkEntityId}, cData) <- getShortLinkConnReq nm user relayLink
|
||||
(FixedLinkData {rootKey = relayKey, linkEntityId}, cData, cReq) <- getShortLinkConnReq nm user relayLink
|
||||
relayLinkData_ <- liftIO $ decodeLinkUserData cData
|
||||
relayMemberId <- case (relayLinkData_, linkEntityId) of
|
||||
(Just RelayShortLinkData {relayProfile = p}, Just entityId) -> do
|
||||
withFastStore $ \db -> updateRelayMemberData db cxt user relayMember (MemberId entityId) (MemberKey relayKey) p
|
||||
pure $ MemberId entityId
|
||||
_ -> throwChatError $ CEException "relay link: no relay link data or entity id"
|
||||
let cReq = linkConnReq fd
|
||||
relayLinkToConnect = CCLink cReq (Just relayLink)
|
||||
let relayLinkToConnect = CCLink cReq (Just relayLink)
|
||||
void $ connectViaContact user (Just $ PCEGroup gInfo (relayMember {memberId = relayMemberId})) (incognitoMembership gInfo) relayLinkToConnect Nothing Nothing
|
||||
relayMember' <- withFastStore $ \db -> getGroupMember db cxt user (groupId' gInfo) (groupMemberId' relayMember)
|
||||
pure (relayLink, relayMember', r)
|
||||
@@ -3943,8 +3978,10 @@ processChatCommand cxt nm = \case
|
||||
setMyAddressData' :: User -> CM ()
|
||||
setMyAddressData' user' =
|
||||
withFastStore' (\db -> runExceptT $ getUserAddress db user) >>= \case
|
||||
Right ucl@UserContactLink {shortLinkDataSet}
|
||||
| shortLinkDataSet -> void $ setMyAddressData user' ucl
|
||||
Right ucl@UserContactLink {shortLinkDataSet, connLinkContact = CCLink {connFullLink = CRContactUri _ e2e}}
|
||||
| shortLinkDataSet ->
|
||||
let pqInitKeys = (\(_, E2ERatchetParamsUri _ _ _ pq) -> if isJust pq then IKUsePQ else IKPQOn) <$> e2e
|
||||
in void $ setMyAddressData False pqInitKeys user' ucl
|
||||
_ -> pure ()
|
||||
sendUpdateToContacts :: User -> [Contact] -> CM UserProfileUpdateSummary
|
||||
sendUpdateToContacts user' contacts = do
|
||||
@@ -3985,16 +4022,17 @@ processChatCommand cxt nm = \case
|
||||
ctMsgReq ChangedProfileContact {conn} =
|
||||
fmap $ \SndMessage {msgId, msgBody} ->
|
||||
(conn, MsgFlags {notification = hasNotification XInfo_}, (vrValue msgBody, [msgId]))
|
||||
setMyAddressData :: User -> UserContactLink -> CM UserContactLink
|
||||
setMyAddressData user@User {userChatRelay} ucl@UserContactLink {userContactLinkId, connLinkContact = CCLink connFullLink _, addressSettings} = do
|
||||
setMyAddressData :: Bool -> Maybe InitialKeys -> User -> UserContactLink -> CM UserContactLink
|
||||
setMyAddressData rotateKeys pqInitKeys user@User {userChatRelay} ucl@UserContactLink {userContactLinkId, connLinkContact = CCLink connFullLink _, addressSettings} = do
|
||||
conn <- withFastStore $ \db -> getUserAddressConnection db cxt user
|
||||
shortLinkProfile <- presentUserBadge user Nothing (userProfileDirect user Nothing Nothing True)
|
||||
-- TODO [short links] do not save address to server if data did not change, spinners, error handling
|
||||
let userData
|
||||
| isTrue userChatRelay = relayShortLinkData shortLinkProfile
|
||||
| otherwise = contactShortLinkData shortLinkProfile $ Just addressSettings
|
||||
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
|
||||
sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a nm (aConnId conn) SCMContact userLinkData Nothing)
|
||||
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData, ratchetKeys = Nothing}
|
||||
-- TODO [address DR] remove parameter and switch to (Just IKUsePQ) after rotateKeys
|
||||
sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a nm (aConnId conn) SCMContact userLinkData Nothing rotateKeys pqInitKeys)
|
||||
withFastStore' $ \db -> setUserContactLinkShortLink db userContactLinkId sLnk
|
||||
let autoAccept' = (\aa -> aa {acceptIncognito = False}) <$> autoAccept addressSettings
|
||||
ucl' = (ucl :: UserContactLink) {connLinkContact = CCLink connFullLink (Just sLnk), shortLinkDataSet = True, shortLinkLargeDataSet = BoolDef True, addressSettings = addressSettings {autoAccept = autoAccept'}}
|
||||
@@ -4212,7 +4250,7 @@ processChatCommand cxt nm = \case
|
||||
where
|
||||
addRelay :: UserChatRelay -> CM (UserChatRelay, Either ChatError GroupRelay)
|
||||
addRelay relay@UserChatRelay {address} = fmap (relay,) . tryAllErrors $ do
|
||||
(FixedLinkData {linkConnReq = cReq}, _cData) <- getShortLinkConnReq nm user address
|
||||
(_, _, cReq) <- getShortLinkConnReq nm user address
|
||||
lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOff cReq) >>= \case
|
||||
Nothing -> throwChatError CEInvalidConnReq
|
||||
Just _ -> do
|
||||
@@ -4311,7 +4349,7 @@ processChatCommand cxt nm = \case
|
||||
knownLinkPlans l' >>= \case
|
||||
Just (createdLink, p) -> pure (createdLink, Nothing, Nothing, p)
|
||||
Nothing -> do
|
||||
(FixedLinkData {linkConnReq = cReq, rootKey}, cData) <- getShortLinkConnReq nm user l'
|
||||
(FixedLinkData {rootKey}, cData, cReq) <- getShortLinkConnReq nm user l'
|
||||
contactSLinkData_ <- mapM linkDataBadge =<< liftIO (decodeLinkUserData cData)
|
||||
let ov = verifyLinkOwner rootKey [] l sig_
|
||||
invitationReqAndPlan cReq (Just l') contactSLinkData_ ov
|
||||
@@ -4365,7 +4403,7 @@ processChatCommand cxt nm = \case
|
||||
Nothing -> do
|
||||
when (resolveMode == PRMNever) $ throwChatError CENotResolvedLocally
|
||||
l' <- resolveSLink
|
||||
(FixedLinkData {linkConnReq = cReq, rootKey}, cData) <- getShortLinkConnReq nm user l'
|
||||
(FixedLinkData {rootKey}, cData, cReq) <- getShortLinkConnReq nm user l'
|
||||
contactSLinkData_ <- mapM linkDataBadge =<< liftIO (decodeLinkUserData cData)
|
||||
let linkProfile_ = (\ContactShortLinkData {profile} -> profile) <$> contactSLinkData_
|
||||
linkDomain_ = linkProfile_ >>= \Profile {contactDomain} -> claimDomain <$> contactDomain
|
||||
@@ -4425,13 +4463,13 @@ processChatCommand cxt nm = \case
|
||||
Nothing -> do
|
||||
when (resolveMode == PRMNever) $ throwChatError CENotResolvedLocally
|
||||
l' <- resolveSLink
|
||||
(fd, cData@(ContactLinkData _ UserContactData {direct, owners, relays})) <- getShortLinkConnReq' nm user l'
|
||||
(fd, cData@(ContactLinkData _ UserContactData {direct, owners, relays}), cReq) <- getShortLinkConnReq' nm user l'
|
||||
groupSLinkData_ <- liftIO $ decodeLinkUserData cData
|
||||
if
|
||||
| not direct && unsupportedGroupType groupSLinkData_ -> pure (con l' (linkConnReq fd), CPGroupLink (GLPUpdateRequired groupSLinkData_))
|
||||
| not direct && null relays -> pure (con l' (linkConnReq fd), CPGroupLink (GLPNoRelays groupSLinkData_))
|
||||
| not direct && unsupportedGroupType groupSLinkData_ -> pure (con l' cReq, CPGroupLink (GLPUpdateRequired groupSLinkData_))
|
||||
| not direct && null relays -> pure (con l' cReq, CPGroupLink (GLPNoRelays groupSLinkData_))
|
||||
| otherwise -> do
|
||||
let FixedLinkData {linkConnReq = cReq, linkEntityId, rootKey} = fd
|
||||
let FixedLinkData {linkEntityId, rootKey} = fd
|
||||
linkInfo = GroupShortLinkInfo {direct, groupRelays = relays, publicGroupId = B64UrlByteString <$> linkEntityId}
|
||||
let profilePGId = groupSLinkData_ >>= \GroupShortLinkData {groupProfile = GroupProfile {publicGroup}} ->
|
||||
fmap (\PublicGroupProfile {publicGroupId} -> publicGroupId) publicGroup
|
||||
@@ -4470,14 +4508,14 @@ processChatCommand cxt nm = \case
|
||||
Nothing -> (gPlan =<<) <$> getGroupToConnect db cxt user nl'
|
||||
resolveKnownGroup g = do
|
||||
l' <- resolveSLink
|
||||
(fd@FixedLinkData {rootKey = rk}, cData@(ContactLinkData _ UserContactData {owners})) <- getShortLinkConnReq' nm user l'
|
||||
(FixedLinkData {rootKey = rk}, cData@(ContactLinkData _ UserContactData {owners}), cReq) <- getShortLinkConnReq' nm user l'
|
||||
groupSLinkData_ <- liftIO $ decodeLinkUserData cData
|
||||
let ov = verifyLinkOwner rk owners l' sig_
|
||||
glOwners = map (\OwnerAuth {ownerId, ownerKey} -> GroupLinkOwner {memberId = MemberId ownerId, memberKey = ownerKey}) owners
|
||||
(g', updated) <- case groupSLinkData_ of
|
||||
Just sLinkData -> updateGroupFromLinkData user g sLinkData Nothing
|
||||
_ -> pure (g, False)
|
||||
pure (con l' (linkConnReq fd), CPGroupLink (GLPKnown g' updated ov (ListDef glOwners)))
|
||||
pure (con l' cReq, CPGroupLink (GLPKnown g' updated ov (ListDef glOwners)))
|
||||
-- resolve a name to its first contact/channel short link
|
||||
resolveNameLink :: SimplexNameInfo -> CM (ConnShortLink 'CMContact)
|
||||
resolveNameLink SimplexNameInfo {nameType, nameDomain} = do
|
||||
@@ -4544,15 +4582,14 @@ processChatCommand cxt nm = \case
|
||||
| otherwise -> CPInvitationLink (ILPConnecting Nothing)
|
||||
_ -> CPError $ ChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
||||
contactOrGroupRequestPlan :: User -> ConnReqContact -> CM ConnectionPlan
|
||||
contactOrGroupRequestPlan user cReq@(CRContactUri crData) = do
|
||||
let ConnReqUriData {crClientData} = crData
|
||||
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||
contactOrGroupRequestPlan user cReq@(CRContactUri ConnReqUriData {crClientData} _) = do
|
||||
let groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||
case groupLinkId of
|
||||
Nothing -> contactRequestPlan user cReq Nothing Nothing
|
||||
Just _ -> groupJoinRequestPlan user cReq Nothing Nothing Nothing []
|
||||
contactRequestPlan :: User -> ConnReqContact -> Maybe ContactShortLinkData -> Maybe OwnerVerification -> CM ConnectionPlan
|
||||
contactRequestPlan user (CRContactUri crData) cld ov = do
|
||||
let cReqSchemas = contactCReqSchemas crData
|
||||
contactRequestPlan user cReq cld ov = do
|
||||
let cReqSchemas = contactCReqSchemas cReq
|
||||
cReqHashes = bimap contactCReqHash contactCReqHash cReqSchemas
|
||||
plan p = pure $ CPContactAddress p
|
||||
withFastStore' (\db -> getUserContactLinkByConnReq db user cReqSchemas) >>= \case
|
||||
@@ -4574,8 +4611,8 @@ processChatCommand cxt nm = \case
|
||||
Just (RcvGroupMsgConnection _ gInfo _) -> groupPlan gInfo Nothing Nothing Nothing []
|
||||
Just _ -> throwCmdError "found connection entity is not RcvDirectMsgConnection or RcvGroupMsgConnection"
|
||||
groupJoinRequestPlan :: User -> ConnReqContact -> Maybe GroupShortLinkInfo -> Maybe GroupShortLinkData -> Maybe OwnerVerification -> [GroupLinkOwner] -> CM ConnectionPlan
|
||||
groupJoinRequestPlan user (CRContactUri crData) linkInfo gld ov glOwners = do
|
||||
let cReqSchemas = contactCReqSchemas crData
|
||||
groupJoinRequestPlan user cReq linkInfo gld ov glOwners = do
|
||||
let cReqSchemas = contactCReqSchemas cReq
|
||||
cReqHashes = bimap contactCReqHash contactCReqHash cReqSchemas
|
||||
plan p = pure $ CPGroupLink p
|
||||
withFastStore' (\db -> getGroupInfoByUserContactLinkConnReq db cxt user cReqSchemas) >>= \case
|
||||
@@ -4601,10 +4638,10 @@ processChatCommand cxt nm = \case
|
||||
| otherwise = plan $ GLPOk linkInfo gld ov
|
||||
where
|
||||
plan p = pure $ CPGroupLink p
|
||||
contactCReqSchemas :: ConnReqUriData -> (ConnReqContact, ConnReqContact)
|
||||
contactCReqSchemas crData =
|
||||
( CRContactUri crData {crScheme = SSSimplex},
|
||||
CRContactUri crData {crScheme = simplexChat}
|
||||
contactCReqSchemas :: ConnReqContact -> (ConnReqContact, ConnReqContact)
|
||||
contactCReqSchemas (CRContactUri crData e2e) =
|
||||
( CRContactUri crData {crScheme = SSSimplex} e2e,
|
||||
CRContactUri crData {crScheme = simplexChat} e2e
|
||||
)
|
||||
-- This function is needed, as UI uses simplex:/ schema in message view, so that the links can be handled without browser,
|
||||
-- and short links are stored with server hostname schema, so they wouldn't match without it.
|
||||
@@ -4655,7 +4692,7 @@ processChatCommand cxt nm = \case
|
||||
forM (connShortLink' =<< connLinkInv) $ \_ -> do
|
||||
let userData = contactShortLinkData profile Nothing
|
||||
userLinkData = UserInvLinkData userData
|
||||
shortenShortLink' =<< withAgent (\a -> setConnShortLink a nm (aConnId' conn) SCMInvitation userLinkData Nothing)
|
||||
shortenShortLink' =<< withAgent (\a -> setConnShortLink a nm (aConnId' conn) SCMInvitation userLinkData Nothing False Nothing)
|
||||
updateCIGroupInvitationStatus :: User -> GroupInfo -> CIGroupInvitationStatus -> CM ()
|
||||
updateCIGroupInvitationStatus user GroupInfo {groupId} newStatus = do
|
||||
AChatItem _ _ cInfo ChatItem {content, meta = CIMeta {itemId}} <- withFastStore $ \db -> getChatItemByGroupId db cxt user groupId
|
||||
@@ -5004,7 +5041,7 @@ verifyEntityDomain user nm nameType SimplexDomainClaim {domain = StrJSON domain,
|
||||
where
|
||||
verifyDomainProof :: SimplexDomainProof -> ShortLinkContact -> CM Bool
|
||||
verifyDomainProof SimplexDomainProof {linkOwnerId, presHeader, signature} sLnk@(CSLContact _ ct srv key) = do
|
||||
(FixedLinkData {rootKey}, ContactLinkData _ UserContactData {owners}) <- getShortLinkConnReq nm user sLnk
|
||||
(FixedLinkData {rootKey}, ContactLinkData _ UserContactData {owners}, _) <- getShortLinkConnReq nm user sLnk
|
||||
let ownerKey_ = case linkOwnerId of
|
||||
Nothing -> Just rootKey
|
||||
Just (StrJSON oid) -> ownerKey <$> find (\OwnerAuth {ownerId} -> ownerId == oid) owners
|
||||
@@ -5286,7 +5323,7 @@ runRelayGroupLinkChecks user = do
|
||||
forM_ relayGroups $ \gInfo@GroupInfo {groupProfile = gp} -> flip catchAllErrors eToView $ do
|
||||
case publicGroup gp of
|
||||
Just PublicGroupProfile {groupLink = sLnk} -> do
|
||||
(_, ContactLinkData _ UserContactData {relays = relayLinks}) <-
|
||||
(_, ContactLinkData _ UserContactData {relays = relayLinks}, _) <-
|
||||
getShortLinkConnReq' NRMBackground user sLnk
|
||||
gLink_ <- withStore' $ \db -> runExceptT $ getGroupLink db user gInfo
|
||||
case gLink_ of
|
||||
@@ -5404,8 +5441,9 @@ chatCommandP =
|
||||
"/_start " *> do
|
||||
mainApp <- "main=" *> onOffP
|
||||
enableSndFiles <- " snd_files=" *> onOffP <|> pure mainApp
|
||||
pure StartChat {mainApp, enableSndFiles},
|
||||
"/_start" $> StartChat {mainApp = True, enableSndFiles = True},
|
||||
serviceRequests <- " service_requests=" *> onOffP <|> pure False
|
||||
pure StartChat {mainApp, enableSndFiles, serviceRequests},
|
||||
"/_start" $> StartChat {mainApp = True, enableSndFiles = True, serviceRequests = False},
|
||||
"/_check running" $> CheckChatRunning,
|
||||
"/_stop" $> APIStopChat,
|
||||
"/_app activate restore=" *> (APIActivateChat <$> onOffP),
|
||||
@@ -5478,7 +5516,9 @@ chatCommandP =
|
||||
"/_delete " *> (APIDeleteChat <$> chatRefP <*> chatDeleteMode),
|
||||
"/_clear chat " *> (APIClearChat <$> chatRefP),
|
||||
"/_accept" *> (APIAcceptContact <$> incognitoOnOffP <* A.space <*> A.decimal),
|
||||
"/_reject " *> (APIRejectContact <$> A.decimal),
|
||||
"/_reject " *> (APIRejectContact <$> A.decimal <*> (" notify=" *> onOffP <|> pure False)),
|
||||
"/_service_request " *> (APISendServiceRequest <$> A.decimal <* A.space <*> strP <*> optional (" timeout=" *> (realToFrac <$> A.double)) <*> optional (" sign_key=" *> strP) <* A.space <*> jsonP),
|
||||
"/_service_response " *> (APISendServiceResponse <$> A.decimal <* A.space <*> strP <* A.space <*> jsonP),
|
||||
"/_call invite @" *> (APISendCallInvitation <$> A.decimal <* A.space <*> jsonP),
|
||||
"/call " *> char_ '@' *> (SendCallInvitation <$> displayNameP <*> pure defaultCallType),
|
||||
"/_call reject @" *> (APIRejectCall <$> A.decimal),
|
||||
@@ -5697,19 +5737,20 @@ chatCommandP =
|
||||
("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal),
|
||||
"/_connect contact " *> (APIConnectContactViaAddress <$> A.decimal <*> incognitoOnOffP <* A.space <*> A.decimal),
|
||||
"/simplex" *> (ConnectSimplex <$> incognitoP),
|
||||
"/_address " *> (APICreateMyAddress <$> A.decimal <*> optional (A.space *> strP)),
|
||||
("/address" <|> "/ad") $> CreateMyAddress,
|
||||
"/_address " *> (APICreateMyAddress <$> A.decimal <*> optional (A.space *> strP) <*> optional (" pq_ratchet=" *> onOffP)),
|
||||
("/address" <|> "/ad") *> (CreateMyAddress <$> optional (" pq_ratchet=" *> onOffP)),
|
||||
"/_delete_address " *> (APIDeleteMyAddress <$> A.decimal),
|
||||
("/delete_address" <|> "/da") $> DeleteMyAddress,
|
||||
"/_show_address " *> (APIShowMyAddress <$> A.decimal),
|
||||
("/show_address" <|> "/sa") $> ShowMyAddress,
|
||||
"/_short_link_address " *> (APIAddMyAddressShortLink <$> A.decimal),
|
||||
"/_short_link_address " *> (APIAddMyAddressShortLink <$> A.decimal <*> optional (" pq_ratchet=" *> onOffP)),
|
||||
"/_rotate_address_keys " *> (APIRotateAddressRatchetKeys <$> A.decimal),
|
||||
"/_profile_address " *> (APISetProfileAddress <$> A.decimal <* A.space <*> onOffP),
|
||||
("/profile_address " <|> "/pa ") *> (SetProfileAddress <$> onOffP),
|
||||
"/_address_settings " *> (APISetAddressSettings <$> A.decimal <* A.space <*> jsonP),
|
||||
"/auto_accept " *> (SetAddressSettings <$> autoAcceptP),
|
||||
"/_address_settings " *> (APISetAddressSettings <$> A.decimal <*> optional (" pq_ratchet=" *> onOffP) <* A.space <*> jsonP),
|
||||
"/auto_accept" *> (SetAddressSettings <$> optional (" pq_ratchet=" *> onOffP) <* A.space <*> autoAcceptP),
|
||||
("/accept" <|> "/ac") *> (AcceptContact <$> incognitoP <* A.space <* char_ '@' <*> displayNameP),
|
||||
("/reject " <|> "/rc ") *> char_ '@' *> (RejectContact <$> displayNameP),
|
||||
("/reject " <|> "/rc ") *> char_ '@' *> (RejectContact <$> displayNameP <*> (" notify" $> True <|> pure False)),
|
||||
("/markdown" <|> "/m") $> ChatHelp HSMarkdown,
|
||||
("/welcome" <|> "/w") $> Welcome,
|
||||
"/set profile image file " *> (UpdateProfileImageFromFile <$> filePath),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -107,6 +107,7 @@ import Text.Read (readMaybe)
|
||||
import UnliftIO.Concurrent (ThreadId, forkIO, mkWeakThreadId)
|
||||
import UnliftIO.Directory
|
||||
import UnliftIO.STM
|
||||
import qualified Data.Aeson as J
|
||||
|
||||
smallGroupsRcptsMemLimit :: Int
|
||||
smallGroupsRcptsMemLimit = 20
|
||||
@@ -127,6 +128,8 @@ processAgentMessage _ _ (DEL_CONNS connIds) =
|
||||
toView $ CEvtAgentConnsDeleted $ L.map AgentConnId connIds
|
||||
processAgentMessage _ "" (ERR e) =
|
||||
eToView $ chatErrorAgent e
|
||||
processAgentMessage _ connId (SSENT _ _) =
|
||||
toView $ CEvtServiceReplySent (AgentConnId connId)
|
||||
processAgentMessage corrId connId msg = do
|
||||
lockEntity <- critical connId (withStore (`getChatLockEntity` AgentConnId connId))
|
||||
withEntityLock "processAgentMessage" lockEntity $ do
|
||||
@@ -515,7 +518,10 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
withCompletedCommand conn agentMsg $ \_ ->
|
||||
case cReq of
|
||||
CRInvitationUri _ _ -> withStore' $ \db -> setConnConnReqInv db user connId cReq
|
||||
CRContactUri _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
||||
CRContactUri _ _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
||||
RJCT reason -> do
|
||||
ct' <- withStore' $ \db -> updateContactStatus db user ct CSRejected
|
||||
toView $ CEvtContactRequestRejected user ct' (either (const Nothing) Just $ strDecode reason)
|
||||
MSG msgMeta _msgFlags msgBody -> do
|
||||
tags <- newTVarIO []
|
||||
withAckMessage "contact msg" agentConnId msgMeta True (Just tags) $ \eInfo -> do
|
||||
@@ -740,7 +746,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
getHostConnId db user groupId
|
||||
sendXGrpMemInv hostConnId Nothing XGrpMemIntroCont {groupId, groupMemberId, memberId, groupConnReq}
|
||||
_ -> throwChatError $ CECommandError "unexpected cmdFunction"
|
||||
CRContactUri _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
||||
CRContactUri _ _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
||||
CONF confId _pqSupport _ connInfo -> do
|
||||
ChatMessage {chatVRange, chatMsgEvent} <- parseChatMessage conn connInfo
|
||||
conn' <- updatePeerChatVRange conn chatVRange
|
||||
@@ -1179,7 +1185,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
forM_ mc_ $ \mc -> do
|
||||
connReq_ <- withStore' $ \db -> getBusinessContactRequest db user groupId
|
||||
sendGroupAutoReply mc connReq_
|
||||
LDATA FixedLinkData {linkConnReq = cReq, rootKey = relayKey, linkEntityId} cData ->
|
||||
LDATA FixedLinkData {rootKey = relayKey, linkEntityId} cData cReq ->
|
||||
withCompletedCommand conn agentMsg $ \CommandData {cmdFunction} ->
|
||||
case cmdFunction of
|
||||
CFGetRelayDataJoin -> do
|
||||
@@ -1191,14 +1197,14 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
pure $ MemberId entityId
|
||||
_ -> throwChatError $ CEException "relay link: no relay link data or entity id"
|
||||
case cReq of
|
||||
CRContactUri crData@ConnReqUriData {crClientData} -> do
|
||||
CRContactUri crData@ConnReqUriData {crClientData} e2e -> do
|
||||
let pqSup = PQSupportOff
|
||||
lift (withAgent' $ \a -> connRequestPQSupport a pqSup cReq) >>= \case
|
||||
Nothing -> throwChatError CEInvalidConnReq
|
||||
Just _ -> do
|
||||
let chatV = initialChatVersion
|
||||
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||
cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex}
|
||||
cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex} e2e
|
||||
-- Update connection with data derived from cReq, now available after getConnShortLinkAsync
|
||||
withStore' $ \db -> updateConnLinkData db user conn cReq cReqHash groupLinkId chatV pqSup
|
||||
let incognitoProfile = fromLocalProfile <$> incognitoMembershipProfile gInfo
|
||||
@@ -1346,16 +1352,24 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
|
||||
processContactConnMessage :: AEvent e -> ConnectionEntity -> Connection -> UserContact -> CM ()
|
||||
processContactConnMessage agentMsg connEntity conn UserContact {userContactLinkId = uclId, groupId = ucGroupId_} = case agentMsg of
|
||||
REQ invId pqSupport _ connInfo -> do
|
||||
REQ invId pqSupport _ connInfo rejectionSupported -> do
|
||||
(signedMsg_, ChatMessage {chatVRange, chatMsgEvent}) <- parseChatMessage' conn connInfo
|
||||
case chatMsgEvent of
|
||||
XContact p xContactId_ welcomeMsgId_ requestMsg_ -> profileContactRequest invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ pqSupport
|
||||
XContact p xContactId_ welcomeMsgId_ requestMsg_ -> profileContactRequest invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ pqSupport rejectionSupported
|
||||
XMember p joiningMemberId joiningMemberKey viaRelay -> memberJoinRequestViaRelay invId chatVRange signedMsg_ p joiningMemberId joiningMemberKey viaRelay
|
||||
XInfo p -> profileContactRequest invId chatVRange p Nothing Nothing Nothing pqSupport
|
||||
XInfo p -> profileContactRequest invId chatVRange p Nothing Nothing Nothing pqSupport rejectionSupported
|
||||
XGrpRelayInv groupRelayInv -> xGrpRelayInv invId chatVRange groupRelayInv
|
||||
XGrpRelayTest challenge _ -> xGrpRelayTest invId chatVRange challenge
|
||||
-- TODO show/log error, other events in contact request
|
||||
_ -> pure ()
|
||||
SREQ invId sigKey_ payload ->
|
||||
chatReadVar processServiceRequests >>= \case
|
||||
True -> case J.eitherDecodeStrict' payload of
|
||||
Right request -> toView $ CEvtServiceRequest user (AgentInvId invId) sigKey_ request
|
||||
Left _ -> dropSReq
|
||||
False -> dropSReq
|
||||
where
|
||||
dropSReq = withAgent $ \a -> rejectServiceRequest a NRMBackground (aUserId user) invId Nothing
|
||||
LINK _link auData ->
|
||||
withCompletedCommand conn agentMsg $ \CommandData {cmdFunction} ->
|
||||
case cmdFunction of
|
||||
@@ -1417,8 +1431,8 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
-- TODO add debugging output
|
||||
_ -> pure ()
|
||||
where
|
||||
profileContactRequest :: InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> Maybe SharedMsgId -> Maybe (SharedMsgId, MsgContent) -> PQSupport -> CM ()
|
||||
profileContactRequest invId chatVRange p@Profile {displayName} xContactId_ welcomeMsgId_ requestMsg_ reqPQSup = do
|
||||
profileContactRequest :: InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> Maybe SharedMsgId -> Maybe (SharedMsgId, MsgContent) -> PQSupport -> Bool -> CM ()
|
||||
profileContactRequest invId chatVRange p@Profile {displayName} xContactId_ welcomeMsgId_ requestMsg_ reqPQSup rejectionSupported = do
|
||||
(ucl, gLinkInfo_) <- withStore $ \db -> getUserContactLinkById db userId uclId
|
||||
let v = maxVersion chatVRange
|
||||
case gLinkInfo_ of
|
||||
@@ -1428,7 +1442,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
AddressSettings {autoAccept} = addressSettings
|
||||
isSimplexTeam = sameConnReqContact connReq adminContactReq
|
||||
gVar <- asks random
|
||||
withStore (\db -> createOrUpdateContactRequest db gVar cxt user uclId ucl isSimplexTeam invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ reqPQSup) >>= \case
|
||||
withStore (\db -> createOrUpdateContactRequest db gVar cxt user uclId ucl isSimplexTeam invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ reqPQSup rejectionSupported) >>= \case
|
||||
RSAcceptedRequest _ucr re -> case re of
|
||||
REContact ct ->
|
||||
-- TODO [short links] update request msg
|
||||
@@ -3176,7 +3190,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
|
||||
void $ withStore $ \db -> do
|
||||
reMember <- createIntroReMember db cxt user gInfo memInfo memRestrictions
|
||||
createIntroReMemberConn db user m reMember chatV memInfo groupConnIds subMode
|
||||
withAgent $ \a -> createConnectionAsync a (aCorrId cmdId) connId (chatHasNtfs chatSettings) SCMInvitation CR.IKPQOff subMode
|
||||
withAgent $ \a -> createConnectionAsync a (aCorrId cmdId) connId (chatHasNtfs chatSettings) SCMInvitation CR.IKPQOff True subMode
|
||||
_ -> messageError "x.grp.mem.intro can be only sent by host member"
|
||||
|
||||
sendXGrpMemInv :: Int64 -> Maybe ConnReqInvitation -> XGrpMemIntroCont -> CM ()
|
||||
@@ -4392,7 +4406,7 @@ runRelayRequestWorker a Worker {doWork} = do
|
||||
where
|
||||
getLinkDataCreateRelayLink :: RelayRequestData -> GroupInfo -> CM (GroupInfo, ShortLinkContact)
|
||||
getLinkDataCreateRelayLink RelayRequestData {reqGroupLink} gInfo = do
|
||||
(FixedLinkData {linkEntityId, rootKey}, cData@(ContactLinkData _ UserContactData {owners})) <- getShortLinkConnReq' NRMBackground user reqGroupLink
|
||||
(FixedLinkData {linkEntityId, rootKey}, cData@(ContactLinkData _ UserContactData {owners}), _) <- getShortLinkConnReq' NRMBackground user reqGroupLink
|
||||
liftIO (decodeLinkUserData cData) >>= \case
|
||||
Nothing -> throwChatError $ CEException "getLinkDataCreateRelayLink: no group link data"
|
||||
Just GroupShortLinkData {groupProfile = gp@GroupProfile {publicGroup}} -> do
|
||||
@@ -4422,15 +4436,15 @@ runRelayRequestWorker a Worker {doWork} = do
|
||||
sigKeys <- liftIO $ atomically $ C.generateKeyPair gVar
|
||||
let crClientData = encodeJSON $ CRDataGroup groupLinkId
|
||||
-- prepare link with relayMemId as linkEntityId (no server request)
|
||||
(ccLink, preparedParams) <- withAgent $ \a' -> prepareConnectionLink a' (aUserId user) sigKeys relayMemId True (Just crClientData) Nothing
|
||||
(ccLink, preparedParams) <- withAgent $ \a' -> prepareConnectionLink a' (aUserId user) sigKeys relayMemId True (Just crClientData) CR.IKPQOff False Nothing
|
||||
ccLink' <- setShortLinkType CCTGroup <$> shortenCreatedLink ccLink
|
||||
sLnk <- case connShortLink' ccLink' of
|
||||
Just sl -> pure sl
|
||||
Nothing -> throwChatError $ CEException "failed to create relay link: no short link"
|
||||
let userData = encodeShortLinkData $ RelayShortLinkData {relayProfile = fromLocalProfile p}
|
||||
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
|
||||
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData, ratchetKeys = Nothing}
|
||||
-- create connection with prepared link (single network call)
|
||||
connId <- withAgent $ \a' -> createConnectionForLink a' NRMBackground (aUserId user) True ccLink preparedParams userLinkData CR.IKPQOff subMode
|
||||
connId <- withAgent $ \a' -> createConnectionForLink a' NRMBackground (aUserId user) True ccLink preparedParams userLinkData subMode
|
||||
-- TODO [relays] starting role should be communicated in protocol from owner to relays
|
||||
subRole <- asks $ channelSubscriberRole . config
|
||||
void $ withFastStore $ \db -> createGroupLink db gVar user gi connId ccLink' groupLinkId subRole subMode
|
||||
|
||||
@@ -349,7 +349,7 @@ markdownP = mconcat <$> A.many' fragmentP
|
||||
simplexUriFormat :: Maybe Text -> AConnectionLink -> Format
|
||||
simplexUriFormat showText = \case
|
||||
ACL m (CLFull cReq) -> case cReq of
|
||||
CRContactUri crData -> SimplexLink showText (linkType' crData) cLink $ uriHosts crData
|
||||
CRContactUri crData _ -> SimplexLink showText (linkType' crData) cLink $ uriHosts crData
|
||||
CRInvitationUri crData _ -> SimplexLink showText XLInvitation cLink $ uriHosts crData
|
||||
where
|
||||
cLink = ACL m $ CLFull $ simplexConnReqUri cReq
|
||||
|
||||
@@ -276,6 +276,7 @@ mobileChatOpts dbOptions =
|
||||
optFilesFolder = Nothing,
|
||||
optTempDirectory = Nothing,
|
||||
showReactions = False,
|
||||
showFullLinks = False,
|
||||
allowInstantFiles = True,
|
||||
autoAcceptFileSize = 0,
|
||||
muteNotifications = True,
|
||||
|
||||
@@ -46,6 +46,7 @@ data ChatOpts = ChatOpts
|
||||
optFilesFolder :: Maybe FilePath,
|
||||
optTempDirectory :: Maybe FilePath,
|
||||
showReactions :: Bool,
|
||||
showFullLinks :: Bool,
|
||||
allowInstantFiles :: Bool,
|
||||
autoAcceptFileSize :: Integer,
|
||||
muteNotifications :: Bool,
|
||||
@@ -418,6 +419,11 @@ chatOptsP appDir defaultDbName = do
|
||||
( long "reactions"
|
||||
<> help "Show message reactions"
|
||||
)
|
||||
showFullLinks <-
|
||||
switch
|
||||
( long "show-full-links"
|
||||
<> help "Show full connection links and addresses"
|
||||
)
|
||||
allowInstantFiles <-
|
||||
switch
|
||||
( long "allow-instant-files"
|
||||
@@ -485,6 +491,7 @@ chatOptsP appDir defaultDbName = do
|
||||
optFilesFolder,
|
||||
optTempDirectory,
|
||||
showReactions,
|
||||
showFullLinks,
|
||||
allowInstantFiles,
|
||||
autoAcceptFileSize,
|
||||
muteNotifications,
|
||||
|
||||
@@ -113,25 +113,27 @@ getConnectionEntity db cxt user@User {userId, userContactId} agentConnId = do
|
||||
[sql|
|
||||
SELECT
|
||||
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
|
||||
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
|
||||
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id, cr2.rejection_supported,
|
||||
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
|
||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl,
|
||||
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
|
||||
p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
|
||||
FROM contacts c
|
||||
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
|
||||
LEFT JOIN contact_requests cr2 ON cr2.contact_request_id = c.contact_request_id
|
||||
WHERE c.user_id = ? AND c.contact_id = ? AND c.contact_status = ? AND c.deleted = 0
|
||||
|]
|
||||
(userId, contactId, CSActive)
|
||||
toContact' :: UTCTime -> Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
|
||||
toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) =
|
||||
toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) =
|
||||
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge currentTs badgeRow, preferences, localAlias}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
|
||||
activeConn = Just conn
|
||||
preparedContact = toPreparedContact preparedContactRow
|
||||
contactRequest = UserContactRequestRef <$> contactRequestId <*> (unBI <$> rejectionSupported_)
|
||||
groupDirectInv = toGroupDirectInvitation groupDirectInvRow
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactRequest, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
getGroupAndMember_ :: Int64 -> Connection -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember_ groupMemberId c = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
|
||||
@@ -62,6 +62,7 @@ createOrUpdateContactRequest ::
|
||||
Maybe SharedMsgId ->
|
||||
Maybe (SharedMsgId, MsgContent) ->
|
||||
PQSupport ->
|
||||
Bool ->
|
||||
ExceptT StoreError IO RequestStage
|
||||
createOrUpdateContactRequest
|
||||
db
|
||||
@@ -77,7 +78,8 @@ createOrUpdateContactRequest
|
||||
xContactId_
|
||||
welcomeMsgId_
|
||||
requestMsg_
|
||||
pqSup =
|
||||
pqSup
|
||||
rejectionSupported =
|
||||
case xContactId_ of
|
||||
-- 0) this is very old legacy, when we didn't have xContactId at all (this should be deprecated)
|
||||
Nothing -> createContactRequest
|
||||
@@ -113,7 +115,7 @@ createOrUpdateContactRequest
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
@@ -124,6 +126,7 @@ createOrUpdateContactRequest
|
||||
FROM contacts ct
|
||||
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 contact_requests cr2 ON cr2.contact_request_id = ct.contact_request_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
|]
|
||||
(userId, xContactId)
|
||||
@@ -147,7 +150,7 @@ createOrUpdateContactRequest
|
||||
[sql|
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -177,11 +180,11 @@ createOrUpdateContactRequest
|
||||
[sql|
|
||||
INSERT INTO contact_requests
|
||||
(user_contact_link_id, agent_invitation_id, peer_chat_min_version, peer_chat_max_version, contact_profile_id, local_display_name, user_id,
|
||||
created_at, updated_at, xcontact_id, welcome_shared_msg_id, request_shared_msg_id, pq_support)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
created_at, updated_at, xcontact_id, welcome_shared_msg_id, request_shared_msg_id, pq_support, rejection_supported)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (uclId, Binary invId, minV, maxV, profileId, ldn, userId)
|
||||
:. (currentTs, currentTs, xContactId_, welcomeMsgId_, fst <$> requestMsg_, pqSup)
|
||||
:. (currentTs, currentTs, xContactId_, welcomeMsgId_, fst <$> requestMsg_, pqSup, BI rejectionSupported)
|
||||
)
|
||||
contactRequestId <- liftIO $ insertedRowId db
|
||||
createRequestEntity ldn profileId contactRequestId currentTs
|
||||
|
||||
@@ -322,7 +322,7 @@ getContactByConnReqHash db cxt user@User {userId} cReqHash1 cReqHash2 = do
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
@@ -334,6 +334,7 @@ getContactByConnReqHash db cxt user@User {userId} cReqHash1 cReqHash2 = do
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
JOIN connections c ON c.contact_id = ct.contact_id
|
||||
LEFT JOIN contact_requests cr2 ON cr2.contact_request_id = ct.contact_request_id
|
||||
WHERE
|
||||
( (c.user_id = ? AND c.via_contact_uri_hash = ?) OR
|
||||
(c.user_id = ? AND c.via_contact_uri_hash = ?)
|
||||
@@ -848,7 +849,7 @@ contactRequestQuery =
|
||||
[sql|
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -918,6 +919,7 @@ createContactFromRequest db user@User {userId, profile = LocalProfile {preferenc
|
||||
chatTs = Just currentTs,
|
||||
preparedContact = Nothing,
|
||||
contactRequestId = Nothing,
|
||||
contactRequest = Nothing,
|
||||
contactGroupMemberId = Nothing,
|
||||
contactGrpInvSent = False,
|
||||
groupDirectInv = Nothing,
|
||||
@@ -971,7 +973,7 @@ getContact_ db cxt user@User {userId} contactId deleted = do
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
@@ -983,6 +985,7 @@ getContact_ db cxt user@User {userId} contactId deleted = do
|
||||
FROM contacts ct
|
||||
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 contact_requests cr2 ON cr2.contact_request_id = ct.contact_request_id
|
||||
WHERE ct.user_id = ? AND ct.contact_id = ?
|
||||
AND ct.deleted = ?
|
||||
|]
|
||||
|
||||
@@ -3247,7 +3247,7 @@ createMemberContact
|
||||
quotaErrCounter = 0
|
||||
}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, preparedContact = Nothing, contactRequestId = Nothing, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, groupDirectInv = Nothing, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, preparedContact = Nothing, contactRequestId = Nothing, contactRequest = Nothing, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, groupDirectInv = Nothing, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
|
||||
getMemberContact :: DB.Connection -> StoreCxt -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation)
|
||||
getMemberContact db cxt user contactId = do
|
||||
|
||||
@@ -1141,7 +1141,7 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = do
|
||||
[sql|
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
|
||||
@@ -45,6 +45,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260714_member_security_code
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260715_profile_description
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260716_signed_history
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260720_server_roles
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260723_contact_request_rejection
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
@@ -89,7 +90,8 @@ schemaMigrations =
|
||||
("20260714_member_security_code", m20260714_member_security_code, Just down_m20260714_member_security_code),
|
||||
("20260715_profile_description", m20260715_profile_description, Just down_m20260715_profile_description),
|
||||
("20260716_signed_history", m20260716_signed_history, Just down_m20260716_signed_history),
|
||||
("20260720_server_roles", m20260720_server_roles, Just down_m20260720_server_roles)
|
||||
("20260720_server_roles", m20260720_server_roles, Just down_m20260720_server_roles),
|
||||
("20260723_contact_request_rejection", m20260723_contact_request_rejection, Just down_m20260723_contact_request_rejection)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations.M20260723_contact_request_rejection where
|
||||
|
||||
import Data.Text (Text)
|
||||
import Text.RawString.QQ (r)
|
||||
|
||||
m20260723_contact_request_rejection :: Text
|
||||
m20260723_contact_request_rejection =
|
||||
[r|
|
||||
ALTER TABLE contact_requests ADD COLUMN rejection_supported SMALLINT NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
down_m20260723_contact_request_rejection :: Text
|
||||
down_m20260723_contact_request_rejection =
|
||||
[r|
|
||||
ALTER TABLE contact_requests DROP COLUMN rejection_supported;
|
||||
|]
|
||||
@@ -580,7 +580,8 @@ CREATE TABLE test_chat_schema.contact_requests (
|
||||
contact_id bigint,
|
||||
business_group_id bigint,
|
||||
welcome_shared_msg_id bytea,
|
||||
request_shared_msg_id bytea
|
||||
request_shared_msg_id bytea,
|
||||
rejection_supported smallint DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -993,7 +994,7 @@ CREATE TABLE test_chat_schema.groups (
|
||||
public_member_count bigint,
|
||||
relay_request_retries bigint DEFAULT 0 NOT NULL,
|
||||
relay_request_delay bigint DEFAULT 0 NOT NULL,
|
||||
relay_request_execute_at timestamp with time zone DEFAULT '1970-01-01 04:00:00+04'::timestamp with time zone NOT NULL,
|
||||
relay_request_execute_at timestamp with time zone DEFAULT '1970-01-01 01:00:00+01'::timestamp with time zone NOT NULL,
|
||||
relay_inactive_at timestamp with time zone,
|
||||
relay_sent_web_domain text,
|
||||
roster_version bigint,
|
||||
|
||||
@@ -168,6 +168,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260714_member_security_code
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260715_profile_description
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260716_signed_history
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260720_server_roles
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260723_contact_request_rejection
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -335,7 +336,8 @@ schemaMigrations =
|
||||
("20260714_member_security_code", m20260714_member_security_code, Just down_m20260714_member_security_code),
|
||||
("20260715_profile_description", m20260715_profile_description, Just down_m20260715_profile_description),
|
||||
("20260716_signed_history", m20260716_signed_history, Just down_m20260716_signed_history),
|
||||
("20260720_server_roles", m20260720_server_roles, Just down_m20260720_server_roles)
|
||||
("20260720_server_roles", m20260720_server_roles, Just down_m20260720_server_roles),
|
||||
("20260723_contact_request_rejection", m20260723_contact_request_rejection, Just down_m20260723_contact_request_rejection)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20260723_contact_request_rejection where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20260723_contact_request_rejection :: Query
|
||||
m20260723_contact_request_rejection =
|
||||
[sql|
|
||||
ALTER TABLE contact_requests ADD COLUMN rejection_supported INTEGER NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
down_m20260723_contact_request_rejection :: Query
|
||||
down_m20260723_contact_request_rejection =
|
||||
[sql|
|
||||
ALTER TABLE contact_requests DROP COLUMN rejection_supported;
|
||||
|]
|
||||
@@ -286,7 +286,7 @@ SEARCH c USING COVERING INDEX idx_connections_user (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT user_id, conn_id, conn_mode, smp_agent_version, enable_ntfs,
|
||||
last_external_snd_msg_id, deleted, ratchet_sync_state, pq_support
|
||||
last_external_snd_msg_id, deleted, ratchet_sync_state, pq_support, service_request_expires_at
|
||||
FROM connections
|
||||
WHERE conn_id = ? AND deleted = ?
|
||||
|
||||
@@ -310,7 +310,7 @@ Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO conn_invitations
|
||||
(invitation_id, contact_conn_id, cr_invitation, recipient_conn_info, accepted) VALUES (?, ?, ?, ?, 0);
|
||||
(invitation_id, contact_conn_id, cr_invitation, recipient_conn_info, accepted, service_request) VALUES (?, ?, ?, ?, 0, ?);
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -429,8 +429,8 @@ Query:
|
||||
WHERE cs.deleted = 0
|
||||
|
||||
Plan:
|
||||
SCAN c USING INDEX idx_commands_conn_id
|
||||
SEARCH cs USING PRIMARY KEY (conn_id=?)
|
||||
SEARCH cs USING COVERING INDEX idx_connections_deleted (deleted=?)
|
||||
SEARCH c USING INDEX idx_commands_conn_id (conn_id=?)
|
||||
SEARCH s USING PRIMARY KEY (host=? AND port=?) LEFT-JOIN
|
||||
USE TEMP B-TREE FOR DISTINCT
|
||||
|
||||
@@ -441,8 +441,8 @@ Query:
|
||||
JOIN connections c ON q.conn_id = c.conn_id
|
||||
WHERE c.deleted = 0 AND q.deleted = 0
|
||||
Plan:
|
||||
SCAN q USING INDEX idx_rcv_queues_client_notice_id
|
||||
SEARCH c USING PRIMARY KEY (conn_id=?)
|
||||
SEARCH c USING INDEX idx_connections_deleted (deleted=?)
|
||||
SEARCH q USING INDEX idx_rcv_queue_id (conn_id=?)
|
||||
SEARCH s USING PRIMARY KEY (host=? AND port=?)
|
||||
USE TEMP B-TREE FOR DISTINCT
|
||||
|
||||
@@ -504,7 +504,7 @@ Plan:
|
||||
SEARCH conn_confirmations USING PRIMARY KEY (confirmation_id=?)
|
||||
|
||||
Query:
|
||||
SELECT contact_conn_id, cr_invitation, recipient_conn_info, own_conn_info, accepted
|
||||
SELECT contact_conn_id, cr_invitation, recipient_conn_info, own_conn_info, accepted, service_request, created_at
|
||||
FROM conn_invitations
|
||||
WHERE invitation_id = ?
|
||||
AND accepted = 0
|
||||
@@ -538,6 +538,17 @@ Plan:
|
||||
SEARCH n USING INDEX idx_client_notices_entity (protocol=?)
|
||||
SEARCH s USING PRIMARY KEY (host=? AND port=?)
|
||||
|
||||
Query:
|
||||
SELECT ratchet_key_id, x3dh_priv_key_1, x3dh_priv_key_2, pq_priv_kem
|
||||
FROM address_ratchet_keys
|
||||
WHERE conn_id = ?
|
||||
ORDER BY address_ratchet_key_id DESC
|
||||
LIMIT 1
|
||||
|
||||
Plan:
|
||||
SEARCH address_ratchet_keys USING INDEX idx_address_ratchet_keys (conn_id=?)
|
||||
USE TEMP B-TREE FOR ORDER BY
|
||||
|
||||
Query:
|
||||
SELECT s.internal_id, m.msg_type, s.internal_hash, s.rcpt_internal_id, s.rcpt_status
|
||||
FROM snd_messages s
|
||||
@@ -548,6 +559,31 @@ Plan:
|
||||
SEARCH s USING PRIMARY KEY (conn_id=? AND internal_snd_id=?)
|
||||
SEARCH m USING PRIMARY KEY (conn_id=? AND internal_id=?)
|
||||
|
||||
Query:
|
||||
SELECT x3dh_priv_key_1, x3dh_priv_key_2, pq_priv_kem
|
||||
FROM address_ratchet_keys
|
||||
WHERE conn_id = ? AND ratchet_key_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH address_ratchet_keys USING INDEX idx_address_ratchet_keys (conn_id=? AND ratchet_key_id=?)
|
||||
|
||||
Query:
|
||||
DELETE FROM address_ratchet_keys
|
||||
WHERE conn_id = ?
|
||||
AND address_ratchet_key_id NOT IN (
|
||||
SELECT address_ratchet_key_id
|
||||
FROM address_ratchet_keys
|
||||
WHERE conn_id = ?
|
||||
ORDER BY address_ratchet_key_id DESC
|
||||
LIMIT ?
|
||||
)
|
||||
|
||||
Plan:
|
||||
SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?)
|
||||
LIST SUBQUERY 1
|
||||
SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?)
|
||||
USE TEMP B-TREE FOR ORDER BY
|
||||
|
||||
Query:
|
||||
DELETE FROM conn_confirmations
|
||||
WHERE conn_id = ?
|
||||
@@ -555,9 +591,17 @@ Query:
|
||||
Plan:
|
||||
SEARCH conn_confirmations USING COVERING INDEX idx_conn_confirmations_conn_id (conn_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO address_ratchet_keys
|
||||
(conn_id, ratchet_key_id, x3dh_priv_key_1, x3dh_priv_key_2, pq_priv_kem)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
|
||||
Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO connections
|
||||
(user_id, conn_id, conn_mode, smp_agent_version, enable_ntfs, pq_support, duplex_handshake) VALUES (?,?,?,?,?,?,?)
|
||||
(user_id, conn_id, conn_mode, smp_agent_version, enable_ntfs, pq_support, service_request_expires_at, duplex_handshake, created_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -849,10 +893,10 @@ Query:
|
||||
Plan:
|
||||
MATERIALIZE d
|
||||
SCAN snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_expired
|
||||
SCAN d
|
||||
SEARCH q USING INDEX idx_snd_queue_id (conn_id=? AND snd_queue_id=?)
|
||||
SEARCH c USING INDEX idx_connections_deleted (deleted=?)
|
||||
SEARCH q USING INDEX idx_snd_queue_id (conn_id=?)
|
||||
SEARCH d USING AUTOMATIC COVERING INDEX (conn_id=? AND snd_queue_id=?)
|
||||
SEARCH s USING PRIMARY KEY (host=? AND port=?)
|
||||
SEARCH c USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
@@ -994,6 +1038,7 @@ SEARCH conn_invitations USING PRIMARY KEY (invitation_id=?)
|
||||
Query: DELETE FROM connections WHERE conn_id = ?
|
||||
Plan:
|
||||
SEARCH connections USING PRIMARY KEY (conn_id=?)
|
||||
SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?)
|
||||
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_hash (conn_id=?)
|
||||
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=?)
|
||||
SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_conn_id_internal_id (conn_id=?)
|
||||
@@ -1008,6 +1053,7 @@ SEARCH rcv_queues USING COVERING INDEX idx_rcv_queue_id (conn_id=?)
|
||||
Query: DELETE FROM connections WHERE user_id = 2
|
||||
Plan:
|
||||
SEARCH connections USING COVERING INDEX idx_connections_user (user_id=?)
|
||||
SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?)
|
||||
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_hash (conn_id=?)
|
||||
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=?)
|
||||
SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_conn_id_internal_id (conn_id=?)
|
||||
@@ -1143,6 +1189,10 @@ Plan:
|
||||
Query: INSERT INTO users DEFAULT VALUES
|
||||
Plan:
|
||||
|
||||
Query: SELECT 1 FROM connections WHERE conn_id = ? AND deleted_at_wait_delivery < ? LIMIT 1
|
||||
Plan:
|
||||
SEARCH connections USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query: SELECT 1 FROM encrypted_rcv_message_hashes WHERE conn_id = ? AND hash = ? LIMIT 1
|
||||
Plan:
|
||||
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=? AND hash=?)
|
||||
@@ -1157,7 +1207,7 @@ SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_ex
|
||||
|
||||
Query: SELECT conn_id FROM connections WHERE deleted = 0
|
||||
Plan:
|
||||
SCAN connections
|
||||
SEARCH connections USING COVERING INDEX idx_connections_deleted (deleted=?)
|
||||
|
||||
Query: SELECT conn_id FROM connections WHERE user_id = ?
|
||||
Plan:
|
||||
@@ -1165,7 +1215,7 @@ SEARCH connections USING COVERING INDEX idx_connections_user (user_id=?)
|
||||
|
||||
Query: SELECT count(1) FROM connections
|
||||
Plan:
|
||||
SCAN connections USING COVERING INDEX idx_connections_user
|
||||
SCAN connections USING COVERING INDEX idx_connections_deleted
|
||||
|
||||
Query: SELECT count(1) FROM snd_message_bodies
|
||||
Plan:
|
||||
|
||||
@@ -124,7 +124,7 @@ Query:
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
|
||||
@@ -135,12 +135,14 @@ Query:
|
||||
FROM contacts ct
|
||||
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 contact_requests cr2 ON cr2.contact_request_id = ct.contact_request_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
|
||||
Plan:
|
||||
SEARCH ct USING INDEX idx_contacts_chat_ts (user_id=?)
|
||||
SEARCH cp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_contact_id (contact_id=?) LEFT-JOIN
|
||||
SEARCH cr2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
@@ -269,8 +271,8 @@ SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
INSERT INTO contact_requests
|
||||
(user_contact_link_id, agent_invitation_id, peer_chat_min_version, peer_chat_max_version, contact_profile_id, local_display_name, user_id,
|
||||
created_at, updated_at, xcontact_id, welcome_shared_msg_id, request_shared_msg_id, pq_support)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
created_at, updated_at, xcontact_id, welcome_shared_msg_id, request_shared_msg_id, pq_support, rejection_supported)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -401,7 +403,7 @@ Plan:
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -731,18 +733,20 @@ Plan:
|
||||
Query:
|
||||
SELECT
|
||||
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
|
||||
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
|
||||
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id, cr2.rejection_supported,
|
||||
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
|
||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl,
|
||||
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
|
||||
p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
|
||||
FROM contacts c
|
||||
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
|
||||
LEFT JOIN contact_requests cr2 ON cr2.contact_request_id = c.contact_request_id
|
||||
WHERE c.user_id = ? AND c.contact_id = ? AND c.contact_status = ? AND c.deleted = 0
|
||||
|
||||
Plan:
|
||||
SEARCH c USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH cr2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
@@ -1455,7 +1459,7 @@ Query:
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
@@ -1467,6 +1471,7 @@ Query:
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
JOIN connections c ON c.contact_id = ct.contact_id
|
||||
LEFT JOIN contact_requests cr2 ON cr2.contact_request_id = ct.contact_request_id
|
||||
WHERE
|
||||
( (c.user_id = ? AND c.via_contact_uri_hash = ?) OR
|
||||
(c.user_id = ? AND c.via_contact_uri_hash = ?)
|
||||
@@ -1480,6 +1485,7 @@ INDEX 2
|
||||
SEARCH c USING INDEX idx_connections_via_contact_uri_hash (user_id=? AND via_contact_uri_hash=?)
|
||||
SEARCH ct USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH cp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH cr2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT 1 FROM users
|
||||
@@ -2022,7 +2028,7 @@ Query:
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
|
||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
|
||||
@@ -2034,6 +2040,7 @@ Query:
|
||||
FROM contacts ct
|
||||
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 contact_requests cr2 ON cr2.contact_request_id = ct.contact_request_id
|
||||
WHERE ct.user_id = ? AND ct.contact_id = ?
|
||||
AND ct.deleted = ?
|
||||
|
||||
@@ -2041,6 +2048,7 @@ Plan:
|
||||
SEARCH ct USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH cp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_contact_id (contact_id=?) LEFT-JOIN
|
||||
SEARCH cr2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
@@ -2107,7 +2115,7 @@ USE TEMP B-TREE FOR ORDER BY
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -2137,7 +2145,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -2167,7 +2175,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -5653,7 +5661,7 @@ SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
@@ -5670,7 +5678,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
|
||||
@@ -428,6 +428,7 @@ CREATE TABLE contact_requests(
|
||||
business_group_id INTEGER REFERENCES groups(group_id) ON DELETE CASCADE,
|
||||
welcome_shared_msg_id BLOB,
|
||||
request_shared_msg_id BLOB,
|
||||
rejection_supported INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON UPDATE CASCADE
|
||||
|
||||
@@ -488,22 +488,23 @@ type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Ma
|
||||
|
||||
type GroupDirectInvitationRow = (Maybe ConnReqInvitation, Maybe GroupId, Maybe GroupMemberId, Maybe Int64, BoolInt)
|
||||
|
||||
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64) :. BadgeRow :. ContactDomainRow
|
||||
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe BoolInt, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64) :. BadgeRow :. ContactDomainRow
|
||||
|
||||
type ContactRow = Only ContactId :. ContactRow'
|
||||
|
||||
type ContactDomainRow = (Maybe SimplexDomain, Maybe SimplexDomainProof, Maybe BoolInt)
|
||||
|
||||
toContact :: UTCTime -> StoreCxt -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
|
||||
toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) :. connRow) =
|
||||
toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) :. connRow) =
|
||||
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, preferences, localAlias}
|
||||
activeConn = toMaybeConnection cxt connRow
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
incognito = maybe False connIncognito activeConn
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences incognito
|
||||
preparedContact = toPreparedContact preparedContactRow
|
||||
contactRequest = UserContactRequestRef <$> contactRequestId <*> (unBI <$> rejectionSupported_)
|
||||
groupDirectInv = toGroupDirectInvitation groupDirectInvRow
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactRequest, contactGroupMemberId, contactGrpInvSent, groupDirectInv, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
|
||||
rowToContactDomain :: ContactDomainRow -> Maybe SimplexDomainClaim
|
||||
rowToContactDomain (domain_, domainProof_, _) = (`SimplexDomainClaim` domainProof_) . StrJSON <$> domain_
|
||||
@@ -544,13 +545,13 @@ getProfileById db userId profileId = do
|
||||
|]
|
||||
(userId, profileId)
|
||||
|
||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat) :. BadgeRow :. ContactDomainRow
|
||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64, BoolInt) :. (Int64, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat) :. BadgeRow :. ContactDomainRow
|
||||
|
||||
toContactRequest :: UTCTime -> ContactRequestRow -> UserContactRequest
|
||||
toContactRequest now ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer) :. badgeRow :. domainRow) = do
|
||||
toContactRequest now ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, BI rejectionSupported) :. (profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer) :. badgeRow :. domainRow) = do
|
||||
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, preferences, localBadge = rowToBadge now badgeRow, localAlias}
|
||||
cReqChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
||||
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt}
|
||||
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt, rejectionSupported}
|
||||
|
||||
userQuery :: Query
|
||||
userQuery =
|
||||
|
||||
@@ -60,7 +60,7 @@ import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.FileTransfer.Description (FileDigest)
|
||||
import Simplex.FileTransfer.Types (RcvFileId, SndFileId)
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, ACreatedConnLink, AConnectionLink (..), AEventTag (..), AEvtTag (..), ConnId, ConnShortLink (..), ConnectionLink (..), ConnectionMode (..), ConnectionModeI, ConnectionRequestUri, ContactConnType (..), CreatedConnLink (..), InvitationId, SAEntity (..), SConnectionMode (..), SimplexDomain, SimplexNameInfo (..), UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, ACreatedConnLink, AConnectionLink (..), AEventTag (..), AEvtTag (..), ConnId, ConnShortLink (..), ConnectionLink (..), ConnectionMode (..), ConnectionModeI, ConnectionRequestUri, ContactConnType (..), CreatedConnLink (..), InvitationId, SAEntity (..), SConnectionMode (..), SimplexDomain, SimplexNameInfo (..), UserId, sConnectionMode)
|
||||
import Simplex.Messaging.Agent.Store.DB (Binary (..), blobFieldDecoder, fromTextField_)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFileArgs (..))
|
||||
@@ -205,6 +205,7 @@ data Contact = Contact
|
||||
chatTs :: Maybe UTCTime,
|
||||
preparedContact :: Maybe PreparedContact,
|
||||
contactRequestId :: Maybe Int64,
|
||||
contactRequest :: Maybe UserContactRequestRef,
|
||||
-- contactGroupMemberId + contactGrpInvSent are used in conjunction for making connection request
|
||||
-- to a group member via direct message feature
|
||||
contactGroupMemberId :: Maybe GroupMemberId,
|
||||
@@ -232,6 +233,12 @@ data PreparedContact = PreparedContact
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data UserContactRequestRef = UserContactRequestRef
|
||||
{ contactRequestId :: Int64,
|
||||
rejectionSupported :: Bool
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data GroupDirectInvitation = GroupDirectInvitation
|
||||
{ groupDirectInvLink :: ConnReqInvitation,
|
||||
fromGroupId_ :: Maybe GroupId,
|
||||
@@ -316,6 +323,7 @@ data ContactStatus
|
||||
= CSActive
|
||||
| CSDeleted
|
||||
| CSDeletedByUser
|
||||
| CSRejected
|
||||
deriving (Eq, Show, Ord)
|
||||
|
||||
instance FromField ContactStatus where fromField = fromTextField_ textDecode
|
||||
@@ -334,11 +342,13 @@ instance TextEncoding ContactStatus where
|
||||
"active" -> Just CSActive
|
||||
"deleted" -> Just CSDeleted
|
||||
"deletedByUser" -> Just CSDeletedByUser
|
||||
"rejected" -> Just CSRejected
|
||||
_ -> Nothing
|
||||
textEncode = \case
|
||||
CSActive -> "active"
|
||||
CSDeleted -> "deleted"
|
||||
CSDeletedByUser -> "deletedByUser"
|
||||
CSRejected -> "rejected"
|
||||
|
||||
data ContactRef = ContactRef
|
||||
{ contactId :: ContactId,
|
||||
@@ -383,7 +393,8 @@ data UserContactRequest = UserContactRequest
|
||||
xContactId :: Maybe XContactId,
|
||||
pqSupport :: PQSupport,
|
||||
welcomeSharedMsgId :: Maybe SharedMsgId,
|
||||
requestSharedMsgId :: Maybe SharedMsgId
|
||||
requestSharedMsgId :: Maybe SharedMsgId,
|
||||
rejectionSupported :: Bool
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -995,6 +1006,26 @@ instance ToJSON GroupRejectionReason where
|
||||
toJSON = strToJSON
|
||||
toEncoding = strToJEncoding
|
||||
|
||||
data ContactRejectionReason
|
||||
= CRRUserRejected
|
||||
| CRRUnknown {text :: Text}
|
||||
deriving (Eq, Show)
|
||||
|
||||
instance StrEncoding ContactRejectionReason where
|
||||
strEncode = \case
|
||||
CRRUserRejected -> "user_rejected"
|
||||
CRRUnknown text -> encodeUtf8 text
|
||||
strP =
|
||||
"user_rejected" $> CRRUserRejected
|
||||
<|> CRRUnknown . safeDecodeUtf8 <$> A.takeByteString
|
||||
|
||||
instance FromJSON ContactRejectionReason where
|
||||
parseJSON = strParseJSON "ContactRejectionReason"
|
||||
|
||||
instance ToJSON ContactRejectionReason where
|
||||
toJSON = strToJSON
|
||||
toEncoding = strToJEncoding
|
||||
|
||||
data RelayRejectionReason
|
||||
= RRRRejoinRejected
|
||||
| RRRUnknown {text :: Text}
|
||||
@@ -1833,6 +1864,17 @@ instance StrEncoding AConnectTarget where
|
||||
where
|
||||
nameStart = "@" <|> "#" <|> "simplex:/name"
|
||||
|
||||
instance ConnectionModeI m => StrEncoding (ConnectTarget m) where
|
||||
strEncode t = strEncode $ ACTarget sConnectionMode t
|
||||
strP = connectTargetP
|
||||
|
||||
connectTargetP :: forall m. ConnectionModeI m => A.Parser (ConnectTarget m)
|
||||
connectTargetP = do
|
||||
ACTarget m t <- strP
|
||||
case testEquality m (sConnectionMode :: SConnectionMode m) of
|
||||
Just Refl -> pure t
|
||||
Nothing -> fail "bad connect target mode"
|
||||
|
||||
aConnectTarget :: AConnectionLink -> AConnectTarget
|
||||
aConnectTarget (ACL SCMInvitation cl) = ACTarget SCMInvitation (CTInv cl)
|
||||
aConnectTarget (ACL SCMContact cl) = ACTarget SCMContact $ case cl of
|
||||
@@ -2345,6 +2387,8 @@ $(JQ.deriveJSON defaultJSON ''FileTransferMeta)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''PreparedContact)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''UserContactRequestRef)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupDirectInvitation)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''LocalFileMeta)
|
||||
|
||||
+30
-21
@@ -112,7 +112,7 @@ chatErrorToView :: Bool -> ChatConfig -> ChatError -> [StyledString]
|
||||
chatErrorToView isCmd ChatConfig {logLevel, testView} = viewChatError isCmd logLevel testView
|
||||
|
||||
chatResponseToView :: (Maybe RemoteHostId, Maybe User) -> ChatConfig -> Bool -> CurrentTime -> TimeZone -> Maybe RemoteHostId -> ChatResponse -> [StyledString]
|
||||
chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveItems ts tz outputRH = \case
|
||||
chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, showFullLinks, testView} liveItems ts tz outputRH = \case
|
||||
CRActiveUser User {profile = p@LocalProfile {localBadge}, uiThemes} -> viewUserProfile localBadge (fromLocalProfile p) <> viewUITheme uiThemes
|
||||
CRUsersList users -> viewUsersList users
|
||||
CRChatStarted -> ["chat started"]
|
||||
@@ -179,9 +179,11 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
HSDatabase -> databaseHelpInfo
|
||||
CRWelcome user -> chatWelcome user
|
||||
CRContactsList u cs -> ttyUser u $ viewContactsList cs
|
||||
CRUserContactLink u UserContactLink {connLinkContact, addressSettings} -> ttyUser u $ connReqContact_ "Your chat address:" connLinkContact <> viewAddressSettings addressSettings
|
||||
CRUserContactLink u UserContactLink {connLinkContact, addressSettings} -> ttyUser u $ connReqContact_ showFullLinks "Your chat address:" connLinkContact <> viewAddressSettings addressSettings
|
||||
CRUserContactLinkUpdated u UserContactLink {addressSettings} -> ttyUser u $ viewAddressSettings addressSettings
|
||||
CRContactRequestRejected u UserContactRequest {localDisplayName = c} _ct_ -> ttyUser u [ttyContact c <> ": contact request rejected"]
|
||||
CRServiceResponse u resp -> ttyUser u ["service response: " <> viewJSON resp]
|
||||
CRServiceReplyAccepted u (AgentConnId cId) -> ttyUser u [plain $ "service reply accepted, connection id: " <> safeDecodeUtf8 (strEncode cId)]
|
||||
CRGroupCreated u g -> ttyUser u $ viewGroupCreated g testView
|
||||
CRPublicGroupCreated u g _groupLink _relays -> ttyUser u $ viewGroupCreated g testView
|
||||
CRPublicGroupCreationFailed u results -> ttyUser u $ viewPublicGroupCreationFailed results
|
||||
@@ -201,9 +203,9 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRUserProfileNoChange u -> ttyUser u ["user profile did not change"]
|
||||
CRUserPrivacy u u' -> ttyUserPrefix hu outputRH u $ viewUserPrivacy u u'
|
||||
CRVersionInfo info _ _ -> viewVersionInfo logLevel info
|
||||
CRInvitation u ccLink _ -> ttyUser u $ viewConnReqInvitation ccLink
|
||||
CRInvitation u ccLink _ -> ttyUser u $ viewConnReqInvitation showFullLinks ccLink
|
||||
CRConnectionIncognitoUpdated u c customUserProfile -> ttyUser u $ viewConnectionIncognitoUpdated c customUserProfile testView
|
||||
CRConnectionUserChanged u c c' nu -> ttyUser u $ viewConnectionUserChanged u c nu c'
|
||||
CRConnectionUserChanged u c c' nu -> ttyUser u $ viewConnectionUserChanged showFullLinks u c nu c'
|
||||
CRConnectionPlan u connLink _ otherSimplexName connectionPlan -> ttyUser u $ viewConnectionPlan cfg connLink connectionPlan <> otherSimplexNameNote otherSimplexName
|
||||
CRNewPreparedChat u (AChat _ (Chat cInfo _ _)) -> ttyUser u $ case cInfo of
|
||||
DirectChat ct -> [ttyContact' ct <> ": contact is prepared"]
|
||||
@@ -221,7 +223,7 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRChatCleared u chatInfo -> ttyUser u $ viewChatCleared chatInfo
|
||||
CRAcceptingContactRequest u c -> ttyUser u $ viewAcceptingContactRequest c
|
||||
CRContactAlreadyExists u c -> ttyUser u [ttyFullContact c <> ": contact already exists"]
|
||||
CRUserContactLinkCreated u ccLink -> ttyUser u $ connReqContact_ "Your new chat address is created!" ccLink
|
||||
CRUserContactLinkCreated u ccLink -> ttyUser u $ connReqContact_ showFullLinks "Your new chat address is created!" ccLink
|
||||
CRUserContactLinkDeleted u -> ttyUser u viewUserContactLinkDeleted
|
||||
CRUserAcceptedGroupSent u _g _ -> ttyUser u [] -- [ttyGroup' g <> ": joining the group..."]
|
||||
CRUserDeletedMembers u g members wm signed -> case members of
|
||||
@@ -260,8 +262,8 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRGroupUpdated u g g' m signed -> ttyUser u $ viewGroupUpdated g g' m (if signed then Just MSSVerified else Nothing)
|
||||
CRGroupProfile u g -> ttyUser u $ viewGroupProfile g
|
||||
CRGroupDescription u g -> ttyUser u $ viewGroupDescription g
|
||||
CRGroupLinkCreated u g gLink -> ttyUser u $ groupLink_ "Group link is created!" g gLink
|
||||
CRGroupLink u g gLink -> ttyUser u $ groupLink_ "Group link:" g gLink
|
||||
CRGroupLinkCreated u g gLink -> ttyUser u $ groupLink_ showFullLinks "Group link is created!" g gLink
|
||||
CRGroupLink u g gLink -> ttyUser u $ groupLink_ showFullLinks "Group link:" g gLink
|
||||
CRGroupLinkDeleted u g -> ttyUser u $ viewGroupLinkDeleted g
|
||||
CRNewMemberContact u _ g m -> ttyUser u ["contact for member " <> ttyGroup' g <> " " <> ttyMember m <> " is created"]
|
||||
CRNewMemberContactSentInv u _ct g m -> ttyUser u ["sent invitation to connect directly to member " <> ttyGroup' g <> " " <> ttyMember m]
|
||||
@@ -462,6 +464,13 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
||||
CEvtContactUpdated {user = u, fromContact = c, toContact = c'} -> ttyUser u $ viewContactUpdated c c' <> viewContactPrefsUpdated u c c'
|
||||
CEvtGroupMemberUpdated {} -> []
|
||||
CEvtReceivedContactRequest u UserContactRequest {localDisplayName = c, profile} _chat -> ttyUser u $ viewReceivedContactRequest c (fromLocalProfile profile)
|
||||
CEvtServiceRequest u reqId sigKey_ req ->
|
||||
ttyUser u $
|
||||
[plain $ "service request " <> safeDecodeUtf8 (strEncode reqId)]
|
||||
<> maybe [] (\k -> [plain $ "signed by " <> safeDecodeUtf8 (strEncode k)]) sigKey_
|
||||
<> ["request: " <> viewJSON req]
|
||||
CEvtServiceReplySent (AgentConnId cId) -> [plain $ "service reply sent, connection id: " <> safeDecodeUtf8 (strEncode cId)]
|
||||
CEvtContactRequestRejected u Contact {localDisplayName = c} _reason -> ttyUser u [ttyContact c <> ": contact request rejected"]
|
||||
CEvtRcvFileStart u ci -> ttyUser u $ receivingFile_' hu testView "started" ci
|
||||
CEvtRcvFileComplete u ci -> ttyUser u $ receivingFile_' hu testView "completed" ci
|
||||
CEvtRcvStandaloneFileComplete u _ ft -> ttyUser u $ receivingFileStandalone "completed" ft
|
||||
@@ -1038,8 +1047,8 @@ viewInvalidConnReq =
|
||||
plain updateStr
|
||||
]
|
||||
|
||||
viewConnReqInvitation :: CreatedLinkInvitation -> [StyledString]
|
||||
viewConnReqInvitation (CCLink cReq shortLink) =
|
||||
viewConnReqInvitation :: Bool -> CreatedLinkInvitation -> [StyledString]
|
||||
viewConnReqInvitation showFullLinks (CCLink cReq shortLink) =
|
||||
[ "pass this invitation link to your contact (via another channel): ",
|
||||
"",
|
||||
plain $ maybe cReqStr strEncode shortLink,
|
||||
@@ -1047,7 +1056,7 @@ viewConnReqInvitation (CCLink cReq shortLink) =
|
||||
"and ask them to connect: " <> highlight' "/c <invitation_link_above>"
|
||||
]
|
||||
<>
|
||||
if isJust shortLink
|
||||
if showFullLinks && isJust shortLink
|
||||
then
|
||||
[ "The invitation link for old clients:",
|
||||
plain cReqStr
|
||||
@@ -1110,8 +1119,8 @@ viewForwardPlan count itemIds = maybe [forwardCount] $ \fc -> [confirmation fc,
|
||||
| otherwise = plain $ show len <> " message(s) out of " <> show count <> " can be forwarded"
|
||||
len = length itemIds
|
||||
|
||||
connReqContact_ :: StyledString -> CreatedLinkContact -> [StyledString]
|
||||
connReqContact_ intro (CCLink cReq shortLink) =
|
||||
connReqContact_ :: Bool -> StyledString -> CreatedLinkContact -> [StyledString]
|
||||
connReqContact_ showFullLinks intro (CCLink cReq shortLink) =
|
||||
[ intro,
|
||||
"",
|
||||
plain $ maybe cReqStr strEncode shortLink,
|
||||
@@ -1121,16 +1130,16 @@ connReqContact_ intro (CCLink cReq shortLink) =
|
||||
"to share with your contacts: " <> highlight' "/profile_address on",
|
||||
"to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)"
|
||||
]
|
||||
<> ["The contact link for old clients: " <> plain cReqStr | isJust shortLink]
|
||||
<> ["The contact link for old clients: " <> plain cReqStr | showFullLinks, isJust shortLink]
|
||||
where
|
||||
cReqStr = strEncode $ simplexChatContact cReq
|
||||
|
||||
simplexChatContact :: ConnReqContact -> ConnReqContact
|
||||
simplexChatContact (CRContactUri crData) = CRContactUri crData {crScheme = simplexChat}
|
||||
simplexChatContact (CRContactUri crData e2e) = CRContactUri crData {crScheme = simplexChat} e2e
|
||||
|
||||
simplexChatContact' :: ConnLinkContact -> ConnLinkContact
|
||||
simplexChatContact' = \case
|
||||
CLFull (CRContactUri crData) -> CLFull $ CRContactUri crData {crScheme = simplexChat}
|
||||
CLFull (CRContactUri crData e2e) -> CLFull $ CRContactUri crData {crScheme = simplexChat} e2e
|
||||
l@(CLShort _) -> l
|
||||
|
||||
groupSimplexDomain :: GroupInfo -> Maybe SimplexDomain
|
||||
@@ -1171,8 +1180,8 @@ viewAddressSettings AddressSettings {businessAddress, autoAccept, autoReply} = c
|
||||
| otherwise = ""
|
||||
_ -> ["auto_accept off"]
|
||||
|
||||
groupLink_ :: StyledString -> GroupInfo -> GroupLink -> [StyledString]
|
||||
groupLink_ intro g GroupLink {connLinkContact = CCLink cReq shortLink, acceptMemberRole} =
|
||||
groupLink_ :: Bool -> StyledString -> GroupInfo -> GroupLink -> [StyledString]
|
||||
groupLink_ showFullLinks intro g GroupLink {connLinkContact = CCLink cReq shortLink, acceptMemberRole} =
|
||||
[ intro,
|
||||
"",
|
||||
plain $ maybe cReqStr strEncode shortLink
|
||||
@@ -1183,7 +1192,7 @@ groupLink_ intro g GroupLink {connLinkContact = CCLink cReq shortLink, acceptMem
|
||||
"to show it again: " <> highlight ("/show link #" <> viewGroupName g),
|
||||
"to delete it: " <> highlight ("/delete link #" <> viewGroupName g) <> " (joined members will remain connected to you)"
|
||||
]
|
||||
<> ["The group link for old clients: " <> plain cReqStr | isJust shortLink]
|
||||
<> ["The group link for old clients: " <> plain cReqStr | showFullLinks, isJust shortLink]
|
||||
where
|
||||
cReqStr = strEncode $ simplexChatContact cReq
|
||||
|
||||
@@ -2115,8 +2124,8 @@ viewConnectionIncognitoUpdated PendingContactConnection {pccConnId, customUserPr
|
||||
Nothing -> ["unexpected response when changing connection, please report to developers"]
|
||||
| otherwise = ["connection " <> sShow pccConnId <> " changed to non incognito"]
|
||||
|
||||
viewConnectionUserChanged :: User -> PendingContactConnection -> User -> PendingContactConnection -> [StyledString]
|
||||
viewConnectionUserChanged User {localDisplayName = n} PendingContactConnection {pccConnId} User {localDisplayName = n'} PendingContactConnection {connLinkInv = connLinkInv'} =
|
||||
viewConnectionUserChanged :: Bool -> User -> PendingContactConnection -> User -> PendingContactConnection -> [StyledString]
|
||||
viewConnectionUserChanged showFullLinks User {localDisplayName = n} PendingContactConnection {pccConnId} User {localDisplayName = n'} PendingContactConnection {connLinkInv = connLinkInv'} =
|
||||
case connLinkInv' of
|
||||
Just ccLink' -> [userChangedStr <> ", new link:"] <> newLink ccLink'
|
||||
_ -> [userChangedStr]
|
||||
@@ -2128,7 +2137,7 @@ viewConnectionUserChanged User {localDisplayName = n} PendingContactConnection {
|
||||
""
|
||||
]
|
||||
<>
|
||||
if isJust shortLink
|
||||
if showFullLinks && isJust shortLink
|
||||
then
|
||||
[ "The invitation link for old clients:",
|
||||
plain cReqStr
|
||||
|
||||
Reference in New Issue
Block a user