diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index ef56e4205..e224f04eb 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -2222,7 +2222,7 @@ processChatCommand = \case processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ updateSettings chatSettings connectPlan :: User -> AConnectionRequestUri -> m ConnectionPlan connectPlan user (ACR SCMInvitation cReq) = do - withStore' (\db -> getConnectionEntityByConnReq db user cReq) >>= \case + withStore' (\db -> getConnectionEntityByConnReq db user cReqSchemas) >>= \case Nothing -> pure $ CPInvitationLink ILPOk Just (RcvDirectMsgConnection conn ct_) -> do let Connection {connStatus, contactConnInitiated} = conn @@ -2235,17 +2235,23 @@ processChatCommand = \case Just ct -> pure $ CPInvitationLink (ILPKnown ct) Nothing -> throwChatError $ CEInternalError "ready RcvDirectMsgConnection connection should have associated contact" Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection" + where + cReqSchemas :: (ConnReqInvitation, ConnReqInvitation) + cReqSchemas = case cReq of + (CRInvitationUri crData e2e) -> + ( CRInvitationUri crData {crScheme = CRSSimplex} e2e, + CRInvitationUri crData {crScheme = simplexChat} e2e + ) connectPlan user (ACR SCMContact cReq) = do let CRContactUri ConnReqUriData {crClientData} = cReq groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli case groupLinkId of -- contact address Nothing -> - withStore' (`getUserContactLinkByConnReq` cReq) >>= \case + withStore' (`getUserContactLinkByConnReq` cReqSchemas) >>= \case Just _ -> pure $ CPContactAddress CAPOwnLink Nothing -> do - let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq - withStore' (\db -> getContactConnEntityByConnReqHash db user cReqHash) >>= \case + withStore' (\db -> getContactConnEntityByConnReqHash db user cReqHashes) >>= \case Nothing -> pure $ CPContactAddress CAPOk Just (RcvDirectMsgConnection _conn Nothing) -> pure $ CPContactAddress CAPConnectingConfirmReconnect Just (RcvDirectMsgConnection _ (Just ct)) @@ -2255,12 +2261,11 @@ processChatCommand = \case Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection" -- group link Just _ -> - withStore' (\db -> getGroupInfoByUserContactLinkConnReq db user cReq) >>= \case + withStore' (\db -> getGroupInfoByUserContactLinkConnReq db user cReqSchemas) >>= \case Just g -> pure $ CPGroupLink (GLPOwnLink g) Nothing -> do - let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq - connEnt_ <- withStore' $ \db -> getContactConnEntityByConnReqHash db user cReqHash - gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db user cReqHash + connEnt_ <- withStore' $ \db -> getContactConnEntityByConnReqHash db user cReqHashes + gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db user cReqHashes case (gInfo_, connEnt_) of (Nothing, Nothing) -> pure $ CPGroupLink GLPOk (Nothing, Just (RcvDirectMsgConnection _conn Nothing)) -> pure $ CPGroupLink GLPConnectingConfirmReconnect @@ -2273,6 +2278,16 @@ processChatCommand = \case pure $ CPGroupLink (GLPConnectingProhibit gInfo_) | memberActive membership -> pure $ CPGroupLink (GLPKnown gInfo) | otherwise -> pure $ CPGroupLink GLPOk + where + cReqSchemas :: (ConnReqContact, ConnReqContact) + cReqSchemas = case cReq of + (CRContactUri crData) -> + ( CRContactUri crData {crScheme = CRSSimplex}, + CRContactUri crData {crScheme = simplexChat} + ) + cReqHashes :: (ConnReqUriHash, ConnReqUriHash) + cReqHashes = bimap hash hash cReqSchemas + hash = ConnReqUriHash . C.sha256Hash . strEncode assertDirectAllowed :: ChatMonad m => User -> MsgDirection -> Contact -> CMEventTag e -> m () assertDirectAllowed user dir ct event = diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs index d8ec38269..59ffb57c6 100644 --- a/src/Simplex/Chat/Store/Connections.hs +++ b/src/Simplex/Chat/Store/Connections.hs @@ -154,18 +154,18 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do userContact_ [(cReq, groupId)] = Right UserContact {userContactLinkId, connReqContact = cReq, groupId} userContact_ _ = Left SEUserContactLinkNotFound -getConnectionEntityByConnReq :: DB.Connection -> User -> ConnReqInvitation -> IO (Maybe ConnectionEntity) -getConnectionEntityByConnReq db user cReq = do +getConnectionEntityByConnReq :: DB.Connection -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity) +getConnectionEntityByConnReq db user (cReqSchema1, cReqSchema2) = do connId_ <- maybeFirstRow fromOnly $ - DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv = ? LIMIT 1" (Only cReq) + DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv IN (?,?) LIMIT 1" (cReqSchema1, cReqSchema2) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_ -- search connection for connection plan: -- multiple connections can have same via_contact_uri_hash if request was repeated; -- this function searches for latest connection with contact so that "known contact" plan would be chosen; -- deleted connections are filtered out to allow re-connecting via same contact address -getContactConnEntityByConnReqHash :: DB.Connection -> User -> ConnReqUriHash -> IO (Maybe ConnectionEntity) -getContactConnEntityByConnReqHash db user cReqHash = do +getContactConnEntityByConnReqHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity) +getContactConnEntityByConnReqHash db user (cReqHash1, cReqHash2) = do connId_ <- maybeFirstRow fromOnly $ DB.query db @@ -175,12 +175,12 @@ getContactConnEntityByConnReqHash db user cReqHash = do agent_conn_id, (CASE WHEN contact_id IS NOT NULL THEN 1 ELSE 0 END) AS conn_ord FROM connections - WHERE via_contact_uri_hash = ? AND conn_status != ? + WHERE via_contact_uri_hash IN (?,?) AND conn_status != ? ORDER BY conn_ord DESC, created_at DESC LIMIT 1 ) |] - (cReqHash, ConnDeleted) + (cReqHash1, cReqHash2, ConnDeleted) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_ getConnectionsToSubscribe :: DB.Connection -> IO ([ConnId], [ConnectionEntity]) diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index 30e45a82d..a4a19816d 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -1121,21 +1121,21 @@ getGroupInfo db User {userId, userContactId} groupId = |] (groupId, userId, userContactId) -getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> ConnReqContact -> IO (Maybe GroupInfo) -getGroupInfoByUserContactLinkConnReq db user cReq = do +getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo) +getGroupInfoByUserContactLinkConnReq db user (cReqSchema1, cReqSchema2) = do groupId_ <- maybeFirstRow fromOnly $ DB.query db [sql| SELECT group_id FROM user_contact_links - WHERE conn_req_contact = ? + WHERE conn_req_contact IN (?,?) |] - (Only cReq) + (cReqSchema1, cReqSchema2) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_ -getGroupInfoByGroupLinkHash :: DB.Connection -> User -> ConnReqUriHash -> IO (Maybe GroupInfo) -getGroupInfoByGroupLinkHash db user@User {userId, userContactId} groupLinkHash = do +getGroupInfoByGroupLinkHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo) +getGroupInfoByGroupLinkHash db user@User {userId, userContactId} (groupLinkHash1, groupLinkHash2) = do groupId_ <- maybeFirstRow fromOnly $ DB.query db @@ -1143,11 +1143,11 @@ getGroupInfoByGroupLinkHash db user@User {userId, userContactId} groupLinkHash = SELECT g.group_id FROM groups g JOIN group_members mu ON mu.group_id = g.group_id - WHERE g.user_id = ? AND g.via_group_link_uri_hash = ? + WHERE g.user_id = ? AND g.via_group_link_uri_hash IN (?,?) AND mu.contact_id = ? AND mu.member_status NOT IN (?,?,?) LIMIT 1 |] - (userId, groupLinkHash, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted) + (userId, groupLinkHash1, groupLinkHash2, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_ getGroupIdByName :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO GroupId diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs index fa573e4e6..c9c480649 100644 --- a/src/Simplex/Chat/Store/Profiles.hs +++ b/src/Simplex/Chat/Store/Profiles.hs @@ -441,17 +441,17 @@ getUserContactLinkById db userId userContactLinkId = |] (userId, userContactLinkId) -getUserContactLinkByConnReq :: DB.Connection -> ConnReqContact -> IO (Maybe UserContactLink) -getUserContactLinkByConnReq db cReq = +getUserContactLinkByConnReq :: DB.Connection -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink) +getUserContactLinkByConnReq db (cReqSchema1, cReqSchema2) = maybeFirstRow toUserContactLink $ DB.query db [sql| SELECT conn_req_contact, auto_accept, auto_accept_incognito, auto_reply_msg_content FROM user_contact_links - WHERE conn_req_contact = ? + WHERE conn_req_contact IN (?,?) |] - (Only cReq) + (cReqSchema1, cReqSchema2) updateUserAddressAutoAccept :: DB.Connection -> User -> Maybe AutoAccept -> ExceptT StoreError IO UserContactLink updateUserAddressAutoAccept db user@User {userId} autoAccept = do diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 677bc4c09..1a133fd8e 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -270,6 +270,10 @@ testPlanInvitationLinkOwn tmp = alice ##> ("/_connect plan 1 " <> inv) alice <## "invitation link: own link" + let invSchema2 = linkAnotherSchema inv + alice ##> ("/_connect plan 1 " <> invSchema2) + alice <## "invitation link: own link" + alice ##> ("/c " <> inv) alice <## "confirmation sent!" alice @@ -305,6 +309,10 @@ testPlanInvitationLinkConnecting tmp = do bob ##> ("/_connect plan 1 " <> inv) bob <## "invitation link: connecting" + let invSchema2 = linkAnotherSchema inv + bob ##> ("/_connect plan 1 " <> invSchema2) + bob <## "invitation link: connecting" + testContactClear :: HasCallStack => FilePath -> IO () testContactClear = testChat2 aliceProfile bobProfile $ diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 43498711e..7a8b1368b 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -2290,6 +2290,11 @@ testPlanGroupLinkOkKnown = bob <## "group link: known group #team" bob <## "use #team to send messages" + let gLinkSchema2 = linkAnotherSchema gLink + bob ##> ("/_connect plan 1 " <> gLinkSchema2) + bob <## "group link: known group #team" + bob <## "use #team to send messages" + bob ##> ("/c " <> gLink) bob <## "group link: known group #team" bob <## "use #team to send messages" @@ -2331,6 +2336,11 @@ testPlanHostContactDeletedGroupLinkKnown = bob <## "group link: known group #team" bob <## "use #team to send messages" + let gLinkSchema2 = linkAnotherSchema gLink + bob ##> ("/_connect plan 1 " <> gLinkSchema2) + bob <## "group link: known group #team" + bob <## "use #team to send messages" + bob ##> ("/c " <> gLink) bob <## "group link: known group #team" bob <## "use #team to send messages" @@ -2347,6 +2357,10 @@ testPlanGroupLinkOwn tmp = alice ##> ("/_connect plan 1 " <> gLink) alice <## "group link: own link for group #team" + let gLinkSchema2 = linkAnotherSchema gLink + alice ##> ("/_connect plan 1 " <> gLinkSchema2) + alice <## "group link: own link for group #team" + alice ##> ("/c " <> gLink) alice <## "connection request sent!" alice <## "alice_1 (Alice): accepting request to join group #team..." @@ -2373,6 +2387,9 @@ testPlanGroupLinkOwn tmp = alice ##> ("/_connect plan 1 " <> gLink) alice <## "group link: own link for group #team" + alice ##> ("/_connect plan 1 " <> gLinkSchema2) + alice <## "group link: own link for group #team" + -- group works if merged contact is deleted alice ##> "/d alice_1" alice <## "alice_1: contact is deleted" @@ -2405,6 +2422,10 @@ testPlanGroupLinkConnecting tmp = do bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: connecting, allowed to reconnect" + let gLinkSchema2 = linkAnotherSchema gLink + bob ##> ("/_connect plan 1 " <> gLinkSchema2) + bob <## "group link: connecting, allowed to reconnect" + threadDelay 100000 withTestChat tmp "alice" $ \alice -> do alice @@ -2417,6 +2438,10 @@ testPlanGroupLinkConnecting tmp = do bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: connecting" + let gLinkSchema2 = linkAnotherSchema gLink + bob ##> ("/_connect plan 1 " <> gLinkSchema2) + bob <## "group link: connecting" + bob ##> ("/c " <> gLink) bob <## "group link: connecting" @@ -2462,6 +2487,10 @@ testPlanGroupLinkLeaveRejoin = bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: ok to connect" + let gLinkSchema2 = linkAnotherSchema gLink + bob ##> ("/_connect plan 1 " <> gLinkSchema2) + bob <## "group link: ok to connect" + bob ##> ("/c " <> gLink) bob <## "connection request sent!" alice <## "bob_1 (Bob): accepting request to join group #team..." @@ -2490,6 +2519,10 @@ testPlanGroupLinkLeaveRejoin = bob <## "group link: known group #team_1" bob <## "use #team_1 to send messages" + bob ##> ("/_connect plan 1 " <> gLinkSchema2) + bob <## "group link: known group #team_1" + bob <## "use #team_1 to send messages" + bob ##> ("/c " <> gLink) bob <## "group link: known group #team_1" bob <## "use #team_1 to send messages" diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index 5682d09ee..bd7f4c168 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -599,6 +599,11 @@ testPlanAddressOkKnown = bob <## "contact address: known contact alice" bob <## "use @alice to send messages" + let cLinkSchema2 = linkAnotherSchema cLink + bob ##> ("/_connect plan 1 " <> cLinkSchema2) + bob <## "contact address: known contact alice" + bob <## "use @alice to send messages" + bob ##> ("/c " <> cLink) bob <## "contact address: known contact alice" bob <## "use @alice to send messages" @@ -612,11 +617,15 @@ testPlanAddressOwn tmp = alice ##> ("/_connect plan 1 " <> cLink) alice <## "contact address: own address" + let cLinkSchema2 = linkAnotherSchema cLink + alice ##> ("/_connect plan 1 " <> cLinkSchema2) + alice <## "contact address: own address" + alice ##> ("/c " <> cLink) alice <## "connection request sent!" alice <## "alice_1 (Alice) wants to connect to you!" alice <## "to accept: /ac alice_1" - alice <## ("to reject: /rc alice_1 (the sender will NOT be notified)") + alice <## "to reject: /rc alice_1 (the sender will NOT be notified)" alice @@@ [("<@alice_1", ""), (":2","")] alice ##> "/ac alice_1" alice <## "alice_1 (Alice): accepting contact request..." @@ -658,6 +667,10 @@ testPlanAddressConnecting tmp = do bob ##> ("/_connect plan 1 " <> cLink) bob <## "contact address: connecting, allowed to reconnect" + let cLinkSchema2 = linkAnotherSchema cLink + bob ##> ("/_connect plan 1 " <> cLinkSchema2) + bob <## "contact address: connecting, allowed to reconnect" + threadDelay 100000 withTestChat tmp "alice" $ \alice -> do alice <## "Your address is active! To show: /sa" @@ -672,6 +685,10 @@ testPlanAddressConnecting tmp = do bob ##> ("/_connect plan 1 " <> cLink) bob <## "contact address: connecting to contact alice" + let cLinkSchema2 = linkAnotherSchema cLink + bob ##> ("/_connect plan 1 " <> cLinkSchema2) + bob <## "contact address: connecting to contact alice" + bob ##> ("/c " <> cLink) bob <## "contact address: connecting to contact alice" @@ -706,6 +723,10 @@ testPlanAddressContactDeletedReconnected = bob ##> ("/_connect plan 1 " <> cLink) bob <## "contact address: ok to connect" + let cLinkSchema2 = linkAnotherSchema cLink + bob ##> ("/_connect plan 1 " <> cLinkSchema2) + bob <## "contact address: ok to connect" + bob ##> ("/c " <> cLink) bob <## "connection request sent!" alice <## "bob (Bob) wants to connect to you!" @@ -726,6 +747,11 @@ testPlanAddressContactDeletedReconnected = bob <## "contact address: known contact alice_1" bob <## "use @alice_1 to send messages" + let cLinkSchema2 = linkAnotherSchema cLink + bob ##> ("/_connect plan 1 " <> cLinkSchema2) + bob <## "contact address: known contact alice_1" + bob <## "use @alice_1 to send messages" + bob ##> ("/c " <> cLink) bob <## "contact address: known contact alice_1" bob <## "use @alice_1 to send messages" diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index d98431e81..fcb6b65e4 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -559,3 +559,11 @@ currentChatVRangeInfo = vRangeStr :: VersionRange -> String vRangeStr (VersionRange minVer maxVer) = "(" <> show minVer <> ", " <> show maxVer <> ")" + +linkAnotherSchema :: String -> String +linkAnotherSchema link + | "https://simplex.chat/" `isPrefixOf` link = + T.unpack $ T.replace "https://simplex.chat/" "simplex:/" $ T.pack link + | "simplex:/" `isPrefixOf` link = + T.unpack $ T.replace "simplex:/" "https://simplex.chat/" $ T.pack link + | otherwise = error "link starts with neither https://simplex.chat/ nor simplex:/"