diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift index 8a7f60ce08..408ee6812b 100644 --- a/apps/ios/Shared/Model/AppAPITypes.swift +++ b/apps/ios/Shared/Model/AppAPITypes.swift @@ -350,8 +350,8 @@ enum ChatCommand: ChatCmdProtocol { case let .apiConnectPlan(userId, connLink, linkOwnerSig): let sigStr = if let linkOwnerSig { " sig=\(encodeJSON(linkOwnerSig))" } else { "" } return "/_connect plan \(userId) \(connLink)\(sigStr)" - case let .apiPrepareContact(userId, connLink, contactShortLinkData, verifiedDomain): return "/_prepare contact \(userId) \(connLink.connFullLink) \(connLink.connShortLink ?? "") \(encodeJSON(contactShortLinkData))" + (verifiedDomain.map { " \($0.fullDomainName)" } ?? "") - case let .apiPrepareGroup(userId, connLink, directLink, groupShortLinkData, verifiedDomain): return "/_prepare group \(userId) \(connLink.connFullLink) \(connLink.connShortLink ?? "") direct=\(onOff(directLink)) \(encodeJSON(groupShortLinkData))" + (verifiedDomain.map { " \($0.fullDomainName)" } ?? "") + case let .apiPrepareContact(userId, connLink, contactShortLinkData, verifiedDomain): return "/_prepare contact \(userId) \(connLink.cmdString)\(verifiedDomain.map{ " \($0.cmdString)" } ?? "")\(encodeJSON(contactShortLinkData))" + case let .apiPrepareGroup(userId, connLink, directLink, groupShortLinkData, verifiedDomain): return "/_prepare group \(userId) \(connLink.cmdString) direct=\(onOff(directLink))\(verifiedDomain.map{ " \($0.cmdString)" } ?? "") \(encodeJSON(groupShortLinkData))" case let .apiChangePreparedContactUser(contactId, newUserId): return "/_set contact user @\(contactId) \(newUserId)" case let .apiChangePreparedGroupUser(groupId, newUserId): return "/_set group user #\(groupId) \(newUserId)" case let .apiConnectPreparedContact(contactId, incognito, mc): return "/_connect contact @\(contactId) incognito=\(onOff(incognito))\(maybeContent(mc))" diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 2066f7119f..47522a5deb 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -184,6 +184,10 @@ public struct CreatedConnLink: Decodable, Hashable { public func simplexChatUri(short: Bool = true) -> String { short ? (connShortLink ?? simplexChatLink(connFullLink)) : simplexChatLink(connFullLink) } + + public var cmdString: String { + connFullLink + (connShortLink.map { " \($0)"} ?? "") + } } public func simplexChatLink(_ uri: String) -> String { diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index fbdde8d8e7..3acc2c7eec 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -5312,6 +5312,10 @@ public struct SimplexDomain: Codable, Equatable, Hashable { return (subDomain.reversed() + [domain] + tld).joined(separator: ".") } + public var cmdString: String { + "domain=\(fullDomainName)" + } + public init(nameTLD: SimplexTLD, domain: String, subDomain: [String]) { self.nameTLD = nameTLD self.domain = domain diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 398448808d..ef5fe334b8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -4896,15 +4896,16 @@ data class SimplexDomain( val subDomain: List ) { // mirrors backend fullDomainName: reverse(subDomain) + [domain] + tld - val fullDomainName: String - get() { - val tld = when (nameTLD) { - SimplexTLD.simplex -> listOf("simplex") - SimplexTLD.testing -> listOf("testing") - SimplexTLD.web -> emptyList() - } - return (subDomain.reversed() + domain + tld).joinToString(".") + val fullDomainName: String get() { + val tld = when (nameTLD) { + SimplexTLD.simplex -> listOf("simplex") + SimplexTLD.testing -> listOf("testing") + SimplexTLD.web -> emptyList() } + return (subDomain.reversed() + domain + tld).joinToString(".") + } + + val cmdString: String get() = "domain=$fullDomainName" } @Serializable diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index af503d2524..bf0afe4546 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -4073,8 +4073,8 @@ sealed class CC { val sigStr = if (linkOwnerSig != null) " sig=${json.encodeToString(linkOwnerSig)}" else "" "/_connect plan $userId $connLink$sigStr" } - is APIPrepareContact -> "/_prepare contact $userId ${connLink.connFullLink} ${connLink.connShortLink ?: ""} ${json.encodeToString(contactShortLinkData)}${verifiedDomain?.let { " ${it.fullDomainName}" } ?: ""}" - is APIPrepareGroup -> "/_prepare group $userId ${connLink.connFullLink} ${connLink.connShortLink ?: ""} direct=${onOff(directLink)} ${json.encodeToString(groupShortLinkData)}${verifiedDomain?.let { " ${it.fullDomainName}" } ?: ""}" + is APIPrepareContact -> "/_prepare contact $userId ${connLink.cmdString}${verifiedDomain?.let { " ${it.cmdString}" } ?: ""} ${json.encodeToString(contactShortLinkData)}" + is APIPrepareGroup -> "/_prepare group $userId ${connLink.cmdString} direct=${onOff(directLink)}${verifiedDomain?.let { " ${it.cmdString}" } ?: ""} ${json.encodeToString(groupShortLinkData)}" is APIChangePreparedContactUser -> "/_set contact user @$contactId $newUserId" is APIChangePreparedGroupUser -> "/_set group user #$groupId $newUserId" is APIConnectPreparedContact -> "/_connect contact @$contactId incognito=${onOff(incognito)}${maybeContent(msg)}" @@ -7083,6 +7083,8 @@ data class CreatedConnLink(val connFullLink: String, val connShortLink: String?) fun simplexChatUri(short: Boolean): String = if (short) connShortLink ?: simplexChatLink(connFullLink) else simplexChatLink(connFullLink) + + val cmdString: String get() = connFullLink + (if (connShortLink == null) "" else " $connShortLink") } fun simplexChatLink(uri: String): String = diff --git a/apps/simplex-directory-service/src/Directory/Service.hs b/apps/simplex-directory-service/src/Directory/Service.hs index 8cd1422f78..044c08ef1b 100644 --- a/apps/simplex-directory-service/src/Directory/Service.hs +++ b/apps/simplex-directory-service/src/Directory/Service.hs @@ -979,7 +979,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName let GroupShortLinkData {groupProfile = GroupProfile {displayName}} = groupSLinkData ownerContact = GroupOwnerContact {contactId = contactId' ct, memberId = mId} sendMessage cc ct $ "Joining the " <> gt <> " " <> displayName <> "…" - sendChatCmd cc (APIPrepareGroup userId ccLink False groupSLinkData Nothing) >>= \case + sendChatCmd cc (APIPrepareGroup userId ccLink False Nothing groupSLinkData) >>= \case Right (CRNewPreparedChat _ (AChat SCTGroup (Chat (GroupChat gInfo _) _ _))) -> do let gId = groupId' gInfo addGroupReg notifyAdminUsers st cc ct gInfo GRSProposed $ \_ -> pure () diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 62a8445f8f..50b862758b 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -530,8 +530,8 @@ data ChatCommand | APISetConnectionIncognito Int64 IncognitoEnabled | APIChangeConnectionUser Int64 UserId -- new user id to switch connection to | APIConnectPlan {userId :: UserId, connectTarget :: Maybe AConnectTarget, resolveKnown :: Bool, linkOwnerSig :: Maybe LinkOwnerSig} -- Maybe AConnectTarget is used to report parsing failure as special error - | APIPrepareContact UserId ACreatedConnLink ContactShortLinkData (Maybe SimplexDomain) - | APIPrepareGroup UserId CreatedLinkContact DirectLink GroupShortLinkData (Maybe SimplexDomain) + | APIPrepareContact UserId ACreatedConnLink (Maybe SimplexDomain) ContactShortLinkData + | APIPrepareGroup UserId CreatedLinkContact DirectLink (Maybe SimplexDomain) GroupShortLinkData | APIChangePreparedContactUser ContactId UserId | APIChangePreparedGroupUser GroupId UserId | APIConnectPreparedContact {contactId :: ContactId, incognito :: IncognitoEnabled, msgContent_ :: Maybe MsgContent} diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index d13d04462b..836f4e72fd 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -56,7 +56,7 @@ import Data.Type.Equality import qualified Data.UUID as UUID import qualified Data.UUID.V4 as V4 import Simplex.Chat.Library.Subscriber -import Simplex.Chat.Badges (BadgeCredential (..), LocalBadge (..), ProofPresHeader (..), maxXFTPFileSize, mkBadgeStatus, verifyCredential) +import Simplex.Chat.Badges (BadgeCredential (..), LocalBadge (..), maxXFTPFileSize, mkBadgeStatus, verifyCredential) import Simplex.Chat.Names (SimplexDomainProof (..), SimplexDomainClaim (..), claimDomain, mkDomainClaim) import Simplex.Chat.Call import Simplex.Chat.Controller @@ -96,7 +96,7 @@ import Simplex.Chat.Web (webPreviewWorker) import Simplex.FileTransfer.Description (FileDescriptionURI (..), maxFileSizeHard) import Simplex.Messaging.Agent import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..), ServerRoles (..), allRoles) -import Simplex.Messaging.Agent.Protocol hiding (ConnectTarget (..)) +import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Agent.Store.Entity import Simplex.Messaging.Agent.Store.Interface (execSQL) import Simplex.Messaging.Agent.Store.Shared (upMigration) @@ -2061,7 +2061,7 @@ processChatCommand cxt nm = \case APIConnectPlan userId (Just ct) resolveKnown linkOwnerSig_ -> withUserId userId $ \user -> uncurry (CRConnectionPlan user) <$> connectPlan user ct resolveKnown linkOwnerSig_ APIConnectPlan _ Nothing _ _ -> throwChatError CEInvalidConnReq - APIPrepareContact userId accLink contactSLinkData verifiedDomain -> withUserId userId $ \user -> do + APIPrepareContact userId accLink verifiedDomain contactSLinkData -> withUserId userId $ \user -> do let ContactShortLinkData {profile, message, business} = contactSLinkData welcomeSharedMsgId <- forM message $ \_ -> getSharedMsgId case accLink of @@ -2096,7 +2096,7 @@ processChatCommand cxt nm = \case Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci} _ -> Chat cInfo [] emptyChatStats pure $ CRNewPreparedChat user $ AChat SCTDirect chat - APIPrepareGroup userId ccLink direct groupSLinkData verifiedDomain -> withUserId userId $ \user -> do + APIPrepareGroup userId ccLink direct verifiedDomain groupSLinkData -> withUserId userId $ \user -> do let GroupShortLinkData {groupProfile = GroupProfile {description}} = groupSLinkData welcomeSharedMsgId <- forM description $ \_ -> getSharedMsgId (gInfo, hostMember_) <- preparedGroupFromLink user ccLink direct groupSLinkData welcomeSharedMsgId (True <$ verifiedDomain) @@ -4340,7 +4340,7 @@ processChatCommand cxt nm = \case throwError e where prepareChannelGroup = - processChatCommand cxt nm (APIPrepareGroup userId ccl False gld vName) >>= \case + processChatCommand cxt nm (APIPrepareGroup userId ccl False vName gld) >>= \case CRNewPreparedChat _ (AChat SCTGroup (Chat (GroupChat gInfo _) _ _)) -> pure gInfo _ -> throwChatError $ CEException "joinChannelViaRelays: unexpected response from APIPrepareGroup" deletePreparedChannel groupId = do @@ -4349,7 +4349,7 @@ processChatCommand cxt nm = \case withFastStore' $ \db -> deleteGroup db user gInfo connectContactViaName :: ContactShortLinkData -> Maybe SimplexDomain -> CM ChatResponse connectContactViaName sld vName = - processChatCommand cxt nm (APIPrepareContact userId ccLink sld vName) >>= \case + processChatCommand cxt nm (APIPrepareContact userId ccLink vName sld) >>= \case CRNewPreparedChat _ (AChat SCTDirect (Chat (DirectChat Contact {contactId}) _ _)) -> processChatCommand cxt nm (APIConnectPreparedContact contactId incognito Nothing) _ -> throwChatError $ CEException "connectContactViaName: unexpected response from APIPrepareContact" @@ -5450,8 +5450,8 @@ chatCommandP = "/_contacts " *> (APIListContacts <$> A.decimal), "/contacts" $> ListContacts, "/_connect plan " *> (APIConnectPlan <$> A.decimal <* A.space <*> ((Just <$> strP) <|> A.takeTill (== ' ') $> Nothing) <*> ((" resolve=" *> onOffP) <|> pure False) <*> optional (" sig=" *> jsonP)), - "/_prepare contact " *> (APIPrepareContact <$> A.decimal <* A.space <*> connLinkP <* A.space <*> jsonP <*> optional (A.space *> strP)), - "/_prepare group " *> (APIPrepareGroup <$> A.decimal <* A.space <*> connLinkP' <*> (" direct=" *> onOffP <|> pure True) <* A.space <*> jsonP <*> optional (A.space *> strP)), + "/_prepare contact " *> (APIPrepareContact <$> A.decimal <* A.space <*> connLinkP <*> optional (" domain=" *> strP) <* A.space <*> jsonP), + "/_prepare group " *> (APIPrepareGroup <$> A.decimal <* A.space <*> connLinkP' <*> (" direct=" *> onOffP <|> pure True) <*> optional (" domain=" *> strP) <* A.space <*> jsonP), "/_set contact user @" *> (APIChangePreparedContactUser <$> A.decimal <* A.space <*> A.decimal), "/_set group user #" *> (APIChangePreparedGroupUser <$> A.decimal <* A.space <*> A.decimal), "/_connect contact @" *> (APIConnectPreparedContact <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)), diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt index a986773cb2..bfa23bc04e 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt @@ -1143,6 +1143,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=?)