diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift index 2158bc5e4e..3f736c8cb1 100644 --- a/apps/ios/Shared/Model/AppAPITypes.swift +++ b/apps/ios/Shared/Model/AppAPITypes.swift @@ -156,9 +156,9 @@ enum ChatCommand: ChatCmdProtocol { case apiAddMyAddressShortLink(userId: Int64) case apiSetProfileAddress(userId: Int64, on: Bool) case apiSetAddressSettings(userId: Int64, addressSettings: AddressSettings) - case apiSetUserName(userId: Int64, name: String?) - case apiVerifyContactName(contactId: Int64) - case apiVerifyPublicGroupName(groupId: Int64) + case apiSetUserDomain(userId: Int64, simplexDomain: String?) + case apiVerifyContactDomain(contactId: Int64) + case apiVerifyGroupDomain(groupId: Int64) case apiAcceptContact(incognito: Bool, contactReqId: Int64) case apiRejectContact(contactReqId: Int64) // WebRTC calls @@ -374,10 +374,10 @@ enum ChatCommand: ChatCmdProtocol { case let .apiAddMyAddressShortLink(userId): return "/_short_link_address \(userId)" case let .apiSetProfileAddress(userId, on): return "/_profile_address \(userId) \(onOff(on))" case let .apiSetAddressSettings(userId, addressSettings): return "/_address_settings \(userId) \(encodeJSON(addressSettings))" - case let .apiSetUserName(userId, name): return "/_set_name \(userId)" + (name.map { " " + $0 } ?? "") + case let .apiSetUserDomain(userId, simplexDomain): return "/_set domain \(userId)" + (simplexDomain.map { " " + $0 } ?? "") case let .apiSetPublicGroupAccess(groupId, access): return "/_public group access #\(groupId) \(encodeJSON(access))" - case let .apiVerifyContactName(contactId): return "/_verify name @\(contactId)" - case let .apiVerifyPublicGroupName(groupId): return "/_verify name #\(groupId)" + case let .apiVerifyContactDomain(contactId): return "/_verify domain @\(contactId)" + case let .apiVerifyGroupDomain(groupId): return "/_verify domain #\(groupId)" case let .apiAcceptContact(incognito, contactReqId): return "/_accept incognito=\(onOff(incognito)) \(contactReqId)" case let .apiRejectContact(contactReqId): return "/_reject \(contactReqId)" case let .apiSendCallInvitation(contact, callType): return "/_call invite @\(contact.apiId) \(encodeJSON(callType))" @@ -560,9 +560,9 @@ enum ChatCommand: ChatCmdProtocol { case .apiAddMyAddressShortLink: return "apiAddMyAddressShortLink" case .apiSetProfileAddress: return "apiSetProfileAddress" case .apiSetAddressSettings: return "apiSetAddressSettings" - case .apiSetUserName: return "apiSetUserName" - case .apiVerifyContactName: return "apiVerifyContactName" - case .apiVerifyPublicGroupName: return "apiVerifyPublicGroupName" + case .apiSetUserDomain: return "apiSetUserDomain" + case .apiVerifyContactDomain: return "apiVerifyContactDomain" + case .apiVerifyGroupDomain: return "apiVerifyGroupDomain" case .apiAcceptContact: return "apiAcceptContact" case .apiRejectContact: return "apiRejectContact" case .apiSendCallInvitation: return "apiSendCallInvitation" diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index bd4d417f97..7e02d34ab6 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -1083,17 +1083,17 @@ private func apiConnectResponseAlert(_ r: APIResult) -> Alert { title: "Unsupported connection link", message: "This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." ) - case let .error(.simplexName(name, err)): + case let .error(.simplexDomain(domain, err)): switch err { case .noValidLink: mkAlert( title: "No valid link", - message: "The SimpleX name \(name.shortName) is registered, but it has no valid link." + message: "The SimpleX name \(domain.fullDomainName) is registered, but it has no valid link." ) - case .unknownName: + case .unknownDomain: mkAlert( title: "Unconfirmed name", - message: "The SimpleX name \(name.shortName) is registered, but not added to profile. Please add it to your address or channel profile, if you are the owner." + message: "The SimpleX name \(domain.fullDomainName) is registered, but not added to profile. Please add it to your address or channel profile, if you are the owner." ) } case .errorAgent(.NO_NAME_SERVERS): @@ -1368,19 +1368,19 @@ func apiSetProfileAddress(on: Bool) async throws -> User? { // name is the encoded SimplexName (e.g. "@alice.simplex"); nil clears it // owner-specific SNENoValidLink wording; everything else reuses the general apiConnectResponseAlert func showSetSimplexNameError(_ r: APIResult, isChannel: Bool) { - if case let .error(.simplexName(name, .noValidLink)) = r.unexpected { + if case let .error(.simplexDomain(domain, .noValidLink)) = r.unexpected { let format = isChannel - ? NSLocalizedString("The SimpleX name %@ is registered without channel link. Add channel link to the name via the registration page.", comment: "alert message") - : NSLocalizedString("The SimpleX name %@ is registered without SimpleX address. Add your SimpleX address to the name via the registration page.", comment: "alert message") - showAlert(NSLocalizedString("Error saving name", comment: "alert title"), message: String.localizedStringWithFormat(format, name.shortName)) + ? NSLocalizedString("The SimpleX name #%@ is registered without channel link. Add channel link to the name via the registration page.", comment: "alert message") + : NSLocalizedString("The SimpleX name @%@ is registered without SimpleX address. Add your SimpleX address to the name via the registration page.", comment: "alert message") + showAlert(NSLocalizedString("Error saving name", comment: "alert title"), message: String.localizedStringWithFormat(format, domain.fullDomainName)) } else { AlertManager.shared.showAlert(apiConnectResponseAlert(r)) } } -func apiSetUserName(_ name: String?) async throws -> User { - let userId = try currentUserId("apiSetUserName") - let r: APIResult = await chatApiSendCmd(.apiSetUserName(userId: userId, name: name)) +func apiSetUserDomain(_ simplexDomain: String?) async throws -> User { + let userId = try currentUserId("apiSetUserDomain") + let r: APIResult = await chatApiSendCmd(.apiSetUserDomain(userId: userId, simplexDomain: simplexDomain)) switch r { case let .result(.userProfileUpdated(user, _, _, _)): return user case let .result(.userProfileNoChange(user)): return user @@ -1390,14 +1390,14 @@ func apiSetUserName(_ name: String?) async throws -> User { } } -func apiVerifyContactName(_ contactId: Int64) async throws -> (Contact, String?) { - let r: ChatResponse2 = try await chatSendCmd(.apiVerifyContactName(contactId: contactId)) +func apiVerifyContactDomain(_ contactId: Int64) async throws -> (Contact, String?) { + let r: ChatResponse2 = try await chatSendCmd(.apiVerifyContactDomain(contactId: contactId)) if case let .contactNameVerified(_, contact, verificationFailure) = r { return (contact, verificationFailure) } throw r.unexpected } -func apiVerifyPublicGroupName(_ groupId: Int64) async throws -> (GroupInfo, String?) { - let r: ChatResponse2 = try await chatSendCmd(.apiVerifyPublicGroupName(groupId: groupId)) +func apiVerifyGroupDomain(_ groupId: Int64) async throws -> (GroupInfo, String?) { + let r: ChatResponse2 = try await chatSendCmd(.apiVerifyGroupDomain(groupId: groupId)) if case let .groupNameVerified(_, groupInfo, verificationFailure) = r { return (groupInfo, verificationFailure) } throw r.unexpected } diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index ae9e0f4e13..9988b30d4e 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -392,21 +392,21 @@ struct ChatInfoView: View { .lineLimit(3) .padding(.bottom, 2) } - if let claim = contact.profile.simplexName, contact.profile.simplexNameVerification != nil || claim.proof != nil { + if let domain = contact.profile.contactDomain, + contact.profile.contactDomainVerified != nil || domain.proof != nil { SimplexNameView( - name: claim.shortName, - verification: contact.profile.simplexNameVerification, - autoVerify: UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES), + simplexName: "@\(domain.shortName)", + verified: contact.profile.contactDomainVerified, verify: { do { - let (ct, reason) = try await apiVerifyContactName(contact.contactId) + let (ct, reason) = try await apiVerifyContactDomain(contact.contactId) await MainActor.run { chatModel.updateContact(ct) contact = ct } - return (ct.profile.simplexNameVerification, reason) + return (ct.profile.contactDomainVerified, reason) } catch { - logger.error("apiVerifyContactName: \(responseError(error))") + logger.error("apiVerifyContactDomain: \(responseError(error))") return nil } } @@ -1382,31 +1382,31 @@ private func deleteNotReadyContact( } struct SimplexNameView: View { - let name: String - let verification: Bool? - let autoVerify: Bool - let verify: () async -> (Bool?, String?)? @EnvironmentObject var theme: AppTheme + @AppStorage(DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES) var autoVerify = false + let simplexName: String + let verified: Bool? + let verify: () async -> (Bool?, String?)? @State private var inFlight = false @State private var showSpinner = false var body: some View { HStack(spacing: 6) { - Text(name) - .font(verification == true ? .subheadline : .system(.subheadline, design: .monospaced)) - .foregroundColor(verification == true ? theme.colors.primary : theme.colors.secondary) + Text(simplexName) + .font(verified == true ? .subheadline : .system(.subheadline, design: .monospaced)) + .foregroundColor(verified == true ? theme.colors.primary : theme.colors.secondary) indicator() } .padding(.bottom, 2) - .onAppear { if autoVerify && verification == nil { runVerify(manual: false) } } + .onAppear { if autoVerify && verified == nil { runVerify(manual: false) } } } @ViewBuilder private func indicator() -> some View { if showSpinner { ProgressView() - } else if verification == true { + } else if verified == true { Image(systemName: "checkmark") - } else if verification == false { + } else if verified == false { Image(systemName: "xmark") .foregroundColor(.red) .onTapGesture { runVerify(manual: true) } diff --git a/apps/ios/Shared/Views/Chat/Group/ChannelWebAccessView.swift b/apps/ios/Shared/Views/Chat/Group/ChannelWebAccessView.swift index 247144da04..df0867c470 100644 --- a/apps/ios/Shared/Views/Chat/Group/ChannelWebAccessView.swift +++ b/apps/ios/Shared/Views/Chat/Group/ChannelWebAccessView.swift @@ -148,7 +148,7 @@ struct ChannelWebAccessView: View { let existingAccess = pg.publicGroupAccess pg.publicGroupAccess = PublicGroupAccess( groupWebPage: trimmedPage.isEmpty ? nil : trimmedPage, - simplexName: existingAccess?.simplexName, + groupDomainClaim: existingAccess?.groupDomainClaim, domainWebPage: existingAccess?.domainWebPage ?? false, allowEmbedding: allowEmbedding ) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index 251f0dd2d0..358bf6c7d3 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -112,6 +112,7 @@ struct GroupChatInfoView: View { // TODO [relays] allow other owners to manage channel link (requires protocol changes to share link ownership) if groupInfo.isOwner && groupLink != nil { channelLinkButton() + channelSimplexNameButton() } else if let link = groupInfo.groupProfile.publicGroup?.groupLink { SimpleXLinkQRCode(uri: link) Button { @@ -247,32 +248,6 @@ struct GroupChatInfoView: View { if groupInfo.useRelays && groupInfo.isOwner { Section(header: Text("Advanced options").foregroundColor(theme.colors.secondary)) { channelWebAccessButton() - if groupInfo.groupProfile.publicGroup != nil { - NavigationLink { - SetSimplexNameView( - titleKey: "SimpleX name", - footer: "Let people join via name registered with this channel link.", - prefix: "#", - nameText: groupInfo.groupProfile.publicGroup?.publicGroupAccess?.simplexName?.shortName ?? "", - save: { name in - do { - var access = groupInfo.groupProfile.publicGroup?.publicGroupAccess ?? PublicGroupAccess() - access.simplexName = name.map { SimplexNameClaim(name: $0) } - let gInfo = try await apiSetPublicGroupAccess(groupInfo.groupId, access: access) - await MainActor.run { - chatModel.updateGroup(gInfo) - groupInfo = gInfo - } - return true - } catch { - return false - } - } - ) - } label: { - Label("SimpleX name", systemImage: "number") - } - } } } @@ -358,22 +333,21 @@ struct GroupChatInfoView: View { .fixedSize(horizontal: false, vertical: true) } if let access = groupInfo.groupProfile.publicGroup?.publicGroupAccess, - let groupName = access.simplexName?.shortName, - groupInfo.simplexNameVerification != nil || access.simplexName?.proof != nil { + let domain = access.groupDomainClaim?.shortName, + groupInfo.groupDomainVerified != nil || access.groupDomainClaim?.proof != nil { SimplexNameView( - name: groupName, - verification: groupInfo.simplexNameVerification, - autoVerify: UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES), + simplexName: "#\(domain)", + verified: groupInfo.groupDomainVerified, verify: { do { - let (gInfo, reason) = try await apiVerifyPublicGroupName(groupInfo.groupId) + let (gInfo, reason) = try await apiVerifyGroupDomain(groupInfo.groupId) await MainActor.run { chatModel.updateGroup(gInfo) groupInfo = gInfo } - return (gInfo.simplexNameVerification, reason) + return (gInfo.groupDomainVerified, reason) } catch { - logger.error("apiVerifyPublicGroupName: \(responseError(error))") + logger.error("apiVerifyGroupDomain: \(responseError(error))") return nil } } @@ -722,6 +696,34 @@ struct GroupChatInfoView: View { } } + private func channelSimplexNameButton() -> some View { + NavigationLink { + let domain = if let d = groupInfo.groupProfile.publicGroup?.publicGroupAccess?.groupDomainClaim?.shortName { "#\(d)" } else { "" } + SetSimplexDomainView( + title: "SimpleX name", + footer: "Let people join via name registered with this channel link.", + prompt: "#channelname.testing", + simplexName: domain, + save: { domain in + do { + var access = groupInfo.groupProfile.publicGroup?.publicGroupAccess ?? PublicGroupAccess() + access.groupDomainClaim = domain.map { SimplexDomainClaim(domain: $0) } + let gInfo = try await apiSetPublicGroupAccess(groupInfo.groupId, access: access) + await MainActor.run { + chatModel.updateGroup(gInfo) + groupInfo = gInfo + } + return true + } catch { + return false + } + } + ) + } label: { + Label("SimpleX name", systemImage: "number") + } + } + private func groupLinkDestinationView() -> some View { GroupLinkView( groupId: groupInfo.groupId, diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift index 2b2bc8aae5..f1bacee425 100644 --- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift +++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift @@ -16,7 +16,7 @@ struct PrivacySettings: View { @AppStorage(GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS, store: groupDefaults) private var useLinkPreviews = true @AppStorage(GROUP_DEFAULT_PRIVACY_SANITIZE_LINKS, store: groupDefaults) private var privacySanitizeLinks = false @AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true - @AppStorage(DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES) private var verifySimplexNames = true + @AppStorage(DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES) private var verifySimplexNames = false @AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true @AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true @AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index d30415967b..057ce5a227 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -100,7 +100,7 @@ let appDefaults: [String: Any] = [ DEFAULT_PRIVACY_LINK_PREVIEWS: true, DEFAULT_PRIVACY_SIMPLEX_LINK_MODE: SimpleXLinkMode.description.rawValue, DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS: true, - DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES: true, + DEFAULT_PRIVACY_VERIFY_SIMPLEX_NAMES: false, DEFAULT_PRIVACY_SAVE_LAST_DRAFT: true, DEFAULT_PRIVACY_PROTECT_SCREEN: false, DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET: false, diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index 6342d16ac7..5d1bea6079 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -193,14 +193,15 @@ struct UserAddressView: View { Section { NavigationLink { - SetSimplexNameView( - titleKey: "Your SimpleX name", + let simplexName = if let d = chatModel.currentUser?.profile.contactDomain?.shortName { "@\(d)" } else { "" } + SetSimplexDomainView( + title: "Your SimpleX name", footer: "Let people connect to you via name registered with your SimpleX address.", - prefix: "@", - nameText: chatModel.currentUser?.profile.simplexName?.shortName ?? "", - save: { name in + prompt: "@yourname.testing", + simplexName: simplexName, + save: { simplexDomain in do { - let u = try await apiSetUserName(name) + let u = try await apiSetUserDomain(simplexDomain) await MainActor.run { chatModel.updateUser(u) } return true } catch { @@ -710,14 +711,11 @@ private func saveAddressSettings(_ settings: AddressSettingsState, _ savedSettin } } -// Set the user's own (prefix "@") or a channel's (prefix "#") SimpleX name. -// The field is prefilled with the full prefixed name; `save` receives the encoded name (or nil to -// clear) and returns a failure message (nil on success). The prefix is normalised on save. -struct SetSimplexNameView: View { - let titleKey: LocalizedStringKey +struct SetSimplexDomainView: View { + let title: LocalizedStringKey let footer: LocalizedStringKey - let prefix: String - @State var nameText: String + let prompt: String + @State var simplexName: String let save: (String?) async -> Bool @Environment(\.dismiss) var dismiss @EnvironmentObject var theme: AppTheme @@ -726,7 +724,7 @@ struct SetSimplexNameView: View { var body: some View { List { Section { - TextField(prefix + "name.simplex", text: $nameText) + TextField(prompt, text: $simplexName) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) } header: { @@ -750,16 +748,19 @@ struct SetSimplexNameView: View { .disabled(saving) } } - .navigationTitle(titleKey) + .navigationTitle(title) .navigationBarTitleDisplayMode(.large) } - // ensure the correct type prefix so a contact name is not parsed as a group (or vice versa) private func normalized() -> String? { - let s = nameText.trimmingCharacters(in: .whitespacesAndNewlines) - if s.isEmpty { return nil } - if s.hasPrefix("@") || s.hasPrefix("#") { return prefix + s.dropFirst() } - return prefix + s + let s = simplexName.trimmingCharacters(in: .whitespacesAndNewlines) + return s.isEmpty + ? nil + : addSimplexTLD(s.hasPrefix("@") || s.hasPrefix("#") ? String(s.dropFirst()) : s) + } + + private func addSimplexTLD(_ d: String) -> String { + if d.contains(".") { d } else { "\(d).simplex" } } } diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 87e042be82..70606ccc0a 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -183,8 +183,8 @@ 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z.a */; }; 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; }; @@ -563,8 +563,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z.a"; sourceTree = ""; }; 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; }; @@ -735,8 +735,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -822,8 +822,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-IDb07VxlHBtGmeucUQceZv.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.0.0.6-67j0xDFDAaz6bMQcxTdG4Z.a */, ); path = Libraries; sourceTree = ""; diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index c9a93c1e39..82576e433b 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -758,7 +758,7 @@ public enum ChatErrorType: Decodable, Hashable { case chatNotStopped case chatStoreChanged case invalidConnReq - case simplexName(simplexName: SimplexNameInfo, simplexNameError: SimplexNameError) + case simplexDomain(simplexDomain: SimplexDomain, simplexDomainError: SimplexDomainError) case unsupportedConnReq case invalidChatMessage(connection: Connection, message: String) case connReqMessageProhibited diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 7bdb664fad..fbdde8d8e7 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -119,7 +119,8 @@ public struct Profile: Codable, NamedChat, Hashable { image: String? = nil, contactLink: String? = nil, preferences: Preferences? = nil, - peerType: ChatPeerType? = nil + peerType: ChatPeerType? = nil, + contactDomain: SimplexDomainClaim? = nil ) { self.displayName = displayName self.fullName = fullName @@ -127,6 +128,7 @@ public struct Profile: Codable, NamedChat, Hashable { self.image = image self.contactLink = contactLink self.preferences = preferences + self.contactDomain = contactDomain } public var displayName: String @@ -139,6 +141,7 @@ public struct Profile: Codable, NamedChat, Hashable { // the badge proof from the wire profile - opaque to the UI, round-tripped to the core (apiPrepareContact) public var badge: BadgeProof? public var localAlias: String { get { "" } } + public var contactDomain: SimplexDomainClaim? var profileViewName: String { (fullName == "" || displayName == fullName) ? displayName : "\(displayName) (\(fullName))" @@ -162,8 +165,8 @@ public struct LocalProfile: Codable, NamedChat, Hashable { peerType: ChatPeerType? = nil, localBadge: LocalBadge? = nil, localAlias: String, - simplexName: SimplexNameClaim? = nil, - simplexNameVerification: Bool? = nil + contactDomain: SimplexDomainClaim? = nil, + contactDomainVerified: Bool? = nil ) { self.profileId = profileId self.displayName = displayName @@ -175,8 +178,8 @@ public struct LocalProfile: Codable, NamedChat, Hashable { self.peerType = peerType self.localBadge = localBadge self.localAlias = localAlias - self.simplexName = simplexName - self.simplexNameVerification = simplexNameVerification + self.contactDomain = contactDomain + self.contactDomainVerified = contactDomainVerified } public var profileId: Int64 @@ -189,8 +192,8 @@ public struct LocalProfile: Codable, NamedChat, Hashable { public var peerType: ChatPeerType? public var localBadge: LocalBadge? public var localAlias: String - public var simplexName: SimplexNameClaim? - public var simplexNameVerification: Bool? + public var contactDomain: SimplexDomainClaim? + public var contactDomainVerified: Bool? var profileViewName: String { localAlias == "" @@ -2540,7 +2543,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable { public var chatTags: [Int64] public var chatItemTTL: Int64? public var localAlias: String - public var simplexNameVerification: Bool? + public var groupDomainVerified: Bool? public var isOwner: Bool { return membership.memberRole == .owner && membership.memberCurrent @@ -2621,35 +2624,35 @@ public enum GroupType: Codable, Hashable { } public struct PublicGroupAccess: Codable, Hashable { - public init(groupWebPage: String? = nil, simplexName: SimplexNameClaim? = nil, domainWebPage: Bool = false, allowEmbedding: Bool = false) { + public init(groupWebPage: String? = nil, groupDomainClaim: SimplexDomainClaim? = nil, domainWebPage: Bool = false, allowEmbedding: Bool = false) { self.groupWebPage = groupWebPage - self.simplexName = simplexName + self.groupDomainClaim = groupDomainClaim self.domainWebPage = domainWebPage self.allowEmbedding = allowEmbedding } public var groupWebPage: String? - public var simplexName: SimplexNameClaim? + public var groupDomainClaim: SimplexDomainClaim? public var domainWebPage: Bool = false public var allowEmbedding: Bool = false } -public struct SimplexNameClaim: Codable, Hashable { - public init(name: String, proof: NameClaimProof? = nil) { - self.name = name +public struct SimplexDomainClaim: Codable, Hashable { + public init(domain: String, proof: SimplexDomainProof? = nil) { + self.domain = domain self.proof = proof } - public var name: String - public var proof: NameClaimProof? + public var domain: String + public var proof: SimplexDomainProof? public var shortName: String { - name.hasPrefix("simplex:/name") ? String(name.dropFirst("simplex:/name".count)) : name + domain.hasSuffix(".simplex") ? String(domain.dropLast(".simplex".count)) : domain } } -public enum SimplexNameError: Decodable, Hashable { +public enum SimplexDomainError: Decodable, Hashable { case noValidLink - case unknownName + case unknownDomain } public struct RelayCapabilities: Codable, Hashable { @@ -5285,28 +5288,15 @@ public enum SimplexLinkType: String, Decodable, Hashable { public struct SimplexNameInfo: Codable, Equatable, Hashable { public var nameType: SimplexNameType - public var nameDomain: SimplexNameDomain + public var nameDomain: SimplexDomain - // prefix-less domain for prefilling the set-name field (mirrors shortName without the @/# prefix) - public var editDomain: String { - if nameType == .publicGroup, nameDomain.nameTLD == .simplex, nameDomain.subDomain.isEmpty { - return nameDomain.domain - } - return nameDomain.fullDomainName - } - - // user-facing display string, mirrors backend shortNameInfoStr - public var shortName: String { - (nameType == .publicGroup ? "#" : "@") + editDomain - } - - public init(nameType: SimplexNameType, nameDomain: SimplexNameDomain) { + public init(nameType: SimplexNameType, nameDomain: SimplexDomain) { self.nameType = nameType self.nameDomain = nameDomain } } -public struct SimplexNameDomain: Codable, Equatable, Hashable { +public struct SimplexDomain: Codable, Equatable, Hashable { public var nameTLD: SimplexTLD public var domain: String public var subDomain: [String] @@ -5340,11 +5330,10 @@ public enum SimplexNameType: String, Codable, Hashable { case contact } -// peer's signed name claim; UI only checks presence -public struct NameClaimProof: Codable, Hashable { +public struct SimplexDomainProof: Codable, Hashable { + public var linkOwnerId: String? public var presHeader: String public var signature: String - public var linkOwnerId: String? } public enum FormatColor: String, Decodable, Hashable { 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 4e83a614b2..b6d408f223 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 @@ -2039,14 +2039,15 @@ data class Profile( val peerType: ChatPeerType? = null, // the badge proof from the wire profile: not interpreted by the UI (display uses crypto-free LocalBadge), // but preserved so passing a link profile back to the core (apiPrepareContact) keeps the proof - val badge: BadgeProof? = null + val badge: BadgeProof? = null, + val contactDomain: SimplexDomainClaim? = null ): NamedChat { val profileViewName: String get() { return if (fullName == "" || displayName == fullName) displayName else "$displayName ($fullName)" } - fun toLocalProfile(profileId: Long): LocalProfile = LocalProfile(profileId, displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType) + fun toLocalProfile(profileId: Long): LocalProfile = LocalProfile(profileId, displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType, contactDomain = contactDomain) companion object { val sampleData = Profile( @@ -2069,12 +2070,12 @@ data class LocalProfile( val preferences: ChatPreferences? = null, val peerType: ChatPeerType? = null, val localBadge: LocalBadge? = null, - val simplexName: SimplexNameClaim? = null, - val simplexNameVerification: Boolean? = null + val contactDomain: SimplexDomainClaim? = null, + val contactDomainVerified: Boolean? = null ): NamedChat { val profileViewName: String = localAlias.ifEmpty { if (fullName == "" || displayName == fullName) displayName else "$displayName ($fullName)" } - fun toProfile(): Profile = Profile(displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType) + fun toProfile(): Profile = Profile(displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType, contactDomain = contactDomain) companion object { val sampleData = LocalProfile( @@ -2200,7 +2201,7 @@ data class GroupInfo ( val chatTags: List, val chatItemTTL: Long?, override val localAlias: String, - val simplexNameVerification: Boolean? = null, + val groupDomainVerified: Boolean? = null, ): SomeChat, NamedChat { override val chatType get() = ChatType.Group override val id get() = "#$groupId" @@ -2323,17 +2324,17 @@ object GroupTypeSerializer : KSerializer { } @Serializable -data class SimplexNameClaim( - val name: String, - val proof: NameClaimProof? = null +data class SimplexDomainClaim( + val domain: String, + val proof: SimplexDomainProof? = null ) { - val shortName: String get() = name.removePrefix("simplex:/name") + val shortName: String get() = domain.removeSuffix(".simplex") } @Serializable data class PublicGroupAccess( val groupWebPage: String? = null, - val simplexName: SimplexNameClaim? = null, + val groupDomainClaim: SimplexDomainClaim? = null, val domainWebPage: Boolean = false, val allowEmbedding: Boolean = false ) @@ -4885,21 +4886,11 @@ enum class SimplexLinkType(val linkType: String) { @Serializable data class SimplexNameInfo( val nameType: SimplexNameType, - val nameDomain: SimplexNameDomain -) { - // prefix-less domain for prefilling the set-name field (shortName without the @/# prefix) - val editDomain: String - get() = if (nameType == SimplexNameType.publicGroup && nameDomain.nameTLD == SimplexTLD.simplex && nameDomain.subDomain.isEmpty()) - nameDomain.domain - else nameDomain.fullDomainName - - // user-facing display string, mirrors backend shortNameInfoStr - val shortName: String - get() = (if (nameType == SimplexNameType.publicGroup) "#" else "@") + editDomain -} + val nameDomain: SimplexDomain +) @Serializable -data class SimplexNameDomain( +data class SimplexDomain( val nameTLD: SimplexTLD, val domain: String, val subDomain: List @@ -4931,7 +4922,7 @@ enum class SimplexNameType { // peer's signed name claim; UI only checks presence @Serializable -data class NameClaimProof( +data class SimplexDomainProof( val presHeader: String, val signature: String, val linkOwnerId: String? = null 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 68b058b963..c035f974c7 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 @@ -122,7 +122,7 @@ class AppPreferences { val privacyProtectScreen = mkBoolPreference(SHARED_PREFS_PRIVACY_PROTECT_SCREEN, true) val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true) val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true) - val privacyVerifySimplexNames = mkBoolPreference(SHARED_PREFS_PRIVACY_VERIFY_SIMPLEX_NAMES, true) + val privacyVerifySimplexNames = mkBoolPreference(SHARED_PREFS_PRIVACY_VERIFY_SIMPLEX_NAMES, false) val privacyLinkPreviewsShowAlert = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS_SHOW_ALERT, true) val privacySanitizeLinks = mkBoolPreference(SHARED_PREFS_PRIVACY_SANITIZE_LINKS, false) // TODO remove @@ -1558,17 +1558,17 @@ object ChatController { ) } r is API.Error && r.err is ChatError.ChatErrorChat - && r.err.errorType is ChatErrorType.SimplexName -> { - val name = r.err.errorType.simplexName.shortName - if (r.err.errorType.simplexNameError is SimplexNameError.NoValidLink) { + && r.err.errorType is ChatErrorType.CESimplexDomain -> { + val domain = r.err.errorType.simplexDomain.fullDomainName + if (r.err.errorType.simplexDomainError is SimplexDomainError.NoValidLink) { AlertManager.shared.showAlertMsg( generalGetString(MR.strings.simplex_name_no_valid_link), - generalGetString(MR.strings.simplex_name_no_valid_link_desc).format(name) + generalGetString(MR.strings.simplex_name_no_valid_link_desc).format(domain) ) } else { AlertManager.shared.showAlertMsg( generalGetString(MR.strings.simplex_name_unconfirmed), - generalGetString(MR.strings.simplex_name_unconfirmed_desc).format(name) + generalGetString(MR.strings.simplex_name_unconfirmed_desc).format(domain) ) } } @@ -1631,10 +1631,10 @@ object ChatController { // owner-specific wording for setting one's own/channel name; null for other errors (handled by apiConnectResponseAlert) fun simplexNameOwnerError(err: ChatError, isChannel: Boolean): String? = - if (err is ChatError.ChatErrorChat && err.errorType is ChatErrorType.SimplexName && err.errorType.simplexNameError is SimplexNameError.NoValidLink) { - val name = err.errorType.simplexName.shortName - if (isChannel) generalGetString(MR.strings.simplex_name_owner_no_channel_link).format(name) - else generalGetString(MR.strings.simplex_name_owner_no_address).format(name) + if (err is ChatError.ChatErrorChat && err.errorType is ChatErrorType.CESimplexDomain && err.errorType.simplexDomainError is SimplexDomainError.NoValidLink) { + val domain = err.errorType.simplexDomain.fullDomainName + if (isChannel) generalGetString(MR.strings.simplex_name_owner_no_channel_link).format(domain) + else generalGetString(MR.strings.simplex_name_owner_no_address).format(domain) } else null fun connErrorText(e: ChatError): String = when { @@ -1642,8 +1642,8 @@ object ChatController { generalGetString(MR.strings.invalid_connection_link) e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.UnsupportedConnReq -> generalGetString(MR.strings.unsupported_connection_link) - e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.SimplexName -> - if (e.errorType.simplexNameError is SimplexNameError.NoValidLink) + e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.CESimplexDomain -> + if (e.errorType.simplexDomainError is SimplexDomainError.NoValidLink) generalGetString(MR.strings.simplex_name_no_valid_link) else generalGetString(MR.strings.simplex_name_unconfirmed) e is ChatError.ChatErrorAgent && e.agentError is AgentErrorType.NO_NAME_SERVERS -> @@ -1823,9 +1823,9 @@ object ChatController { } // name is the encoded SimplexName (e.g. "@alice.simplex"); null clears it. Throws on rejection. - suspend fun apiSetUserName(rh: Long?, name: String?): User { - val userId = currentUserId("apiSetUserName") - val r = sendCmd(rh, CC.ApiSetUserName(userId, name)) + suspend fun apiSetUserDomain(rh: Long?, simplexDomain: String?): User { + val userId = currentUserId("apiSetUserDomain") + val r = sendCmd(rh, CC.ApiSetUserDomain(userId, simplexDomain)) return when { r is API.Result && r.res is CR.UserProfileUpdated -> r.res.user.updateRemoteHostId(rh) r is API.Result && r.res is CR.UserProfileNoChange -> r.res.user.updateRemoteHostId(rh) @@ -1840,17 +1840,17 @@ object ChatController { } } - suspend fun apiVerifyContactName(rh: Long?, contactId: Long): Pair? { - val r = sendCmd(rh, CC.ApiVerifyContactName(contactId)) + suspend fun apiVerifyContactDomain(rh: Long?, contactId: Long): Pair? { + val r = sendCmd(rh, CC.ApiVerifyContactDomain(contactId)) if (r is API.Result && r.res is CR.ContactNameVerified) return r.res.contact to r.res.verificationFailure - Log.e(TAG, "apiVerifyContactName bad response: ${r.responseType} ${r.details}") + Log.e(TAG, "apiVerifyContactDomain bad response: ${r.responseType} ${r.details}") return null } - suspend fun apiVerifyPublicGroupName(rh: Long?, groupId: Long): Pair? { - val r = sendCmd(rh, CC.ApiVerifyPublicGroupName(groupId)) + suspend fun apiVerifyGroupDomain(rh: Long?, groupId: Long): Pair? { + val r = sendCmd(rh, CC.ApiVerifyGroupDomain(groupId)) if (r is API.Result && r.res is CR.GroupNameVerified) return r.res.groupInfo to r.res.verificationFailure - Log.e(TAG, "apiVerifyPublicGroupName bad response: ${r.responseType} ${r.details}") + Log.e(TAG, "apiVerifyGroupDomain bad response: ${r.responseType} ${r.details}") return null } @@ -3885,9 +3885,9 @@ sealed class CC { class ApiShowMyAddress(val userId: Long): CC() class ApiAddMyAddressShortLink(val userId: Long): CC() class ApiSetProfileAddress(val userId: Long, val on: Boolean): CC() - class ApiSetUserName(val userId: Long, val name: String?): CC() - class ApiVerifyContactName(val contactId: Long): CC() - class ApiVerifyPublicGroupName(val groupId: Long): CC() + class ApiSetUserDomain(val userId: Long, val simplexDomain: String?): CC() + class ApiVerifyContactDomain(val contactId: Long): CC() + class ApiVerifyGroupDomain(val groupId: Long): CC() class ApiSetAddressSettings(val userId: Long, val addressSettings: AddressSettings): CC() class ApiGetCallInvitations: CC() class ApiSendCallInvitation(val contact: Contact, val callType: CallType): CC() @@ -4096,10 +4096,10 @@ sealed class CC { is ApiShowMyAddress -> "/_show_address $userId" is ApiAddMyAddressShortLink -> "/_short_link_address $userId" is ApiSetProfileAddress -> "/_profile_address $userId ${onOff(on)}" - is ApiSetUserName -> "/_set_name $userId" + (if (name != null) " $name" else "") + is ApiSetUserDomain -> "/_set domain $userId" + (if (simplexDomain != null) " $simplexDomain" else "") is ApiSetPublicGroupAccess -> "/_public group access #$groupId ${json.encodeToString(access)}" - is ApiVerifyContactName -> "/_verify name @$contactId" - is ApiVerifyPublicGroupName -> "/_verify name #$groupId" + is ApiVerifyContactDomain -> "/_verify domain @$contactId" + is ApiVerifyGroupDomain -> "/_verify domain #$groupId" is ApiSetAddressSettings -> "/_address_settings $userId ${json.encodeToString(addressSettings)}" is ApiAcceptContact -> "/_accept incognito=${onOff(incognito)} $contactReqId" is ApiRejectContact -> "/_reject $contactReqId" @@ -4281,10 +4281,10 @@ sealed class CC { is ApiShowMyAddress -> "apiShowMyAddress" is ApiAddMyAddressShortLink -> "apiAddMyAddressShortLink" is ApiSetProfileAddress -> "apiSetProfileAddress" - is ApiSetUserName -> "apiSetUserName" + is ApiSetUserDomain -> "apiSetUserDomain" is ApiSetPublicGroupAccess -> "apiSetPublicGroupAccess" - is ApiVerifyContactName -> "apiVerifyContactName" - is ApiVerifyPublicGroupName -> "apiVerifyPublicGroupName" + is ApiVerifyContactDomain -> "apiVerifyContactDomain" + is ApiVerifyGroupDomain -> "apiVerifyGroupDomain" is ApiSetAddressSettings -> "apiSetAddressSettings" is ApiAcceptContact -> "apiAcceptContact" is ApiRejectContact -> "apiRejectContact" @@ -7096,9 +7096,9 @@ sealed class OwnerVerification { } @Serializable -sealed class SimplexNameError { - @Serializable @SerialName("noValidLink") object NoValidLink : SimplexNameError() - @Serializable @SerialName("unknownName") object UnknownName : SimplexNameError() +sealed class SimplexDomainError { + @Serializable @SerialName("noValidLink") object NoValidLink : SimplexDomainError() + @Serializable @SerialName("unknownDomain") object UnknownDomain : SimplexDomainError() } @Serializable @@ -7438,7 +7438,7 @@ sealed class ChatErrorType { is ChatStoreChanged -> "chatStoreChanged" is ConnectionPlanChatError -> "connectionPlan" is InvalidConnReq -> "invalidConnReq" - is SimplexName -> "simplexName" + is CESimplexDomain -> "simplexDomain" is UnsupportedConnReq -> "unsupportedConnReq" is InvalidChatMessage -> "invalidChatMessage" is ConnReqMessageProhibited -> "connReqMessageProhibited" @@ -7521,7 +7521,7 @@ sealed class ChatErrorType { @Serializable @SerialName("chatStoreChanged") object ChatStoreChanged: ChatErrorType() @Serializable @SerialName("connectionPlan") class ConnectionPlanChatError(val connectionPlan: ConnectionPlan): ChatErrorType() @Serializable @SerialName("invalidConnReq") object InvalidConnReq: ChatErrorType() - @Serializable @SerialName("simplexName") class SimplexName(val simplexName: SimplexNameInfo, val simplexNameError: SimplexNameError): ChatErrorType() + @Serializable @SerialName("simplexDomain") class CESimplexDomain(val simplexDomain: SimplexDomain, val simplexDomainError: SimplexDomainError): ChatErrorType() @Serializable @SerialName("unsupportedConnReq") object UnsupportedConnReq: ChatErrorType() @Serializable @SerialName("invalidChatMessage") class InvalidChatMessage(val connection: Connection, val message: String): ChatErrorType() @Serializable @SerialName("connReqMessageProhibited") object ConnReqMessageProhibited: ChatErrorType() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt index 31bc083ad1..e43cc357a0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt @@ -757,17 +757,16 @@ fun ChatInfoHeader(cInfo: ChatInfo, contact: Contact) { modifier = Modifier.combinedClickable(onClick = copyDisplayName, onLongClick = copyDisplayName).onRightClick(copyDisplayName) ) ChatInfoDescription(cInfo, displayName, copyNameToClipboard) - val simplexName = contact.profile.simplexName - if (simplexName != null && (contact.profile.simplexNameVerification != null || simplexName.proof != null)) { + val domain = contact.profile.contactDomain + if (domain != null && (contact.profile.contactDomainVerified != null || domain.proof != null)) { SimplexNameView( - name = simplexName.shortName, - verification = contact.profile.simplexNameVerification, - autoVerify = chatModel.controller.appPrefs.privacyVerifySimplexNames.get(), + simplexName = "@${domain.shortName}", + verified = contact.profile.contactDomainVerified, verify = { val rhId = chatModel.remoteHostId() - chatModel.controller.apiVerifyContactName(rhId, contact.contactId)?.let { (ct, reason) -> + chatModel.controller.apiVerifyContactDomain(rhId, contact.contactId)?.let { (ct, reason) -> chatModel.chatsContext.updateContact(rhId, ct) - ct.profile.simplexNameVerification to reason + ct.profile.contactDomainVerified to reason } } ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SimplexNameView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SimplexNameView.kt index 07e003f93d..ccee6a9035 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SimplexNameView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SimplexNameView.kt @@ -24,9 +24,8 @@ import kotlinx.coroutines.* // null on network error. With `autoVerify`, it runs once on open when state is null. @Composable fun SimplexNameView( - name: String, - verification: Boolean?, - autoVerify: Boolean, + simplexName: String, + verified: Boolean?, verify: suspend () -> Pair? ) { val scope = rememberCoroutineScope() @@ -59,7 +58,7 @@ fun SimplexNameView( } LaunchedEffect(Unit) { - if (autoVerify && verification == null) runVerify(manual = false) + if (chatModel.controller.appPrefs.privacyVerifySimplexNames.get() && verified == null) runVerify(manual = false) } Row( @@ -68,18 +67,18 @@ fun SimplexNameView( modifier = Modifier.padding(top = DEFAULT_PADDING_HALF) ) { Text( - name, + simplexName, style = MaterialTheme.typography.body2.copy( - color = if (verification == true) MaterialTheme.colors.primary else MaterialTheme.colors.secondary, - fontFamily = if (verification == true) FontFamily.Default else FontFamily.Monospace + color = if (verified == true) MaterialTheme.colors.primary else MaterialTheme.colors.secondary, + fontFamily = if (verified == true) FontFamily.Default else FontFamily.Monospace ) ) when { showSpinner.value -> CircularProgressIndicator(Modifier.size(16.dp), strokeWidth = 2.dp, color = MaterialTheme.colors.secondary) - verification == true -> + verified == true -> Icon(painterResource(MR.images.ic_check_filled), null, Modifier.size(18.dp), tint = MaterialTheme.colors.onBackground) - verification == false -> + verified == false -> Icon( painterResource(MR.images.ic_close), null, tint = Color.Red, modifier = Modifier.size(18.dp).clickable { runVerify(manual = true) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelWebPageView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelWebPageView.kt index 30595b8c5c..18a944f671 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelWebPageView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelWebPageView.kt @@ -49,7 +49,7 @@ fun ChannelWebPageView( val trimmedPage = webPage.value.trim() val newAccess = PublicGroupAccess( groupWebPage = trimmedPage.ifEmpty { null }, - simplexName = access?.simplexName, + groupDomainClaim = access?.groupDomainClaim, domainWebPage = access?.domainWebPage ?: false, allowEmbedding = allowEmbedding.value ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index 4db41d61be..c54ca148d4 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -180,14 +180,15 @@ fun ModalData.GroupChatInfoView( }, setSimplexName = { ModalManager.end.showCustomModal { close -> - SetSimplexNameView( + val domain = groupInfo.groupProfile.publicGroup?.publicGroupAccess?.groupDomainClaim?.shortName + SetSimplexDomainView( title = generalGetString(MR.strings.set_simplex_name), footer = generalGetString(MR.strings.set_channel_simplex_name_footer), - prefix = "#", - initial = groupInfo.groupProfile.publicGroup?.publicGroupAccess?.simplexName?.shortName ?: "", - save = { name -> + placeholder = "#channelname.testing", + simplexName = if (domain == null) "" else "#$domain", + save = { domain -> val access = groupInfo.groupProfile.publicGroup?.publicGroupAccess ?: PublicGroupAccess() - val newAccess = access.copy(simplexName = name?.let { SimplexNameClaim(it) }) + val newAccess = access.copy(groupDomainClaim = domain?.let { SimplexDomainClaim(it) }) val gInfo = chatModel.controller.apiSetPublicGroupAccess(rhId, groupInfo.groupId, newAccess) if (gInfo != null) { withContext(Dispatchers.Main) { chatModel.chatsContext.updateGroup(rhId, gInfo) } @@ -637,6 +638,12 @@ fun ModalData.GroupChatInfoLayout( if (groupInfo.isOwner && groupLink != null) { anyTopSectionRowShow = true ChannelLinkButton(manageGroupLink) + SettingsActionItem( + painterResource(MR.images.ic_tag), + stringResource(MR.strings.simplex_name), + setSimplexName, + iconColor = MaterialTheme.colors.secondary + ) } else if (channelLink != null) { anyTopSectionRowShow = true ChannelLinkQRCodeSection(channelLink) @@ -825,14 +832,6 @@ fun ModalData.GroupChatInfoLayout( SectionDividerSpaced() SectionView(title = stringResource(MR.strings.advanced_options)) { ChannelWebPageButton(groupInfo, manageWebPage) - if (groupInfo.groupProfile.publicGroup != null) { - SettingsActionItem( - painterResource(MR.images.ic_tag), - stringResource(MR.strings.simplex_name), - setSimplexName, - iconColor = MaterialTheme.colors.secondary - ) - } } } @@ -975,17 +974,16 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) { ) ChatInfoDescription(cInfo, displayName, copyNameToClipboard) val access = groupInfo.groupProfile.publicGroup?.publicGroupAccess - val groupName = access?.simplexName?.shortName - if (groupName != null && (groupInfo.simplexNameVerification != null || access.simplexName?.proof != null)) { + val domain = access?.groupDomainClaim?.shortName + if (domain != null && (groupInfo.groupDomainVerified != null || access.groupDomainClaim?.proof != null)) { SimplexNameView( - name = groupName, - verification = groupInfo.simplexNameVerification, - autoVerify = chatModel.controller.appPrefs.privacyVerifySimplexNames.get(), + simplexName = "#${domain}", + verified = groupInfo.groupDomainVerified, verify = { val rhId = chatModel.remoteHostId() - chatModel.controller.apiVerifyPublicGroupName(rhId, groupInfo.groupId)?.let { (gInfo, reason) -> + chatModel.controller.apiVerifyGroupDomain(rhId, groupInfo.groupId)?.let { (gInfo, reason) -> chatModel.chatsContext.updateGroup(rhId, gInfo) - gInfo.simplexNameVerification to reason + gInfo.groupDomainVerified to reason } } ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt index b2f157644c..c4cf0d1714 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt @@ -19,24 +19,28 @@ import kotlinx.coroutines.* // The field is prefilled with the full prefixed name; `save` receives the encoded name (or null to // clear) and returns true on success (it shows its own error alert otherwise). @Composable -fun SetSimplexNameView( +fun SetSimplexDomainView( title: String, footer: String, - prefix: String, - initial: String, + placeholder: String, + simplexName: String, save: suspend (String?) -> Boolean, close: () -> Unit ) { - val name = rememberSaveable { mutableStateOf(initial) } + val name = rememberSaveable { mutableStateOf(simplexName) } val saving = remember { mutableStateOf(false) } - val unchanged = name.value.trim() == initial.trim() + val unchanged = name.value.trim() == simplexName.trim() + + fun addSimplexTLD(s: String): String { + return if (s.contains(".")) s else "$s.simplex" + } fun normalized(): String? { val s = name.value.trim() return when { s.isEmpty() -> null - s.startsWith("@") || s.startsWith("#") -> prefix + s.substring(1) - else -> prefix + s + s.startsWith("@") || s.startsWith("#") -> addSimplexTLD(s.substring(1)) + else -> addSimplexTLD(s) } } @@ -44,7 +48,7 @@ fun SetSimplexNameView( withBGApi { saving.value = true val ok = try { save(normalized()) } catch (e: Exception) { - Log.e(TAG, "SetSimplexNameView save: ${e.stackTraceToString()}") + Log.e(TAG, "SetSimplexDomainView save: ${e.stackTraceToString()}") AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_saving_simplex_name), e.message ?: "") false } @@ -57,7 +61,7 @@ fun SetSimplexNameView( ColumnWithScrollBar { AppBarTitle(title) SectionView { - PlainTextEditor(name, placeholder = prefix + stringResource(MR.strings.simplex_name_placeholder)) + PlainTextEditor(name, placeholder) } SectionTextFooter(footer) SectionDividerSpaced() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt index 7088a82600..9d0efad152 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt @@ -369,18 +369,19 @@ private fun UserAddressLayout( stringResource(MR.strings.your_simplex_name), click = { ModalManager.start.showCustomModal { close -> - SetSimplexNameView( + val domain = user?.profile?.contactDomain?.shortName + SetSimplexDomainView( title = generalGetString(MR.strings.set_simplex_name), footer = generalGetString(MR.strings.set_user_simplex_name_footer), - prefix = "@", - initial = user?.profile?.simplexName?.shortName ?: "", - save = { name -> + placeholder = "@yourname.testing", + simplexName = if (domain == null) "" else "@$domain", + save = { simplexDomain -> try { - val u = chatModel.controller.apiSetUserName(user?.remoteHostId, name) + val u = chatModel.controller.apiSetUserDomain(user?.remoteHostId, simplexDomain) withContext(Dispatchers.Main) { chatModel.updateUser(u) } true } catch (e: Exception) { - Log.e(TAG, "apiSetUserName: ${e.message}") + Log.e(TAG, "apiSetUserDomain: ${e.message}") false } }, diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 2f85d4a847..96c7226c36 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -947,7 +947,6 @@ SimpleX name Your SimpleX name Set SimpleX name - name.simplex Error saving name The SimpleX name %1$s is registered without channel link. Add channel link to the name via the registration page. The SimpleX name %1$s is registered without SimpleX address. Add your SimpleX address to the name via the registration page. diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index 00c7e24575..32235ed7fb 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -139,7 +139,6 @@ This file is generated automatically. - [MsgReaction](#msgreaction) - [MsgReceiptStatus](#msgreceiptstatus) - [MsgSigStatus](#msgsigstatus) -- [NameClaimProof](#nameclaimproof) - [NameErrorType](#nameerrortype) - [NetworkError](#networkerror) - [NewUser](#newuser) @@ -174,10 +173,11 @@ This file is generated automatically. - [SMPAgentError](#smpagenterror) - [SecurityCode](#securitycode) - [SimplePreference](#simplepreference) +- [SimplexDomain](#simplexdomain) +- [SimplexDomainClaim](#simplexdomainclaim) +- [SimplexDomainError](#simplexdomainerror) +- [SimplexDomainProof](#simplexdomainproof) - [SimplexLinkType](#simplexlinktype) -- [SimplexNameClaim](#simplexnameclaim) -- [SimplexNameDomain](#simplexnamedomain) -- [SimplexNameError](#simplexnameerror) - [SimplexNameInfo](#simplexnameinfo) - [SimplexNameType](#simplexnametype) - [SimplexTLD](#simplextld) @@ -1111,10 +1111,10 @@ ChatStoreChanged: InvalidConnReq: - type: "invalidConnReq" -SimplexName: -- type: "simplexName" -- simplexName: [SimplexNameInfo](#simplexnameinfo) -- simplexNameError: [SimplexNameError](#simplexnameerror) +SimplexDomain: +- type: "simplexDomain" +- simplexDomain: [SimplexDomain](#simplexdomain) +- simplexDomainError: [SimplexDomainError](#simplexdomainerror) UnsupportedConnReq: - type: "unsupportedConnReq" @@ -2347,7 +2347,7 @@ MemberSupport: - membersRequireAttention: int - viaGroupLinkUri: string? - groupKeys: [GroupKeys](#groupkeys)? -- simplexNameVerification: bool? +- groupDomainVerified: bool? --- @@ -2772,8 +2772,8 @@ Unknown: - peerType: [ChatPeerType](#chatpeertype)? - localBadge: [LocalBadge](#localbadge)? - localAlias: string -- simplexName: [SimplexNameClaim](#simplexnameclaim)? -- simplexNameVerification: bool? +- contactDomain: [SimplexDomainClaim](#simplexdomainclaim)? +- contactDomainVerified: bool? --- @@ -2949,16 +2949,6 @@ Unknown: - "signedNoKey" ---- - -## NameClaimProof - -**Record type**: -- linkOwnerId: string? -- presHeader: string -- signature: string - - --- ## NameErrorType @@ -3146,7 +3136,7 @@ count= - preferences: [Preferences](#preferences)? - peerType: [ChatPeerType](#chatpeertype)? - badge: [BadgeProof](#badgeproof)? -- simplexName: [SimplexNameClaim](#simplexnameclaim)? +- contactDomain: [SimplexDomainClaim](#simplexdomainclaim)? --- @@ -3195,7 +3185,7 @@ NO_SESSION: **Record type**: - groupWebPage: string? -- simplexName: [SimplexNameClaim](#simplexnameclaim)? +- groupDomainClaim: [SimplexDomainClaim](#simplexdomainclaim)? - domainWebPage: bool - allowEmbedding: bool @@ -3579,6 +3569,48 @@ A_QUEUE: - allow: [FeatureAllowed](#featureallowed) +--- + +## SimplexDomain + +**Record type**: +- nameTLD: [SimplexTLD](#simplextld) +- domain: string +- subDomain: [string] + + +--- + +## SimplexDomainClaim + +**Record type**: +- domain: string +- proof: [SimplexDomainProof](#simplexdomainproof)? + + +--- + +## SimplexDomainError + +**Discriminated union type**: + +NoValidLink: +- type: "noValidLink" + +UnknownDomain: +- type: "unknownDomain" + + +--- + +## SimplexDomainProof + +**Record type**: +- linkOwnerId: string? +- presHeader: string +- signature: string + + --- ## SimplexLinkType @@ -3591,45 +3623,13 @@ A_QUEUE: - "relay" ---- - -## SimplexNameClaim - -**Record type**: -- name: string -- proof: [NameClaimProof](#nameclaimproof)? - - ---- - -## SimplexNameDomain - -**Record type**: -- nameTLD: [SimplexTLD](#simplextld) -- domain: string -- subDomain: [string] - - ---- - -## SimplexNameError - -**Discriminated union type**: - -NoValidLink: -- type: "noValidLink" - -UnknownName: -- type: "unknownName" - - --- ## SimplexNameInfo **Record type**: - nameType: [SimplexNameType](#simplexnametype) -- nameDomain: [SimplexNameDomain](#simplexnamedomain) +- nameDomain: [SimplexDomain](#simplexdomain) --- diff --git a/bots/src/API/Docs/Commands.hs b/bots/src/API/Docs/Commands.hs index 462453b77d..ddfc817fa2 100644 --- a/bots/src/API/Docs/Commands.hs +++ b/bots/src/API/Docs/Commands.hs @@ -414,7 +414,7 @@ undocumentedCommands = "APISetServerOperators", "APISetUserContactReceipts", "APISetUserGroupReceipts", - "APISetUserName", + "APISetUserDomain", "APISetUserServers", "APISetUserUIThemes", "APIShareChatMsgContent", @@ -434,9 +434,9 @@ undocumentedCommands = "APIUserRead", "APIValidateServers", "APIVerifyContact", - "APIVerifyContactName", + "APIVerifyContactDomain", "APIVerifyGroupMember", - "APIVerifyPublicGroupName", + "APIVerifyGroupDomain", "APIVerifyToken", "CheckChatRunning", "ConfirmRemoteCtrl", diff --git a/bots/src/API/Docs/Types.hs b/bots/src/API/Docs/Types.hs index eba5454181..b28c71d8fd 100644 --- a/bots/src/API/Docs/Types.hs +++ b/bots/src/API/Docs/Types.hs @@ -322,7 +322,6 @@ chatTypesDocsData = (sti @MsgReaction, STUnion, "MR", [], "", ""), (sti @MsgReceiptStatus, STEnum, "MR", [], "", ""), (sti @MsgSigStatus, STEnum, "MSS", [], "", ""), - (sti @NameClaimProof, STRecord, "", [], "", ""), (sti @NameErrorType, STUnion, "", [], "", ""), (sti @NetworkError, STUnion, "NE", [], "", ""), (sti @NewUser, STRecord, "", [], "", ""), @@ -356,10 +355,11 @@ chatTypesDocsData = (sti @RoleGroupPreference, STRecord, "", [], "", ""), (sti @SecurityCode, STRecord, "", [], "", ""), (sti @SimplePreference, STRecord, "", [], "", ""), + (sti @SimplexDomain, STRecord, "", [], "", ""), + (sti @SimplexDomainClaim, STRecord, "", [], "", ""), + (sti @SimplexDomainError, STUnion, "SDE", [], "", ""), + (sti @SimplexDomainProof, STRecord, "", [], "", ""), (sti @SimplexLinkType, STEnum, "XL", [], "", ""), - (sti @SimplexNameClaim, STRecord, "", [], "", ""), - (sti @SimplexNameDomain, STRecord, "", [], "", ""), - (sti @SimplexNameError, STUnion, "SNE", [], "", ""), (sti @SimplexNameInfo, STRecord, "", [], "", ""), (sti @SimplexNameType, STEnum, "NT", [], "", ""), (sti @SimplexTLD, STEnum, "TLD", [], "", ""), @@ -553,7 +553,6 @@ deriving instance Generic MsgFilter deriving instance Generic MsgReaction deriving instance Generic MsgReceiptStatus deriving instance Generic MsgSigStatus -deriving instance Generic NameClaimProof deriving instance Generic NameErrorType deriving instance Generic NetworkError deriving instance Generic NewUser @@ -585,10 +584,11 @@ deriving instance Generic RelayProfile deriving instance Generic RelayStatus deriving instance Generic ReportReason deriving instance Generic SecurityCode +deriving instance Generic SimplexDomain +deriving instance Generic SimplexDomainClaim +deriving instance Generic SimplexDomainError +deriving instance Generic SimplexDomainProof deriving instance Generic SimplexLinkType -deriving instance Generic SimplexNameClaim -deriving instance Generic SimplexNameDomain -deriving instance Generic SimplexNameError deriving instance Generic SimplexNameInfo deriving instance Generic SimplexNameType deriving instance Generic SimplexTLD diff --git a/cabal.project b/cabal.project index 96ece95906..bd8e4ac384 100644 --- a/cabal.project +++ b/cabal.project @@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 93925b257c76ee95d9b9ea3dcf433fecdb514c9c + tag: 836254a4c6bd5e17b40acbcaf77a83802d44919e source-repository-package type: git diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index 26aa714947..5b90ccab0a 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -1042,7 +1042,7 @@ export type ChatErrorType = | ChatErrorType.ChatNotStopped | ChatErrorType.ChatStoreChanged | ChatErrorType.InvalidConnReq - | ChatErrorType.SimplexName + | ChatErrorType.SimplexDomain | ChatErrorType.UnsupportedConnReq | ChatErrorType.ConnReqMessageProhibited | ChatErrorType.ContactNotReady @@ -1120,7 +1120,7 @@ export namespace ChatErrorType { | "chatNotStopped" | "chatStoreChanged" | "invalidConnReq" - | "simplexName" + | "simplexDomain" | "unsupportedConnReq" | "connReqMessageProhibited" | "contactNotReady" @@ -1275,10 +1275,10 @@ export namespace ChatErrorType { type: "invalidConnReq" } - export interface SimplexName extends Interface { - type: "simplexName" - simplexName: SimplexNameInfo - simplexNameError: SimplexNameError + export interface SimplexDomain extends Interface { + type: "simplexDomain" + simplexDomain: SimplexDomain + simplexDomainError: SimplexDomainError } export interface UnsupportedConnReq extends Interface { @@ -2630,7 +2630,7 @@ export interface GroupInfo { membersRequireAttention: number // int viaGroupLinkUri?: string groupKeys?: GroupKeys - simplexNameVerification?: boolean + groupDomainVerified?: boolean } export interface GroupKeys { @@ -3008,8 +3008,8 @@ export interface LocalProfile { peerType?: ChatPeerType localBadge?: LocalBadge localAlias: string - simplexName?: SimplexNameClaim - simplexNameVerification?: boolean + contactDomain?: SimplexDomainClaim + contactDomainVerified?: boolean } export enum MemberCriteria { @@ -3203,12 +3203,6 @@ export enum MsgSigStatus { SignedNoKey = "signedNoKey", } -export interface NameClaimProof { - linkOwnerId?: string - presHeader: string - signature: string -} - export type NameErrorType = NameErrorType.NO_RESOLVER | NameErrorType.NOT_FOUND | NameErrorType.RESOLVER export namespace NameErrorType { @@ -3390,7 +3384,7 @@ export interface Profile { preferences?: Preferences peerType?: ChatPeerType badge?: BadgeProof - simplexName?: SimplexNameClaim + contactDomain?: SimplexDomainClaim } export type ProxyClientError = @@ -3451,7 +3445,7 @@ export namespace ProxyError { export interface PublicGroupAccess { groupWebPage?: string - simplexName?: SimplexNameClaim + groupDomainClaim?: SimplexDomainClaim domainWebPage: boolean allowEmbedding: boolean } @@ -3954,29 +3948,21 @@ export interface SimplePreference { allow: FeatureAllowed } -export enum SimplexLinkType { - Contact = "contact", - Invitation = "invitation", - Group = "group", - Channel = "channel", - Relay = "relay", -} - -export interface SimplexNameClaim { - name: string - proof?: NameClaimProof -} - -export interface SimplexNameDomain { +export interface SimplexDomain { nameTLD: SimplexTLD domain: string subDomain: string[] } -export type SimplexNameError = SimplexNameError.NoValidLink | SimplexNameError.UnknownName +export interface SimplexDomainClaim { + domain: string + proof?: SimplexDomainProof +} -export namespace SimplexNameError { - export type Tag = "noValidLink" | "unknownName" +export type SimplexDomainError = SimplexDomainError.NoValidLink | SimplexDomainError.UnknownDomain + +export namespace SimplexDomainError { + export type Tag = "noValidLink" | "unknownDomain" interface Interface { type: Tag @@ -3986,14 +3972,28 @@ export namespace SimplexNameError { type: "noValidLink" } - export interface UnknownName extends Interface { - type: "unknownName" + export interface UnknownDomain extends Interface { + type: "unknownDomain" } } +export interface SimplexDomainProof { + linkOwnerId?: string + presHeader: string + signature: string +} + +export enum SimplexLinkType { + Contact = "contact", + Invitation = "invitation", + Group = "group", + Channel = "channel", + Relay = "relay", +} + export interface SimplexNameInfo { nameType: SimplexNameType - nameDomain: SimplexNameDomain + nameDomain: SimplexDomain } export enum SimplexNameType { diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index 20b18ccfc4..59787fd3c3 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -788,10 +788,10 @@ class ChatErrorType_chatStoreChanged(TypedDict): class ChatErrorType_invalidConnReq(TypedDict): type: Literal["invalidConnReq"] -class ChatErrorType_simplexName(TypedDict): - type: Literal["simplexName"] - simplexName: "SimplexNameInfo" - simplexNameError: "SimplexNameError" +class ChatErrorType_simplexDomain(TypedDict): + type: Literal["simplexDomain"] + simplexDomain: "SimplexDomain" + simplexDomainError: "SimplexDomainError" class ChatErrorType_unsupportedConnReq(TypedDict): type: Literal["unsupportedConnReq"] @@ -1023,7 +1023,7 @@ ChatErrorType = ( | ChatErrorType_chatNotStopped | ChatErrorType_chatStoreChanged | ChatErrorType_invalidConnReq - | ChatErrorType_simplexName + | ChatErrorType_simplexDomain | ChatErrorType_unsupportedConnReq | ChatErrorType_connReqMessageProhibited | ChatErrorType_contactNotReady @@ -1080,7 +1080,7 @@ ChatErrorType = ( | ChatErrorType_exception ) -ChatErrorType_Tag = Literal["noActiveUser", "noConnectionUser", "noSndFileUser", "noRcvFileUser", "userUnknown", "userExists", "chatRelayExists", "differentActiveUser", "cantDeleteActiveUser", "cantDeleteLastUser", "cantHideLastUser", "hiddenUserAlwaysMuted", "emptyUserPassword", "userAlreadyHidden", "userNotHidden", "invalidDisplayName", "chatNotStarted", "chatNotStopped", "chatStoreChanged", "invalidConnReq", "simplexName", "unsupportedConnReq", "connReqMessageProhibited", "contactNotReady", "contactNotActive", "contactDisabled", "connectionDisabled", "groupUserRole", "groupMemberInitialRole", "contactIncognitoCantInvite", "groupIncognitoCantInvite", "groupContactRole", "groupDuplicateMember", "groupDuplicateMemberId", "groupNotJoined", "groupMemberNotActive", "cantBlockMemberForSelf", "groupMemberUserRemoved", "groupMemberNotFound", "groupCantResendInvitation", "groupInternal", "fileNotFound", "fileSize", "fileAlreadyReceiving", "fileCancelled", "fileCancel", "fileAlreadyExists", "fileWrite", "fileSend", "fileRcvChunk", "fileInternal", "fileImageType", "fileImageSize", "fileNotReceived", "fileNotApproved", "fallbackToSMPProhibited", "inlineFileProhibited", "invalidForward", "invalidChatItemUpdate", "invalidChatItemDelete", "hasCurrentCall", "noCurrentCall", "callContact", "directMessagesProhibited", "agentVersion", "agentNoSubResult", "commandError", "agentCommandError", "invalidFileDescription", "connectionIncognitoChangeProhibited", "connectionUserChangeProhibited", "peerChatVRangeIncompatible", "relayTestError", "internalError", "exception"] +ChatErrorType_Tag = Literal["noActiveUser", "noConnectionUser", "noSndFileUser", "noRcvFileUser", "userUnknown", "userExists", "chatRelayExists", "differentActiveUser", "cantDeleteActiveUser", "cantDeleteLastUser", "cantHideLastUser", "hiddenUserAlwaysMuted", "emptyUserPassword", "userAlreadyHidden", "userNotHidden", "invalidDisplayName", "chatNotStarted", "chatNotStopped", "chatStoreChanged", "invalidConnReq", "simplexDomain", "unsupportedConnReq", "connReqMessageProhibited", "contactNotReady", "contactNotActive", "contactDisabled", "connectionDisabled", "groupUserRole", "groupMemberInitialRole", "contactIncognitoCantInvite", "groupIncognitoCantInvite", "groupContactRole", "groupDuplicateMember", "groupDuplicateMemberId", "groupNotJoined", "groupMemberNotActive", "cantBlockMemberForSelf", "groupMemberUserRemoved", "groupMemberNotFound", "groupCantResendInvitation", "groupInternal", "fileNotFound", "fileSize", "fileAlreadyReceiving", "fileCancelled", "fileCancel", "fileAlreadyExists", "fileWrite", "fileSend", "fileRcvChunk", "fileInternal", "fileImageType", "fileImageSize", "fileNotReceived", "fileNotApproved", "fallbackToSMPProhibited", "inlineFileProhibited", "invalidForward", "invalidChatItemUpdate", "invalidChatItemDelete", "hasCurrentCall", "noCurrentCall", "callContact", "directMessagesProhibited", "agentVersion", "agentNoSubResult", "commandError", "agentCommandError", "invalidFileDescription", "connectionIncognitoChangeProhibited", "connectionUserChangeProhibited", "peerChatVRangeIncompatible", "relayTestError", "internalError", "exception"] ChatFeature = Literal["timedMessages", "fullDelete", "reactions", "voice", "files", "calls", "sessions"] @@ -1844,7 +1844,7 @@ class GroupInfo(TypedDict): membersRequireAttention: int # int viaGroupLinkUri: NotRequired[str] groupKeys: NotRequired["GroupKeys"] - simplexNameVerification: NotRequired[bool] + groupDomainVerified: NotRequired[bool] class GroupKeys(TypedDict): publicGroupId: str @@ -2107,8 +2107,8 @@ class LocalProfile(TypedDict): peerType: NotRequired["ChatPeerType"] localBadge: NotRequired["LocalBadge"] localAlias: str - simplexName: NotRequired["SimplexNameClaim"] - simplexNameVerification: NotRequired[bool] + contactDomain: NotRequired["SimplexDomainClaim"] + contactDomainVerified: NotRequired[bool] MemberCriteria = Literal["all"] @@ -2241,11 +2241,6 @@ MsgReceiptStatus = Literal["ok", "badMsgHash"] MsgSigStatus = Literal["verified", "signedNoKey"] -class NameClaimProof(TypedDict): - linkOwnerId: NotRequired[str] - presHeader: str - signature: str - class NameErrorType_NO_RESOLVER(TypedDict): type: Literal["NO_RESOLVER"] @@ -2379,7 +2374,7 @@ class Profile(TypedDict): preferences: NotRequired["Preferences"] peerType: NotRequired["ChatPeerType"] badge: NotRequired["BadgeProof"] - simplexName: NotRequired["SimplexNameClaim"] + contactDomain: NotRequired["SimplexDomainClaim"] class ProxyClientError_protocolError(TypedDict): type: Literal["protocolError"] @@ -2421,7 +2416,7 @@ ProxyError_Tag = Literal["PROTOCOL", "BROKER", "BASIC_AUTH", "NO_SESSION"] class PublicGroupAccess(TypedDict): groupWebPage: NotRequired[str] - simplexName: NotRequired["SimplexNameClaim"] + groupDomainClaim: NotRequired["SimplexDomainClaim"] domainWebPage: bool allowEmbedding: bool @@ -2764,30 +2759,35 @@ class SecurityCode(TypedDict): class SimplePreference(TypedDict): allow: "FeatureAllowed" -SimplexLinkType = Literal["contact", "invitation", "group", "channel", "relay"] - -class SimplexNameClaim(TypedDict): - name: str - proof: NotRequired["NameClaimProof"] - -class SimplexNameDomain(TypedDict): +class SimplexDomain(TypedDict): nameTLD: "SimplexTLD" domain: str subDomain: list[str] -class SimplexNameError_noValidLink(TypedDict): +class SimplexDomainClaim(TypedDict): + domain: str + proof: NotRequired["SimplexDomainProof"] + +class SimplexDomainError_noValidLink(TypedDict): type: Literal["noValidLink"] -class SimplexNameError_unknownName(TypedDict): - type: Literal["unknownName"] +class SimplexDomainError_unknownDomain(TypedDict): + type: Literal["unknownDomain"] -SimplexNameError = SimplexNameError_noValidLink | SimplexNameError_unknownName +SimplexDomainError = SimplexDomainError_noValidLink | SimplexDomainError_unknownDomain -SimplexNameError_Tag = Literal["noValidLink", "unknownName"] +SimplexDomainError_Tag = Literal["noValidLink", "unknownDomain"] + +class SimplexDomainProof(TypedDict): + linkOwnerId: NotRequired[str] + presHeader: str + signature: str + +SimplexLinkType = Literal["contact", "invitation", "group", "channel", "relay"] class SimplexNameInfo(TypedDict): nameType: "SimplexNameType" - nameDomain: "SimplexNameDomain" + nameDomain: "SimplexDomain" SimplexNameType = Literal["publicGroup", "contact"] diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 56ebca020d..4ca9cefc7a 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."93925b257c76ee95d9b9ea3dcf433fecdb514c9c" = "0fp1mk0bsdmy9izcccrhci2268vwqgl82maf830908kd5fiiimzs"; + "https://github.com/simplex-chat/simplexmq.git"."836254a4c6bd5e17b40acbcaf77a83802d44919e" = "1g4385sv7i1h63yk3ch2kw8hprak1dzvr34v9mn6xz0fy10g8xlg"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat/Badges.hs b/src/Simplex/Chat/Badges.hs index fb35f0ed36..6e35517ed3 100644 --- a/src/Simplex/Chat/Badges.hs +++ b/src/Simplex/Chat/Badges.hs @@ -66,14 +66,11 @@ import Data.Text (Text) import Data.Text.Encoding (encodeUtf8) import Data.Time.Clock (NominalDiffTime, UTCTime, addUTCTime, nominalDay) import Simplex.FileTransfer.Description (gb, maxFileSize) -import Simplex.Messaging.Agent.Protocol (OwnerId) import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..), fromTextField_) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.BBS import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON) -import Simplex.Messaging.SimplexName (SimplexNameInfo) -import Simplex.Messaging.Util (decodeJSON, encodeJSON) #if defined(dbPostgres) import Database.PostgreSQL.Simple.FromField (FromField (..)) import Database.PostgreSQL.Simple.ToField (ToField (..)) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 289fa90e7b..7d9f1c6155 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -417,7 +417,7 @@ data ChatCommand | APIGetCallInvitations | APICallStatus ContactId WebRTCCallStatus | APIUpdateProfile {userId :: UserId, profile :: Profile} - | APISetUserName {userId :: UserId, simplexName :: Maybe SimplexNameInfo} + | APISetUserDomain {userId :: UserId, simplexDomain :: Maybe SimplexDomain} | APISetContactPrefs {contactId :: ContactId, preferences :: Preferences} | APISetContactAlias {contactId :: ContactId, localAlias :: LocalAlias} | APISetGroupAlias {groupId :: GroupId, localAlias :: LocalAlias} @@ -538,8 +538,8 @@ data ChatCommand | APIConnectPreparedGroup {groupId :: GroupId, incognito :: IncognitoEnabled, ownerContact :: Maybe GroupOwnerContact, msgContent_ :: Maybe MsgContent} | APIConnect {userId :: UserId, incognito :: IncognitoEnabled, preparedLink_ :: Maybe ACreatedConnLink} -- Maybe is used to report link parsing failure as special error | Connect {incognito :: IncognitoEnabled, connTarget_ :: Maybe AConnectTarget} - | APIVerifyContactName {contactId :: ContactId} - | APIVerifyPublicGroupName {groupId :: GroupId} + | APIVerifyContactDomain {contactId :: ContactId} + | APIVerifyGroupDomain {groupId :: GroupId} | APIConnectContactViaAddress UserId IncognitoEnabled ContactId | ConnectSimplex IncognitoEnabled -- UserId (not used in UI) | DeleteContact ContactName ChatDeleteMode @@ -1406,9 +1406,9 @@ data ChatError deriving (Show, Exception) -- why a resolved SimpleX name could not be used (the name itself resolved; an unregistered name is the agent's NAME NOT_FOUND) -data SimplexNameError - = SNENoValidLink -- the name's record has no usable contact/channel link - | SNEUnknownName -- the resolved link's profile has no name, or a different name +data SimplexDomainError + = SDENoValidLink -- the name's record has no usable contact/channel link + | SDEUnknownDomain -- the resolved link's profile has no name, or a different name deriving (Eq, Show) data ChatErrorType @@ -1432,7 +1432,7 @@ data ChatErrorType | CEChatNotStopped | CEChatStoreChanged | CEInvalidConnReq - | CESimplexName {simplexName :: SimplexNameInfo, simplexNameError :: SimplexNameError} + | CESimplexDomain {simplexDomain :: SimplexDomain, simplexDomainError :: SimplexDomainError} | CEUnsupportedConnReq | CEInvalidChatMessage {connection :: Connection, msgMeta :: Maybe MsgMetaJSON, messageData :: Text, message :: String} | CEConnReqMessageProhibited @@ -1764,7 +1764,7 @@ $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "GLP") ''GroupLinkPlan) $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "FC") ''ForwardConfirmation) -$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "SNE") ''SimplexNameError) +$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "SDE") ''SimplexDomainError) $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CE") ''ChatErrorType) diff --git a/src/Simplex/Chat/Core.hs b/src/Simplex/Chat/Core.hs index 957812f6e9..cc3e49beb6 100644 --- a/src/Simplex/Chat/Core.hs +++ b/src/Simplex/Chat/Core.hs @@ -140,7 +140,7 @@ createActiveUser cc CoreChatOpts {chatRelay} = \case displayName <- T.pack <$> withPrompt "display name" getLine createUser loop False $ mkProfile displayName where - mkProfile displayName = Profile {displayName, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, simplexName = Nothing} + mkProfile displayName = Profile {displayName, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, contactDomain = Nothing} createUser onError clientService p = execChatCommand' (CreateActiveUser NewUser {profile = Just p, pastTimestamp = False, userChatRelay = BoolDef chatRelay, clientService = BoolDef clientService}) 0 `runReaderT` cc >>= \case Right (CRActiveUser user) -> pure user diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index b13fe20196..2c2c34fb48 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -57,7 +57,7 @@ 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.Names (NameClaimProof (..), SimplexNameClaim (..), claimName, claimProof, mkSimplexNameClaim, verifyNameProofSig) +import Simplex.Chat.Names (SimplexDomainProof (..), SimplexDomainClaim (..), claimDomain, mkDomainClaim) import Simplex.Chat.Call import Simplex.Chat.Controller import Simplex.Chat.Delivery (DeliveryJobScope (..), DeliveryJobSpec (..), DeliveryWorkerScope (..)) @@ -1493,24 +1493,24 @@ processChatCommand cxt nm = \case withCurrentCall contactId $ \user ct call -> updateCallItemStatus user ct call receivedStatus Nothing $> Just call APIUpdateProfile userId profile -> withUserId userId (`updateProfile` profile) - APISetUserName userId name_ -> withUserId userId $ \user@User {profile = p@LocalProfile {contactLink, simplexName}} -> - if (claimName <$> simplexName) == name_ + APISetUserDomain userId domain_ -> withUserId userId $ \user@User {profile = p@LocalProfile {contactLink, contactDomain}} -> + if (claimDomain <$> contactDomain) == domain_ then pure $ CRUserProfileNoChange user else do - cl' <- case name_ of + cl' <- case domain_ of Nothing -> pure contactLink - Just ni@SimplexNameInfo {nameDomain} -> do - UserContactLink {shortLinkDataSet, connLinkContact = CCLink fl sl_} <- withFastStore (`getUserAddress` user) + Just domain -> do + UserContactLink {shortLinkDataSet, connLinkContact = CCLink _ sl_} <- withFastStore (`getUserAddress` user) case sl_ of Just sl | shortLinkDataSet -> do - NameRecord {nrSimplexContact} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) nameDomain - unless (nameResolvesTo sl nrSimplexContact) $ throwChatError $ CESimplexName ni SNENoValidLink + NameRecord {nrSimplexContact} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) domain + unless (nameResolvesTo sl nrSimplexContact) $ throwChatError $ CESimplexDomain domain SDENoValidLink pure $ Just (CLShort sl) _ -> throwCmdError "create the address short link and add it to name" - let p' = (fromLocalProfile p :: Profile) {simplexName = mkSimplexNameClaim name_ Nothing, contactLink = cl'} + let p' = (fromLocalProfile p :: Profile) {contactDomain = mkDomainClaim <$> domain_, contactLink = cl'} updateProfile_ user p' True $ withFastStore $ \db -> do user' <- updateUserProfile db user p' - liftIO $ setUserSimplexName db user' name_ + liftIO $ setUserSimplexDomain db user' domain_ APISetContactPrefs contactId prefs' -> withUser $ \user -> do ct <- withFastStore $ \db -> getContact db cxt user contactId updateContactPrefs user ct prefs' @@ -2005,7 +2005,7 @@ processChatCommand cxt nm = \case subMode <- chatReadVar subscriptionMode -- TODO [badges] bind link and badge to handshake context linkProfile <- presentUserBadge user incognitoProfile $ userProfileDirect user incognitoProfile Nothing True - let userData = contactShortLinkData linkProfile {simplexName = Nothing} Nothing + 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 ccLink' <- shortenCreatedLink ccLink @@ -2290,20 +2290,19 @@ processChatCommand cxt nm = \case _ -> throwError e connectWithPlan user incognito ccLink plan Connect _ Nothing -> throwChatError CEInvalidConnReq - APIVerifyContactName contactId -> withUser $ \user -> do - Contact {profile = LocalProfile {simplexName}, preparedContact} <- withFastStore $ \db -> getContact db cxt user contactId + APIVerifyContactDomain contactId -> withUser $ \user -> do + ct@Contact {profile = LocalProfile {contactDomain}, preparedContact} <- withFastStore $ \db -> getContact db cxt user contactId let connLink_ = preparedContact >>= \PreparedContact {connLinkToConnect = ACCL m (CCLink _ sLnk_)} -> ACSL m <$> sLnk_ - reason <- verifyEntityName user nm (claimName <$> simplexName) connLink_ (claimProof =<< simplexName) "contact has no name to verify" $ - \v -> withStore' $ \db -> setContactNameVerified db user contactId v - ct' <- withFastStore $ \db -> getContact db cxt user contactId + domain <- maybe (throwCmdError "contact has no name to verify") pure contactDomain + (verified, reason) <- verifyEntityDomain user nm NTContact domain connLink_ + ct' <- maybe (pure ct) (\v -> withFastStore' $ \db -> setContactDomainVerified db user ct v) verified pure $ CRContactNameVerified user ct' reason - APIVerifyPublicGroupName groupId -> withUser $ \user -> do - GroupInfo {groupProfile = GroupProfile {publicGroup}, preparedGroup} <- withFastStore $ \db -> getGroupInfo db cxt user groupId - let access = publicGroup >>= publicGroupAccess - connLink_ = preparedGroup >>= \PreparedGroup {connLinkToConnect = CCLink _ sLnk_} -> ACSL SCMContact <$> sLnk_ - reason <- verifyEntityName user nm (claimName <$> (access >>= publicGroupClaim)) connLink_ (claimProof =<< (access >>= publicGroupClaim)) "group has no name to verify" $ - \v -> withStore' $ \db -> setGroupNameVerified db user groupId v - g' <- withFastStore $ \db -> getGroupInfo db cxt user groupId + APIVerifyGroupDomain groupId -> withUser $ \user -> do + g@GroupInfo {groupProfile = GroupProfile {publicGroup}, preparedGroup} <- withFastStore $ \db -> getGroupInfo db cxt user groupId + let connLink_ = preparedGroup >>= \PreparedGroup {connLinkToConnect = CCLink _ sLnk_} -> ACSL SCMContact <$> sLnk_ + domain <- maybe (throwCmdError "group has no name to verify") pure $ publicGroup >>= publicGroupAccess >>= groupDomainClaim + (verified, reason) <- verifyEntityDomain user nm NTPublicGroup domain connLink_ + g' <- maybe (pure g) (\v -> withFastStore' $ \db -> setGroupDomainVerified db user g v) verified pure $ CRGroupNameVerified user g' reason APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do ct@Contact {profile = LocalProfile {contactLink}} <- withFastStore $ \db -> getContact db cxt user contactId @@ -3134,14 +3133,14 @@ processChatCommand cxt nm = \case updateGroupProfileByName gName $ \p -> p {description} ShowGroupDescription gName -> withUser $ \user -> CRGroupDescription user <$> withFastStore (\db -> getGroupInfoByName db cxt user gName) - APISetPublicGroupAccess gId access@PublicGroupAccess {simplexName} -> withUser $ \user -> do + APISetPublicGroupAccess gId access@PublicGroupAccess {groupDomainClaim = newClaim} -> withUser $ \user -> do gInfo@GroupInfo {groupProfile = p@GroupProfile {publicGroup}} <- withStore $ \db -> getGroupInfo db cxt user gId case publicGroup of Just pg@PublicGroupProfile {groupLink, publicGroupAccess = existingAccess} -> do - forM_ (claimName <$> simplexName) $ \newName@SimplexNameInfo {nameDomain} -> - when (Just newName /= (claimName <$> (existingAccess >>= publicGroupClaim))) $ do - NameRecord {nrSimplexChannel} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) nameDomain - unless (nameResolvesTo groupLink nrSimplexChannel) $ throwChatError $ CESimplexName newName SNENoValidLink + forM_ (claimDomain <$> newClaim) $ \newDomain -> + when (Just newDomain /= (claimDomain <$> (existingAccess >>= groupDomainClaim))) $ do + NameRecord {nrSimplexChannel} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) newDomain + unless (nameResolvesTo groupLink nrSimplexChannel) $ throwChatError $ CESimplexDomain newDomain SDENoValidLink runUpdateGroupProfile user gInfo p {publicGroup = Just pg {publicGroupAccess = Just access}} Nothing -> throwChatError $ CECommandError "not a public group" APICreateGroupLink groupId mRole -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do @@ -4209,12 +4208,13 @@ processChatCommand cxt nm = \case (FixedLinkData {linkConnReq = cReq, rootKey}, cData) <- getShortLinkConnReq nm user l' contactSLinkData_ <- mapM linkDataBadge =<< liftIO (decodeLinkUserData cData) let linkProfile_ = (\ContactShortLinkData {profile} -> profile) <$> contactSLinkData_ - linkName_ = linkProfile_ >>= \Profile {simplexName} -> claimName <$> simplexName - verifiedName_ = case nl of CTName ni -> Just ni; _ -> Nothing - refreshContact ct' = case (verifiedName_, linkProfile_) of + linkDomain_ = linkProfile_ >>= \Profile {contactDomain} -> claimDomain <$> contactDomain + verifiedName = case nl of CTName ni -> Just ni; _ -> Nothing + refreshContact ct' = case (verifiedName, linkProfile_) of (Just _, Just p) -> updateContactFromLinkData user ct' p _ -> pure ct' - forM_ verifiedName_ $ \ni -> verifyNameClaim ni linkName_ + forM_ verifiedName $ \SimplexNameInfo {nameDomain} -> + unless (linkDomain_ == Just nameDomain) $ throwChatError $ CESimplexDomain nameDomain SDEUnknownDomain withFastStore' (\db -> getContactWithoutConnViaShortAddress db cxt user l') >>= \case Just ct' | not (contactDeleted ct') -> do ct'' <- refreshContact ct' @@ -4224,7 +4224,7 @@ processChatCommand cxt nm = \case ov = verifyLinkOwner rootKey owners l' sig_ plan <- contactRequestPlan user cReq contactSLinkData_ ov case plan of - CPContactAddress cap@(CAPOk {}) -> pure (con l' cReq, CPContactAddress cap {verifiedName = verifiedName_}) + CPContactAddress cap@(CAPOk {}) -> pure (con l' cReq, CPContactAddress cap {verifiedName}) CPContactAddress (CAPKnown ct') -> do ct'' <- refreshContact ct' pure (con l' cReq, CPContactAddress (CAPKnown ct'')) @@ -4278,18 +4278,18 @@ processChatCommand cxt nm = \case (Nothing, Nothing) -> pure () _ -> throwChatError CEInvalidConnReq let ov = verifyLinkOwner rootKey owners l' sig_ - verifiedName_ = case nl of CTName ni -> Just ni; _ -> Nothing - claimedName GroupProfile {publicGroup} = claimName <$> (publicGroup >>= publicGroupAccess >>= publicGroupClaim) + verifiedName = case nl of CTName ni -> Just ni; _ -> Nothing plan <- groupJoinRequestPlan user cReq (Just linkInfo) groupSLinkData_ ov - forM_ verifiedName_ $ \ni -> - verifyNameClaim ni $ case plan of - CPGroupLink (GLPOk _ (Just GroupShortLinkData {groupProfile = gp}) _ _) -> claimedName gp - CPGroupLink (GLPKnown GroupInfo {groupProfile = gp} _ _ _) -> claimedName gp - CPGroupLink (GLPOwnLink GroupInfo {groupProfile = gp}) -> claimedName gp - CPGroupLink (GLPConnectingProhibit (Just GroupInfo {groupProfile = gp})) -> claimedName gp - _ -> maybe Nothing (\GroupShortLinkData {groupProfile = gp} -> claimedName gp) groupSLinkData_ + forM_ verifiedName $ \SimplexNameInfo {nameDomain} -> + let domain_ = (\GroupProfile {publicGroup} -> claimDomain <$> (publicGroup >>= publicGroupAccess >>= groupDomainClaim)) =<< case plan of + CPGroupLink (GLPOk _ (Just GroupShortLinkData {groupProfile}) _ _) -> Just groupProfile + CPGroupLink (GLPKnown GroupInfo {groupProfile} _ _ _) -> Just groupProfile + CPGroupLink (GLPOwnLink GroupInfo {groupProfile}) -> Just groupProfile + CPGroupLink (GLPConnectingProhibit (Just GroupInfo {groupProfile})) -> Just groupProfile + _ -> (\GroupShortLinkData {groupProfile} -> groupProfile) <$> groupSLinkData_ + in unless (domain_ == Just nameDomain) $ throwChatError $ CESimplexDomain nameDomain SDEUnknownDomain pure $ case plan of - CPGroupLink glp@(GLPOk {}) -> (con l' cReq, CPGroupLink glp {verifiedName = verifiedName_}) + CPGroupLink glp@(GLPOk {}) -> (con l' cReq, CPGroupLink glp {verifiedName}) _ -> (con l' cReq, plan) where unsupportedGroupType = \case @@ -4311,13 +4311,13 @@ processChatCommand cxt nm = \case pure (con l' (linkConnReq fd), CPGroupLink (GLPKnown g' (BoolDef updated) ov (ListDef glOwners))) -- resolve a name to its first contact/channel short link resolveNameLink :: User -> SimplexNameInfo -> CM (ConnShortLink 'CMContact) - resolveNameLink user ni@SimplexNameInfo {nameType, nameDomain} = do + resolveNameLink user SimplexNameInfo {nameType, nameDomain} = do NameRecord {nrSimplexContact, nrSimplexChannel} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) nameDomain let (candidates, ctType) = case nameType of NTContact -> (nrSimplexContact, CCTContact) NTPublicGroup -> (nrSimplexChannel, CCTChannel) - maybe (throwChatError $ CESimplexName ni SNENoValidLink) pure $ firstNameLink ctType candidates + maybe (throwChatError $ CESimplexDomain nameDomain SDENoValidLink) pure $ firstNameLink ctType candidates connectWithPlan :: User -> IncognitoEnabled -> ACreatedConnLink -> ConnectionPlan -> CM ChatResponse connectWithPlan user@User {userId} incognito ccLink plan | connectionPlanProceed plan = do @@ -4789,53 +4789,31 @@ firstNameLink ctType = foldr (\t r -> nameLink t <|> r) Nothing nameResolvesTo :: ConnShortLink 'CMContact -> [Text] -> Bool nameResolvesTo sLnk = any (either (const False) (sameShortLinkContact sLnk) . strDecode . encodeUtf8) -verifyName :: User -> NetworkRequestMode -> SimplexNameInfo -> Maybe AConnShortLink -> Maybe NameClaimProof -> CM (Maybe Bool, Maybe Text) -verifyName user nm claim connLink_ proof_ = case (proof_, connLink_) of +verifyEntityDomain :: User -> NetworkRequestMode -> SimplexNameType -> SimplexDomainClaim -> Maybe AConnShortLink -> CM (Maybe Bool, Maybe Text) +verifyEntityDomain user nm nameType SimplexDomainClaim {domain = StrJSON domain, proof = proof_} connLink_ = case (proof_, connLink_) of (Nothing, _) -> pure (Nothing, Just "no name proof to verify") (_, Nothing) -> pure (Nothing, Just "no connection link to check the name against") (Just proof, Just (ACSL SCMContact profileSLnk)) -> do - let SimplexNameInfo {nameType, nameDomain} = claim - NameRecord {nrSimplexContact, nrSimplexChannel} <- - withAgent $ \a -> resolveSimplexName a nm (aUserId user) nameDomain + NameRecord {nrSimplexContact, nrSimplexChannel} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) domain let resolvedLinks = case nameType of NTContact -> nrSimplexContact NTPublicGroup -> nrSimplexChannel if not (nameResolvesTo profileSLnk resolvedLinks) then pure (Just False, Just "the name does not resolve to this address") else do - ok <- verifyProofKey nm user profileSLnk claim proof - pure $ - if ok - then (Just True, Nothing) - else (Just False, Just "the name proof was not signed by this address's owner") + ok <- verifyDomainProof proof profileSLnk + pure (Just ok, if ok then Nothing else Just "the name proof was not signed by this address's owner") (Just _, Just _) -> pure (Nothing, Just "unexpected connection link type for name verification") - --- verify the proof signature against the profile address's owner key; --- getShortLinkConnReq's network/agent error propagates (UI can retry), not recorded as a verdict -verifyProofKey :: NetworkRequestMode -> User -> ShortLinkContact -> SimplexNameInfo -> NameClaimProof -> CM Bool -verifyProofKey nm user sLnk claim proof = do - (FixedLinkData {rootKey}, ContactLinkData _ UserContactData {owners}) <- getShortLinkConnReq nm user sLnk - pure $ proofSignedByOwner rootKey owners sLnk claim proof - -proofSignedByOwner :: C.PublicKeyEd25519 -> [OwnerAuth] -> ShortLinkContact -> SimplexNameInfo -> NameClaimProof -> Bool -proofSignedByOwner rootKey owners sLnk claim proof@NameClaimProof {linkOwnerId} = - let key_ = case linkOwnerId of - Nothing -> Just rootKey - Just (StrJSON oid) -> ownerKey <$> find (\OwnerAuth {ownerId} -> ownerId == oid) owners - in maybe False (\key -> verifyNameProofSig key claim sLnk proof) key_ - --- connecting by name resolves the name to this address, so it is verified without checking the proof -verifyNameClaim :: SimplexNameInfo -> Maybe SimplexNameInfo -> CM (Maybe Bool) -verifyNameClaim ni claimedName_ = do - unless (claimedName_ == Just ni) $ throwChatError $ CESimplexName ni SNEUnknownName - pure (Just True) - -verifyEntityName :: User -> NetworkRequestMode -> Maybe SimplexNameInfo -> Maybe AConnShortLink -> Maybe NameClaimProof -> String -> (Bool -> CM ()) -> CM (Maybe Text) -verifyEntityName user nm claim_ connLink_ proof_ noNameErr persist = do - claim <- maybe (throwCmdError noNameErr) pure claim_ - (verdict, reason) <- verifyName user nm claim connLink_ proof_ - forM_ verdict persist - pure reason + 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 + let ownerKey_ = case linkOwnerId of + Nothing -> Just rootKey + Just (StrJSON oid) -> ownerKey <$> find (\OwnerAuth {ownerId} -> ownerId == oid) owners + pure $ maybe False (\k -> C.verify' k signature proofPayload) ownerKey_ + where + proofPayload = strEncode (Str "simplex_domain_v1", presHeader, domain, ct, srv, key) data ConnectViaContactResult = CVRConnectedContact Contact @@ -5313,7 +5291,7 @@ chatCommandP = "/_call status @" *> (APICallStatus <$> A.decimal <* A.space <*> strP), "/_call get" $> APIGetCallInvitations, "/_profile " *> (APIUpdateProfile <$> A.decimal <* A.space <*> jsonP), - "/_set_name " *> (APISetUserName <$> A.decimal <*> optional (A.space *> strP)), + "/_set domain " *> (APISetUserDomain <$> A.decimal <*> optional (A.space *> strP)), "/_set alias @" *> (APISetContactAlias <$> A.decimal <*> (A.space *> textP <|> pure "")), "/_set alias #" *> (APISetGroupAlias <$> A.decimal <*> (A.space *> textP <|> pure "")), "/_set alias :" *> (APISetConnectionAlias <$> A.decimal <*> (A.space *> textP <|> pure "")), @@ -5484,8 +5462,8 @@ chatCommandP = "/_set conn user :" *> (APIChangeConnectionUser <$> A.decimal <* A.space <*> A.decimal), ("/connect" <|> "/c") *> (AddContact <$> incognitoP), ("/connect" <|> "/c") *> (Connect <$> incognitoP <* A.space <*> ((Just <$> strP) <|> A.takeTill isSpace $> Nothing)), - "/_verify name @" *> (APIVerifyContactName <$> A.decimal), - "/_verify name #" *> (APIVerifyPublicGroupName <$> A.decimal), + "/_verify domain @" *> (APIVerifyContactDomain <$> A.decimal), + "/_verify domain #" *> (APIVerifyGroupDomain <$> A.decimal), ForwardMessage <$> chatNameP <* " <- @" <*> displayNameP <* A.space <*> msgTextP, ForwardGroupMessage <$> chatNameP <* " <- #" <*> displayNameP <* A.space <* A.char '@' <*> (Just <$> displayNameP) <* A.space <*> msgTextP, ForwardGroupMessage <$> chatNameP <* " <- #" <*> displayNameP <*> pure Nothing <* A.space <*> msgTextP, @@ -5661,10 +5639,10 @@ chatCommandP = onOffP = ("on" $> True) <|> ("off" $> False) publicGroupAccessP = do groupWebPage <- optional (" web=" *> (safeDecodeUtf8 <$> A.takeTill A.isSpace)) - simplexName <- optional (" name=" *> strP) - domainWebPage <- (" name_page=" *> onOffP) <|> pure False + groupDomain <- optional (" domain=" *> strP) + domainWebPage <- (" domain_page=" *> onOffP) <|> pure False allowEmbedding <- (" embed=" *> onOffP) <|> pure False - pure PublicGroupAccess {groupWebPage, simplexName = mkSimplexNameClaim simplexName Nothing, domainWebPage, allowEmbedding} + pure PublicGroupAccess {groupWebPage, groupDomainClaim = mkDomainClaim <$> groupDomain, domainWebPage, allowEmbedding} profileNameDescr = (,) <$> displayNameP <*> shortDescrP -- 'Help with bot':'link ','Menu of commands':[...] botCommandsP :: Parser [ChatBotCommand] @@ -5685,7 +5663,7 @@ chatCommandP = newUserP relay = do (cName, shortDescr) <- profileNameDescr service <- (" service=" *> onOffP) <|> pure False - let profile = Just Profile {displayName = cName, fullName = "", shortDescr, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, simplexName = Nothing} + let profile = Just Profile {displayName = cName, fullName = "", shortDescr, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, contactDomain = Nothing} pure NewUser {profile, pastTimestamp = False, userChatRelay = BoolDef relay, clientService = BoolDef service} newBotUserP = do files_ <- optional $ "files=" *> onOffP <* A.space @@ -5694,7 +5672,7 @@ chatCommandP = let preferences = case files_ of Just True -> Nothing _ -> Just (emptyChatPrefs :: Preferences) {files = Just FilesPreference {allow = FANo}} - profile = Just Profile {displayName = cName, fullName = "", shortDescr, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences, badge = Nothing, simplexName = Nothing} + profile = Just Profile {displayName = cName, fullName = "", shortDescr, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences, badge = Nothing, contactDomain = Nothing} pure NewUser {profile, pastTimestamp = False, userChatRelay = BoolDef False, clientService = BoolDef service} jsonP :: J.FromJSON a => Parser a jsonP = J.eitherDecodeStrict' <$?> A.takeByteString diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index c1996ded9b..d34e08401b 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -54,7 +54,7 @@ import Data.Time (addUTCTime) import Data.Time.Calendar (fromGregorian) import Data.Time.Clock (UTCTime (..), diffUTCTime, getCurrentTime, nominalDiffTimeToSeconds, secondsToDiffTime) import Simplex.Chat.Badges (BadgeCredential (..), ProofPresHeader (..), BadgeProof (..), BadgeStatus (..), LocalBadge (..), badgeProof, mkBadgeStatus, verifyBadge) -import Simplex.Chat.Names (claimName, mkSimplexNameClaim) +import Simplex.Chat.Names (SimplexDomainClaim (..), claimDomain) import Simplex.Chat.Call import Simplex.Chat.Controller import Simplex.Chat.Files @@ -1244,11 +1244,11 @@ memberInfo g m@GroupMember {memberId, memberRole, memberProfile, memberPubKey, a } redactedMemberProfile :: GroupInfo -> GroupMember -> Profile -> Profile -redactedMemberProfile g m Profile {displayName, fullName, shortDescr, image, contactLink = lnk, peerType, badge, simplexName} = - Profile {displayName, fullName, shortDescr = removeSimplexLink =<< shortDescr, image, contactLink, preferences = Nothing, peerType, badge, simplexName = redactedName} +redactedMemberProfile g m Profile {displayName, fullName, shortDescr, image, contactLink = lnk, peerType, badge, contactDomain} = + Profile {displayName, fullName, shortDescr = removeSimplexLink =<< shortDescr, image, contactLink, preferences = Nothing, peerType, badge, contactDomain = redactedDomain} where contactLink = if allowSimplexLinks then lnk else Nothing - redactedName = mkSimplexNameClaim (if allowDirect then claimName <$> simplexName else Nothing) Nothing + redactedDomain = if allowDirect then (\d -> d {proof = Nothing} :: SimplexDomainClaim) <$> contactDomain else Nothing allowDirect = groupFeatureMemberAllowed SGFDirectMessages m g allowSimplexLinks = groupFeatureMemberAllowed SGFSimplexLinks m g && allowDirect removeSimplexLink s @@ -1468,17 +1468,17 @@ updateGroupFromLinkData user gInfo@GroupInfo {groupProfile = p, groupSummary = G _ -> False updateContactFromLinkData :: User -> Contact -> Profile -> CM Contact -updateContactFromLinkData user ct@Contact {contactId, profile = profile@LocalProfile {simplexName = prevClaim, simplexNameVerification}} linkProfile@Profile {simplexName = newClaim} +updateContactFromLinkData user ct@Contact {profile = profile@LocalProfile {contactDomain = prevClaim, contactDomainVerified}} linkProfile@Profile {contactDomain = newClaim} | profileChanged || verifyChanged = do cxt <- chatStoreCxt - when profileChanged $ void $ withStore $ \db -> updateContactProfile db cxt user ct linkProfile - when verifyChanged $ withStore' $ \db -> setContactNameVerified db user contactId True - withStore $ \db -> getContact db cxt user contactId + withFastStore $ \db -> do + ct' <- updateContactProfile db cxt user ct linkProfile + if verifyChanged then liftIO $ setContactDomainVerified db user ct' True else pure ct' | otherwise = pure ct where profileChanged = fromLocalProfile profile /= linkProfile - claimChanged = (claimName <$> prevClaim) /= (claimName <$> newClaim) - verifyChanged = simplexNameVerification /= Just True || claimChanged + claimChanged = (claimDomain <$> prevClaim) /= (claimDomain <$> newClaim) + verifyChanged = contactDomainVerified /= Just True || claimChanged -- TODO [relays] owner: set owners on updating link data (multi-owner) groupLinkData :: GroupInfo -> GroupLink -> [GroupRelay] -> (UserConnLinkData 'CMContact, CRClientData) @@ -3109,7 +3109,7 @@ simplexTeamContactProfile = peerType = Nothing, preferences = Nothing, badge = Nothing, - simplexName = Nothing + contactDomain = Nothing } simplexStatusContactProfile :: Profile @@ -3123,7 +3123,7 @@ simplexStatusContactProfile = peerType = Just CPTBot, preferences = Nothing, badge = Nothing, - simplexName = Nothing + contactDomain = Nothing } timeItToView :: String -> CM' a -> CM' a diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index 90167ccdc2..9cc6778122 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -1422,7 +1422,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = void $ sendGroupMessages user gInfo Nothing False recipients events where updateRelay :: DB.Connection -> GroupRelay -> ([GroupRelay], Bool, [ShortLinkContact]) -> IO ([GroupRelay], Bool, [ShortLinkContact]) - updateRelay db relay@GroupRelay {groupMemberId, relayLink, relayStatus} (acc, changed, newlyActiveLinks) = + updateRelay db relay@GroupRelay {relayLink, relayStatus} (acc, changed, newlyActiveLinks) = case relayLink of Just rLink -- version is gated upstream at publish (getPublishableGroupRelays): an RSAccepted relay @@ -3335,7 +3335,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = -- transfer record + its scratch file in one transaction (file owned by the transfer, keyed per source) rft@RcvFileTransfer {fileId} <- withStore $ \db -> do transferId <- liftIO $ createRosterTransfer db gInfo (groupMemberId' fromMember) newVer fileDigest (groupMemberId' author) brokerTs relayHdr - createRosterRcvFile db userId gInfo fromMember transferId sharedMsgId rosterFInv (Just IFMSent) (fromIntegral chSize) + createRosterRcvFile db userId gInfo fromMember transferId sharedMsgId rosterFInv (Just IFMSent) chSize -- accept the chat-item-free file before chunk 1 (FIFO before it) so chunk 1 isn't rejected on RFSNew -- transient scratch file (consumed into roster_blob, then deleted): temp folder, not the user's files folder / Downloads tmpDir <- lift getChatTempDirectory @@ -3361,10 +3361,10 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = Just RcvRosterTransfer {rosterTransferId = transferId, rosterTransferVersion = pendingVer, rosterTransferDigest = pendingDigest, rosterTransferOwnerGMId = ownerGMId, rosterTransferBrokerTs = rosterBrokerTs, rosterTransferHeader = header_} -> do owner_ <- withStore' $ \db -> eitherToMaybe <$> runExceptT (getGroupMemberById db cxt user ownerGMId) blob <- readAssembledRoster - let isRelay = isUserGrpFwdRelay gInfo + let isRelay' = isUserGrpFwdRelay gInfo ackErr err = do cleanupRosterTransferById transferId - when isRelay $ forM_ owner_ $ \owner -> sendRosterAck gInfo owner pendingVer (Just err) + when isRelay' $ forM_ owner_ $ \owner -> sendRosterAck gInfo owner pendingVer (Just err) if FD.FileDigest (LC.sha512Hash (LB.fromStrict blob)) /= pendingDigest then ackErr "relay could not verify the roster blob" else case parseAll rosterBlobP blob of @@ -3388,7 +3388,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = forM_ results_ $ \results -> do emitRosterResults gInfo author rosterBrokerTs results -- ack while setting up (own status accepted/acknowledged); a serving (active) relay must not ack broadcasts. - when (isRelay && (relayOwnStatus gInfo == Just RSAccepted || relayOwnStatus gInfo == Just RSAcknowledgedRoster)) $ do + when (isRelay' && (relayOwnStatus gInfo == Just RSAccepted || relayOwnStatus gInfo == Just RSAcknowledgedRoster)) $ do sendRosterAck gInfo author pendingVer Nothing withStore' $ \db -> void $ updateRelayOwnStatusFromTo db gInfo RSAccepted RSAcknowledgedRoster where diff --git a/src/Simplex/Chat/Names.hs b/src/Simplex/Chat/Names.hs index ca45e8d9c7..081d7129a5 100644 --- a/src/Simplex/Chat/Names.hs +++ b/src/Simplex/Chat/Names.hs @@ -2,31 +2,27 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module Simplex.Chat.Names - ( SimplexNameClaim (..), - mkSimplexNameClaim, - claimName, - claimProof, - setClaimProof, - NameClaimProof (..), - verifyNameProofSig, + ( SimplexDomainClaim (..), + SimplexDomainProof (..), + mkDomainClaim, + claimDomain, ) where import qualified Data.Aeson.TH as JQ -import Data.ByteString.Char8 (ByteString) import Simplex.Chat.Badges (ProofPresHeader) -import Simplex.Messaging.Agent.Protocol (ConnShortLink (..), ConnectionMode (..), OwnerId) +import Simplex.Messaging.Agent.Protocol (OwnerId, SimplexDomain) import Simplex.Messaging.Agent.Store.DB (fromTextField_) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (defaultJSON) -import Simplex.Messaging.SimplexName (SimplexNameInfo) import Simplex.Messaging.Util (decodeJSON, encodeJSON) #if defined(dbPostgres) import Database.PostgreSQL.Simple.FromField (FromField (..)) @@ -36,47 +32,30 @@ import Database.SQLite.Simple.FromField (FromField (..)) import Database.SQLite.Simple.ToField (ToField (..)) #endif --- A name claim proof: signed by the address owner's key (linkOwnerId = Just oid when a channel --- owner other than the address signs, Nothing when the address's own root key signs) over --- the name-proof payload, binding the name to the address (see nameProofPayload). -data NameClaimProof = NameClaimProof +-- A name claim proof: signed by the address owner's key over proof payload - see verifyDomainProof. +data SimplexDomainProof = SimplexDomainProof { linkOwnerId :: Maybe (StrJSON "OwnerId" OwnerId), presHeader :: ProofPresHeader, signature :: C.Signature 'C.Ed25519 } deriving (Eq, Show) -nameProofPayload :: SimplexNameInfo -> ProofPresHeader -> ConnShortLink 'CMContact -> ByteString -nameProofPayload name presHeader (CSLContact _ ct srv key) = - strEncode (Str "simplex_names_v1", presHeader, name, ct, srv, key) +$(JQ.deriveJSON defaultJSON ''SimplexDomainProof) -verifyNameProofSig :: C.PublicKeyEd25519 -> SimplexNameInfo -> ConnShortLink 'CMContact -> NameClaimProof -> Bool -verifyNameProofSig ownerKey name sLnk NameClaimProof {presHeader, signature} = - C.verify' ownerKey signature (nameProofPayload name presHeader sLnk) +instance ToField SimplexDomainProof where toField = toField . encodeJSON -$(JQ.deriveJSON defaultJSON ''NameClaimProof) +instance FromField SimplexDomainProof where fromField = fromTextField_ decodeJSON --- stored as JSON in contact_profiles.simplex_name_proof -instance ToField NameClaimProof where toField = toField . encodeJSON - -instance FromField NameClaimProof where fromField = fromTextField_ decodeJSON - -data SimplexNameClaim = SimplexNameClaim - { name :: StrJSON "SimplexNameInfo" SimplexNameInfo, - proof :: Maybe NameClaimProof +data SimplexDomainClaim = SimplexDomainClaim + { domain :: StrJSON "SimplexDomain" SimplexDomain, + proof :: Maybe SimplexDomainProof } deriving (Eq, Show) -mkSimplexNameClaim :: Maybe SimplexNameInfo -> Maybe NameClaimProof -> Maybe SimplexNameClaim -mkSimplexNameClaim name_ proof_ = (\n -> SimplexNameClaim (StrJSON n) proof_) <$> name_ +mkDomainClaim :: SimplexDomain -> SimplexDomainClaim +mkDomainClaim = (`SimplexDomainClaim` Nothing) . StrJSON -claimName :: SimplexNameClaim -> SimplexNameInfo -claimName (SimplexNameClaim n _) = unStrJSON n +claimDomain :: SimplexDomainClaim -> SimplexDomain +claimDomain (SimplexDomainClaim n _) = unStrJSON n -claimProof :: SimplexNameClaim -> Maybe NameClaimProof -claimProof (SimplexNameClaim _ p) = p - -setClaimProof :: Maybe NameClaimProof -> SimplexNameClaim -> SimplexNameClaim -setClaimProof p (SimplexNameClaim n _) = SimplexNameClaim n p - -$(JQ.deriveJSON defaultJSON ''SimplexNameClaim) +$(JQ.deriveJSON defaultJSON ''SimplexDomainClaim) diff --git a/src/Simplex/Chat/ProfileGenerator.hs b/src/Simplex/Chat/ProfileGenerator.hs index 722c7c5f62..4d10945ab6 100644 --- a/src/Simplex/Chat/ProfileGenerator.hs +++ b/src/Simplex/Chat/ProfileGenerator.hs @@ -10,7 +10,7 @@ generateRandomProfile :: IO Profile generateRandomProfile = do adjective <- pick adjectives noun <- pickNoun adjective 2 - pure $ Profile {displayName = adjective <> noun, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, simplexName = Nothing} + pure $ Profile {displayName = adjective <> noun, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, contactDomain = Nothing} where pick :: [a] -> IO a pick xs = (xs !!) <$> randomRIO (0, length xs - 1) diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs index 485f9d8129..74d574b643 100644 --- a/src/Simplex/Chat/Store/Connections.hs +++ b/src/Simplex/Chat/Store/Connections.hs @@ -35,7 +35,6 @@ import Simplex.Chat.Protocol import Simplex.Chat.Store.Direct import Simplex.Chat.Store.Groups import Simplex.Chat.Store.Shared -import Simplex.Chat.Names (mkSimplexNameClaim) import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol (ConnId) import Simplex.Messaging.Agent.Store.AgentStore (firstRow, firstRow', fromOnlyBI, maybeFirstRow) @@ -118,15 +117,15 @@ getConnectionEntity db cxt user@User {userId, userContactId} agentConnId = do 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 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, 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 :. (cpsimplexName, cpContactNameVerification, cpsimplexNameProof)) = - let profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = mkSimplexNameClaim cpsimplexName cpsimplexNameProof, simplexNameVerification = unBI <$> cpContactNameVerification, peerType, localBadge = rowToBadge currentTs badgeRow, preferences, localAlias} + toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, 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) = + let profile = LocalProfile {profileId, displayName, fullName, shortDescr, 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 @@ -145,27 +144,26 @@ getConnectionEntity db cxt user@User {userId, userContactId} agentConnId = do SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, g.business_chat, g.business_member_id, g.customer_member_id, g.use_relays, g.relay_own_status, - g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, + g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, g.group_domain_verified, g.root_priv_key, g.root_pub_key, g.member_priv_key, - g.simplex_name_verification, -- GroupInfo {membership} mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, -- GroupInfo {membership = GroupMember {memberProfile}} pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, - pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.simplex_name, pu.simplex_name_verification, pu.simplex_name_proof, + pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, -- from GroupMember m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link FROM group_members m diff --git a/src/Simplex/Chat/Store/ContactRequest.hs b/src/Simplex/Chat/Store/ContactRequest.hs index b5b663c4ee..3bd481d7db 100644 --- a/src/Simplex/Chat/Store/ContactRequest.hs +++ b/src/Simplex/Chat/Store/ContactRequest.hs @@ -116,7 +116,7 @@ createOrUpdateContactRequest 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, 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof, + 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, -- Connection c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -152,7 +152,7 @@ createOrUpdateContactRequest cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 contact_requests cr JOIN contact_profiles p USING (contact_profile_id) WHERE cr.user_id = ? diff --git a/src/Simplex/Chat/Store/Direct.hs b/src/Simplex/Chat/Store/Direct.hs index 368189450f..71e5c80a63 100644 --- a/src/Simplex/Chat/Store/Direct.hs +++ b/src/Simplex/Chat/Store/Direct.hs @@ -51,7 +51,7 @@ module Simplex.Chat.Store.Direct getContactViaShortLinkToConnect, getContactIdByName, updateContactProfile, - setContactNameVerified, + setContactDomainVerified, updateContactUserPreferences, updateContactAlias, updateContactConnectionAlias, @@ -111,11 +111,11 @@ import Data.Type.Equality import Simplex.Chat.Badges (badgeToRow) import Simplex.Chat.Messages import Simplex.Chat.Store.Shared -import Simplex.Chat.Names (claimName, claimProof, setClaimProof) +import Simplex.Chat.Names (SimplexDomainClaim (..)) import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.UITheme -import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), ACreatedConnLink (..), ConnId, ConnShortLink, ConnectionModeI (..), ConnectionRequestUri, CreatedConnLink (..), SConnectionMode (..), UserId) +import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), ACreatedConnLink (..), ConnId, ConnShortLink, ConnectionModeI (..), ConnectionRequestUri, CreatedConnLink (..), SConnectionMode (..), SimplexNameInfo (..), UserId) import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow) import Simplex.Messaging.Agent.Store.DB (BoolInt (..)) import qualified Simplex.Messaging.Agent.Store.DB as DB @@ -326,7 +326,7 @@ getContactByConnReqHash db cxt user@User {userId} cReqHash1 cReqHash2 = do 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof, + cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified, -- Connection c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -404,13 +404,12 @@ createIncognitoProfile db User {userId} p = do createIncognitoProfile_ db userId createdAt p createPreparedContact :: DB.Connection -> StoreCxt -> User -> Profile -> ACreatedConnLink -> Maybe SharedMsgId -> Maybe Bool -> ExceptT StoreError IO Contact -createPreparedContact db cxt user p connLinkToConnect welcomeSharedMsgId nameVerified = do +createPreparedContact db cxt user p connLinkToConnect welcomeSharedMsgId verified_ = do currentTs <- liftIO getCurrentTime let prepared = Just (connLinkToConnect, welcomeSharedMsgId) ctUserPreferences = newContactUserPrefs user p - contactId <- createContact_ db cxt user p ctUserPreferences prepared "" currentTs - liftIO $ mapM_ (setContactNameVerified db user contactId) nameVerified - getContact db cxt user contactId + ct <- getContact db cxt user =<< createContact_ db cxt user p ctUserPreferences prepared "" currentTs + liftIO $ maybe (pure ct) (setContactDomainVerified db user ct) verified_ updatePreparedContactUser :: DB.Connection -> StoreCxt -> User -> Contact -> User -> ExceptT StoreError IO Contact updatePreparedContactUser @@ -570,14 +569,14 @@ updateContactProfile db cxt user@User {userId} c p' = do profile = toLocalProfile profileId p'' localAlias currentTs badgeVerified nameVerified updateContactProfile' currentTs badgeVerified profile where - Contact {contactId, localDisplayName, profile = lp@LocalProfile {profileId, displayName, localAlias, simplexName = prevClaim, simplexNameVerification = prevVerification}, userPreferences} = c - Profile {displayName = newName, simplexName, preferences} = p' + Contact {contactId, localDisplayName, profile = lp@LocalProfile {profileId, displayName, localAlias, contactDomain = prevClaim, contactDomainVerified = prevVerification}, userPreferences} = c + Profile {displayName = newName, contactDomain, preferences} = p' mergedPreferences = contactUserPreferences user userPreferences preferences $ contactConnIncognito c - claimChanged = (claimName <$> prevClaim) /= (claimName <$> simplexName) - p'' = (p' :: Profile) {simplexName = setClaimProof (if claimChanged then Nothing else claimProof =<< prevClaim) <$> simplexName} + claimChanged = (domain <$> prevClaim) /= (domain <$> contactDomain) + p'' = (p' :: Profile) {contactDomain = (\d -> d {proof = if claimChanged then Nothing else proof =<< prevClaim}) <$> contactDomain} clearVerificationIfClaimChanged = when claimChanged $ - DB.execute db "UPDATE contact_profiles SET simplex_name_verification = NULL WHERE user_id = ? AND contact_profile_id = ?" (userId, profileId) + DB.execute db "UPDATE contact_profiles SET contact_domain_verified = NULL WHERE user_id = ? AND contact_profile_id = ?" (userId, profileId) updateContactProfile' currentTs badgeVerified profile | displayName == newName = do liftIO $ updateContactProfile_' db userId profileId p'' badgeVerified currentTs @@ -590,15 +589,16 @@ updateContactProfile db cxt user@User {userId} c p' = do clearVerificationIfClaimChanged pure $ Right c {localDisplayName = ldn, profile, mergedPreferences} -setContactNameVerified :: DB.Connection -> User -> ContactId -> Bool -> IO () -setContactNameVerified db User {userId} contactId verified = +setContactDomainVerified :: DB.Connection -> User -> Contact -> Bool -> IO Contact +setContactDomainVerified db User {userId} ct@Contact {contactId, profile = p} verified = do DB.execute db [sql| - UPDATE contact_profiles SET simplex_name_verification = ? + UPDATE contact_profiles SET contact_domain_verified = ? WHERE contact_profile_id IN (SELECT contact_profile_id FROM contacts WHERE user_id = ? AND contact_id = ?) |] (BI verified, userId, contactId) + pure (ct {profile = p {contactDomainVerified = Just verified}} :: Contact) updateContactUserPreferences :: DB.Connection -> User -> Contact -> Preferences -> IO Contact updateContactUserPreferences db user@User {userId} c@Contact {contactId} userPreferences = do @@ -731,18 +731,17 @@ updateContactProfile_ db userId profileId profile badgeVerified = do updateContactProfile_' db userId profileId profile badgeVerified currentTs updateContactProfile_' :: DB.Connection -> UserId -> ProfileId -> Profile -> Maybe Bool -> UTCTime -> IO () -updateContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactLink, simplexName, preferences, peerType, badge} badgeVerified updatedAt = +updateContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactLink, contactDomain, preferences, peerType, badge} badgeVerified updatedAt = DB.execute db [sql| UPDATE contact_profiles SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?, badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?, - simplex_name = ?, - simplex_name_proof = ? + contact_domain = ?, contact_domain_proof = ? WHERE user_id = ? AND contact_profile_id = ? |] - ((displayName, fullName, shortDescr, image, contactLink, preferences, peerType, updatedAt) :. badgeToRow badge badgeVerified :. (claimName <$> simplexName, claimProof =<< simplexName) :. (userId, profileId)) + ((displayName, fullName, shortDescr, image, contactLink, preferences, peerType, updatedAt) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain :. (userId, profileId)) -- update only member profile fields (when member doesn't have associated contact - we can reset contactLink and prefs) updateMemberContactProfileReset_ :: DB.Connection -> UserId -> ProfileId -> Profile -> Maybe Bool -> IO () @@ -751,18 +750,17 @@ updateMemberContactProfileReset_ db userId profileId profile badgeVerified = do updateMemberContactProfileReset_' db userId profileId profile badgeVerified currentTs updateMemberContactProfileReset_' :: DB.Connection -> UserId -> ProfileId -> Profile -> Maybe Bool -> UTCTime -> IO () -updateMemberContactProfileReset_' db userId profileId Profile {displayName, fullName, shortDescr, image, simplexName, badge} badgeVerified updatedAt = +updateMemberContactProfileReset_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactDomain, badge} badgeVerified updatedAt = DB.execute db [sql| UPDATE contact_profiles SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?, badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?, - simplex_name = ?, - simplex_name_proof = ? + contact_domain = ?, contact_domain_proof = ? WHERE user_id = ? AND contact_profile_id = ? |] - ((displayName, fullName, shortDescr, image, updatedAt) :. badgeToRow badge badgeVerified :. (claimName <$> simplexName, claimProof =<< simplexName) :. (userId, profileId)) + ((displayName, fullName, shortDescr, image, updatedAt) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain :. (userId, profileId)) -- update only member profile fields (when member has associated contact - we keep contactLink and prefs) updateMemberContactProfile_ :: DB.Connection -> UserId -> ProfileId -> Profile -> Maybe Bool -> IO () @@ -771,18 +769,17 @@ updateMemberContactProfile_ db userId profileId profile badgeVerified = do updateMemberContactProfile_' db userId profileId profile badgeVerified currentTs updateMemberContactProfile_' :: DB.Connection -> UserId -> ProfileId -> Profile -> Maybe Bool -> UTCTime -> IO () -updateMemberContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, simplexName, badge} badgeVerified updatedAt = +updateMemberContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactDomain, badge} badgeVerified updatedAt = DB.execute db [sql| UPDATE contact_profiles SET display_name = ?, full_name = ?, short_descr = ?, image = ?, updated_at = ?, badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?, - simplex_name = ?, - simplex_name_proof = ? + contact_domain = ?, contact_domain_proof = ? WHERE user_id = ? AND contact_profile_id = ? |] - ((displayName, fullName, shortDescr, image, updatedAt) :. badgeToRow badge badgeVerified :. (claimName <$> simplexName, claimProof =<< simplexName) :. (userId, profileId)) + ((displayName, fullName, shortDescr, image, updatedAt) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain :. (userId, profileId)) updateContactLDN_ :: DB.Connection -> User -> Int64 -> ContactName -> ContactName -> UTCTime -> IO () updateContactLDN_ db user@User {userId} contactId displayName newName updatedAt = do @@ -805,7 +802,7 @@ getContactToConnect :: DB.Connection -> StoreCxt -> User -> ContactNameOrLink -> getContactToConnect db cxt user@User {userId} = \case CTLink sl -> first (`CCLink` Just sl) <$$> getContactViaShortLinkToConnect db cxt user sl CTName ni -> - liftIO (maybeFirstRow id $ DB.query db byNameQuery (userId, ni)) >>= \case + liftIO (maybeFirstRow id $ DB.query db byNameQuery (userId, nameDomain ni)) >>= \case Just (ctId :: Int64, Just (ACR cMode cReq), Just (sLnk :: ShortLinkContact)) | Just Refl <- testEquality cMode SCMContact -> Just . (CCLink cReq (Just sLnk),) <$> getContact db cxt user ctId _ -> pure Nothing @@ -814,7 +811,7 @@ getContactToConnect db cxt user@User {userId} = \case [sql| SELECT ct.contact_id, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect FROM contacts ct JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id - WHERE ct.user_id = ? AND cp.simplex_name = ? AND cp.simplex_name_verification = 1 AND ct.deleted = 0 + WHERE ct.user_id = ? AND cp.contact_domain = ? AND cp.contact_domain_verified = 1 AND ct.deleted = 0 |] getUserContacts :: DB.Connection -> StoreCxt -> User -> IO [Contact] @@ -857,7 +854,7 @@ contactRequestQuery = cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + p.contact_domain, p.contact_domain_proof, p.contact_domain_verified FROM contact_requests cr JOIN contact_profiles p USING (contact_profile_id) |] @@ -978,7 +975,7 @@ getContact_ db cxt user@User {userId} contactId deleted = do 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof, + cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified, -- Connection c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index a884d4b70c..7ed599007c 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -46,7 +46,7 @@ module Simplex.Chat.Store.Groups getGroupViaShortLinkToConnect, getGroupInfoByGroupLinkHash, updateGroupProfile, - setGroupNameVerified, + setGroupDomainVerified, updateGroupPreferences, updateGroupProfileFromMember, getGroupIdByName, @@ -222,7 +222,7 @@ import qualified Data.Text as T import Data.Time.Clock (NominalDiffTime, UTCTime (..), addUTCTime, getCurrentTime) import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Badges (BadgeRow, badgeToRow, verifyBadge_) -import Simplex.Chat.Names (NameClaimProof, claimName) +import Simplex.Chat.Names (SimplexDomainClaim (..)) import Simplex.Chat.Messages import Simplex.Chat.Operators import Simplex.Chat.Protocol hiding (Binary) @@ -233,7 +233,7 @@ import Simplex.Chat.Types.MemberRelations (IntroductionDirection (..), MemberRel import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Shared import Simplex.Chat.Types.UITheme -import Simplex.Messaging.Agent.Protocol (ConfirmationId, ConnId, CreatedConnLink (..), InvitationId, OwnerAuth (..), SimplexNameInfo, UserId) +import Simplex.Messaging.Agent.Protocol (ConfirmationId, ConnId, CreatedConnLink (..), InvitationId, OwnerAuth (..), SimplexNameInfo (..), UserId) import Simplex.Messaging.Agent.Store.AgentStore (firstRow, fromOnlyBI, maybeFirstRow) import qualified Simplex.FileTransfer.Description as FD import Simplex.Messaging.Encoding (smpDecode, smpEncode) @@ -254,11 +254,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..)) import Database.SQLite.Simple.QQ (sql) #endif -type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. ((Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. BadgeRow :. (Maybe SimplexNameInfo, Maybe BoolInt, Maybe NameClaimProof)) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact) +type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. ((Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact) toMaybeGroupMember :: UTCTime -> Int64 -> MaybeGroupMemberRow -> Maybe GroupMember -toMaybeGroupMember now userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. ((Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. badgeRow :. (profilesimplexName, profileContactNameVerification, profilesimplexNameProof)) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) = - Just $ toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. ((profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. badgeRow :. (profilesimplexName, profileContactNameVerification, profilesimplexNameProof)) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) +toMaybeGroupMember now userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. ((Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. badgeRow :. domainRow) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) = + Just $ toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. ((profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. badgeRow :. domainRow) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) toMaybeGroupMember _ _ _ = Nothing createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink @@ -398,7 +398,7 @@ createNewGroup db cxt user@User {userId} groupProfile incognitoProfile useRelays INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, group_type, group_link, public_group_id, - group_web_page, simplex_name, domain_web_page, allow_embedding, simplex_name_proof, + group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] @@ -447,7 +447,7 @@ createNewGroup db cxt user@User {userId} groupProfile incognitoProfile useRelays membersRequireAttention = 0, viaGroupLinkUri = Nothing, groupKeys, - simplexNameVerification = Nothing + groupDomainVerified = Nothing } -- | creates a new group record for the group the current user was invited to, or returns an existing one @@ -526,7 +526,7 @@ createGroupInvitation db cxt user@User {userId} contact@Contact {contactId, acti membersRequireAttention = 0, viaGroupLinkUri = Nothing, groupKeys = Nothing, - simplexNameVerification = Nothing + groupDomainVerified = Nothing }, groupMemberId ) @@ -643,7 +643,7 @@ deleteContactCardKeepConn db connId Contact {contactId, profile = LocalProfile { DB.execute db "DELETE FROM contact_profiles WHERE contact_profile_id = ?" (Only profileId) createPreparedGroup :: DB.Connection -> TVar ChaChaDRG -> StoreCxt -> User -> GroupProfile -> Bool -> CreatedLinkContact -> Maybe SharedMsgId -> Bool -> GroupMemberRole -> Maybe Int64 -> Maybe Bool -> ExceptT StoreError IO (GroupInfo, Maybe GroupMember) -createPreparedGroup db gVar cxt user@User {userId, userContactId} groupProfile business connLinkToConnect welcomeSharedMsgId useRelays userMemberRole publicMemberCount_ nameVerified = do +createPreparedGroup db gVar cxt user@User {userId, userContactId} groupProfile business connLinkToConnect welcomeSharedMsgId useRelays userMemberRole publicMemberCount_ verified_ = do currentTs <- liftIO getCurrentTime let prepared = Just (connLinkToConnect, welcomeSharedMsgId) (groupId, groupLDN) <- createGroup_ db userId groupProfile prepared Nothing useRelays Nothing publicMemberCount_ currentTs @@ -661,9 +661,9 @@ createPreparedGroup db gVar cxt user@User {userId, userContactId} groupProfile b hostMember_ <- forM hostMemberId_ $ getGroupMember db cxt user groupId forM_ hostMember_ $ \hostMember -> when business $ liftIO $ setGroupBusinessChatInfo groupId membership hostMember - liftIO $ mapM_ (setGroupNameVerified db user groupId) nameVerified g <- getGroupInfo db cxt user groupId - pure (g, hostMember_) + g' <- liftIO $ maybe (pure g) (setGroupDomainVerified db user g) verified_ + pure (g', hostMember_) where insertHost_ currentTs groupId groupLDN = do randHostId <- liftIO $ encodedRandomBytes gVar 12 @@ -905,7 +905,7 @@ createGroup_ db userId groupProfile prepared business useRelays relayOwnStatus p INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, group_type, group_link, public_group_id, - group_web_page, simplex_name, domain_web_page, allow_embedding, simplex_name_proof, + group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] @@ -1078,7 +1078,7 @@ getGroupToConnect :: DB.Connection -> StoreCxt -> User -> ContactNameOrLink -> E getGroupToConnect db cxt user@User {userId} = \case CTLink sl -> first (`CCLink` Just sl) <$$> getGroupViaShortLinkToConnect db cxt user sl CTName ni -> - liftIO (maybeFirstRow id $ DB.query db byNameQuery (userId, ni)) >>= \case + liftIO (maybeFirstRow id $ DB.query db byNameQuery (userId, nameDomain ni)) >>= \case Just (gId :: Int64, Just cReq, Just (sLnk :: ShortLinkContact)) -> Just . (CCLink cReq (Just sLnk),) <$> getGroupInfo db cxt user gId _ -> pure Nothing where @@ -1086,7 +1086,7 @@ getGroupToConnect db cxt user@User {userId} = \case [sql| SELECT g.group_id, g.conn_full_link_to_connect, g.conn_short_link_to_connect FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id - WHERE g.user_id = ? AND gp.simplex_name = ? AND g.simplex_name_verification = 1 + WHERE g.user_id = ? AND gp.group_domain = ? AND g.group_domain_verified = 1 |] getGroupMember :: DB.Connection -> StoreCxt -> User -> GroupId -> GroupMemberId -> ExceptT StoreError IO GroupMember @@ -1950,7 +1950,7 @@ getRelayPublishableGroups db User {userId, userContactId} = db [sql| SELECT g.group_id, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id JOIN group_members mu ON mu.group_id = g.group_id AND mu.contact_id = ? @@ -2655,12 +2655,12 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, pure $ Right $ (g' :: GroupInfo) {localDisplayName = ldn, groupProfile = p', fullGroupPreferences} where fullGroupPreferences = mergeGroupPreferences groupPreferences - groupClaim pg = claimName <$> (pg >>= publicGroupAccess >>= publicGroupClaim) + groupClaim pg = domain <$> (pg >>= publicGroupAccess >>= groupDomainClaim) claimChanged = groupClaim oldPublicGroup /= groupClaim publicGroup - g' = if claimChanged then (g :: GroupInfo) {simplexNameVerification = Nothing} else g + g' = if claimChanged then (g :: GroupInfo) {groupDomainVerified = Nothing} else g clearVerificationIfClaimChanged = when claimChanged $ - DB.execute db "UPDATE groups SET simplex_name_verification = NULL WHERE user_id = ? AND group_id = ?" (userId, groupId) + DB.execute db "UPDATE groups SET group_domain_verified = NULL WHERE user_id = ? AND group_id = ?" (userId, groupId) (groupType_, groupLink_) = case publicGroup of Just PublicGroupProfile {groupType, groupLink} -> (Just groupType, Just groupLink) Nothing -> (Nothing, Nothing) @@ -2671,7 +2671,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, UPDATE group_profiles SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, group_type = ?, group_link = ?, - group_web_page = ?, simplex_name = ?, domain_web_page = ?, allow_embedding = ?, simplex_name_proof = ?, + group_web_page = ?, group_domain = ?, domain_web_page = ?, allow_embedding = ?, group_domain_proof = ?, preferences = ?, member_admission = ?, updated_at = ? WHERE group_profile_id IN ( SELECT group_profile_id @@ -2687,12 +2687,13 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, (ldn, currentTs, userId, groupId) safeDeleteLDN db user localDisplayName -setGroupNameVerified :: DB.Connection -> User -> GroupId -> Bool -> IO () -setGroupNameVerified db User {userId} groupId verified = +setGroupDomainVerified :: DB.Connection -> User -> GroupInfo -> Bool -> IO GroupInfo +setGroupDomainVerified db User {userId} g@GroupInfo {groupId} verified = do DB.execute db - "UPDATE groups SET simplex_name_verification = ? WHERE user_id = ? AND group_id = ?" + "UPDATE groups SET group_domain_verified = ? WHERE user_id = ? AND group_id = ?" (BI verified, userId, groupId) + pure g {groupDomainVerified = Just verified} updateGroupPreferences :: DB.Connection -> User -> GroupInfo -> GroupPreferences -> IO GroupInfo updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p} ps = do @@ -2726,7 +2727,7 @@ updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName [sql| SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, gp.preferences, gp.member_admission FROM group_profiles gp JOIN groups g ON gp.group_profile_id = g.group_profile_id @@ -2776,9 +2777,9 @@ getGroupInfoViaUserTarget db cxt user@User {userId} target = fmap eitherToMaybe FROM user_contact_links ucl JOIN groups g ON g.group_id = ucl.group_id JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id - WHERE ucl.user_id = ? AND gp.simplex_name = ? + WHERE ucl.user_id = ? AND gp.group_domain = ? |] - (userId, ni) + (userId, nameDomain ni) toConnReqGroupId = \case -- cReq is "not null", group_id is nullable (cReq, Just (sLnk :: ShortLinkContact), Just groupId) -> Right (cReq, sLnk, groupId) @@ -3330,7 +3331,6 @@ setMemberContactStartedConnection db Contact {contactId} = do "UPDATE contacts SET grp_direct_inv_started_connection = ?, updated_at = ? WHERE contact_id = ?" (BI True, currentTs, contactId) --- | Updates the member profile (the profile writer persists simplex_name). updateMemberProfile :: DB.Connection -> StoreCxt -> User -> GroupMember -> Profile -> ExceptT StoreError IO GroupMember updateMemberProfile db cxt user@User {userId} m p' = do currentTs <- liftIO getCurrentTime @@ -3354,7 +3354,6 @@ updateMemberProfile db cxt user@User {userId} m p' = do safeDeleteLDN db user localDisplayName pure $ Right m {localDisplayName = ldn, memberProfile} --- | Updates the member's contact profile (the profile writer persists simplex_name). updateContactMemberProfile :: DB.Connection -> StoreCxt -> User -> GroupMember -> Contact -> Profile -> ExceptT StoreError IO (GroupMember, Contact) updateContactMemberProfile db cxt user@User {userId} m ct@Contact {contactId} p' = do currentTs <- liftIO getCurrentTime diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index d750737add..3ddc04253a 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -715,7 +715,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link FROM group_members m @@ -1139,7 +1139,7 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = do cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 contact_requests cr JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id @@ -3070,7 +3070,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, -- quoted ChatItem @@ -3079,14 +3079,14 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do rm.group_member_id, rm.group_id, rm.index_in_group, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category, rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id, rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, - rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.simplex_name, rp.simplex_name_verification, rp.simplex_name_proof, + rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.contact_domain, rp.contact_domain_proof, rp.contact_domain_verified, rm.created_at, rm.updated_at, rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link, -- deleted by GroupMember dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category, dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id, dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, - dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.simplex_name, dbp.simplex_name_verification, dbp.simplex_name_proof, + dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.contact_domain, dbp.contact_domain_proof, dbp.contact_domain_verified, dbm.created_at, dbm.updated_at, dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link FROM chat_items i diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20260603_simplex_name.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20260603_simplex_name.hs index d48964a747..41d43973be 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/M20260603_simplex_name.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20260603_simplex_name.hs @@ -9,13 +9,13 @@ import Text.RawString.QQ (r) m20260603_simplex_name :: Text m20260603_simplex_name = [r| -ALTER TABLE contact_profiles ADD COLUMN simplex_name TEXT; -ALTER TABLE contact_profiles ADD COLUMN simplex_name_verification SMALLINT; -ALTER TABLE contact_profiles ADD COLUMN simplex_name_proof TEXT; -ALTER TABLE groups ADD COLUMN simplex_name_verification SMALLINT; -ALTER TABLE group_profiles ADD COLUMN simplex_name TEXT; -ALTER TABLE group_profiles DROP COLUMN group_domain; -ALTER TABLE group_profiles ADD COLUMN simplex_name_proof TEXT; +ALTER TABLE contact_profiles ADD COLUMN contact_domain TEXT; +ALTER TABLE contact_profiles ADD COLUMN contact_domain_proof TEXT; +ALTER TABLE contact_profiles ADD COLUMN contact_domain_verified SMALLINT; + +ALTER TABLE group_profiles ADD COLUMN group_domain_proof TEXT; +ALTER TABLE groups ADD COLUMN group_domain_verified SMALLINT; + ALTER TABLE user_contact_links ADD COLUMN link_priv_sig_key BYTEA; ALTER TABLE server_operators ADD COLUMN smp_role_names SMALLINT NOT NULL DEFAULT 0; @@ -25,14 +25,14 @@ UPDATE server_operators SET smp_role_names = 1 WHERE server_operator_tag = 'simp down_m20260603_simplex_name :: Text down_m20260603_simplex_name = [r| -ALTER TABLE server_operators DROP COLUMN smp_role_names; +ALTER TABLE contact_profiles DROP COLUMN contact_domain; +ALTER TABLE contact_profiles DROP COLUMN contact_domain_proof; +ALTER TABLE contact_profiles DROP COLUMN contact_domain_verified; + +ALTER TABLE group_profiles DROP COLUMN group_domain_proof; +ALTER TABLE groups DROP COLUMN group_domain_verified; ALTER TABLE user_contact_links DROP COLUMN link_priv_sig_key; -ALTER TABLE group_profiles DROP COLUMN simplex_name_proof; -ALTER TABLE group_profiles ADD COLUMN group_domain TEXT; -ALTER TABLE group_profiles DROP COLUMN simplex_name; -ALTER TABLE groups DROP COLUMN simplex_name_verification; -ALTER TABLE contact_profiles DROP COLUMN simplex_name_proof; -ALTER TABLE contact_profiles DROP COLUMN simplex_name_verification; -ALTER TABLE contact_profiles DROP COLUMN simplex_name; + +ALTER TABLE server_operators DROP COLUMN smp_role_names; |] diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql index a1a695a7c6..2799a79b81 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql @@ -541,9 +541,9 @@ CREATE TABLE test_chat_schema.contact_profiles ( badge_master_key bytea, badge_signature bytea, badge_key_idx bigint, - simplex_name text, - simplex_name_verification smallint, - simplex_name_proof text + contact_domain text, + contact_domain_proof text, + contact_domain_verified smallint ); @@ -867,10 +867,10 @@ CREATE TABLE test_chat_schema.group_profiles ( group_link bytea, public_group_id bytea, group_web_page text, + group_domain text, domain_web_page bigint, allow_embedding bigint, - simplex_name text, - simplex_name_proof text + group_domain_proof text ); @@ -994,7 +994,7 @@ CREATE TABLE test_chat_schema.groups ( roster_sending_owner_gm_id bigint, roster_broker_ts timestamp with time zone, roster_blob bytea, - simplex_name_verification smallint + group_domain_verified smallint ); diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs index c641c5de57..522f966214 100644 --- a/src/Simplex/Chat/Store/Profiles.hs +++ b/src/Simplex/Chat/Store/Profiles.hs @@ -50,7 +50,7 @@ module Simplex.Chat.Store.Profiles getUserAddressConnection, deleteUserAddress, getUserAddress, - setUserSimplexName, + setUserSimplexDomain, getUserContactLinkById, getGroupLinkInfo, getUserContactLinkByConnReq, @@ -106,13 +106,13 @@ import Simplex.Chat.Operators import Simplex.Chat.Protocol import Simplex.Chat.Store.Direct import Simplex.Chat.Store.Shared -import Simplex.Chat.Names (claimName, mkSimplexNameClaim) +import Simplex.Chat.Names (claimDomain, mkDomainClaim) import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Shared import Simplex.Chat.Types.UITheme import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..)) -import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, ConnectionLink (..), CreatedConnLink (..), SimplexNameInfo, UserId) +import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, ConnectionLink (..), CreatedConnLink (..), SimplexDomain, SimplexNameInfo (..), UserId) import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow) import Simplex.Messaging.Agent.Store.DB (BoolInt (..)) import qualified Simplex.Messaging.Agent.Store.DB as DB @@ -386,14 +386,14 @@ setUserBadge db user@User {userId, profile = p@LocalProfile {profileId}} localBa DB.execute db "UPDATE users SET user_member_profile_updated_at = ? WHERE user_id = ?" (ts, userId) pure (user :: User) {profile = p {localBadge}, userMemberProfileUpdatedAt = Just ts} -setUserSimplexName :: DB.Connection -> User -> Maybe SimplexNameInfo -> IO User -setUserSimplexName db user@User {userId, profile = p@LocalProfile {profileId}} name_ = do +setUserSimplexDomain :: DB.Connection -> User -> Maybe SimplexDomain -> IO User +setUserSimplexDomain db user@User {userId, profile = p@LocalProfile {profileId}} domain_ = do ts <- getCurrentTime DB.execute db - "UPDATE contact_profiles SET simplex_name = ?, updated_at = ? WHERE user_id = ? AND contact_profile_id = ?" - (name_, ts, userId, profileId) - pure (user :: User) {profile = p {simplexName = mkSimplexNameClaim name_ Nothing}} + "UPDATE contact_profiles SET contact_domain = ?, updated_at = ? WHERE user_id = ? AND contact_profile_id = ?" + (domain_, ts, userId, profileId) + pure (user :: User) {profile = p {contactDomain = mkDomainClaim <$> domain_}} setUserProfileContactLink :: DB.Connection -> User -> Maybe UserContactLink -> IO User setUserProfileContactLink db user@User {userId, profile = p@LocalProfile {profileId}} ucl_ = do @@ -417,14 +417,14 @@ getUserContactProfiles db User {userId} = <$> DB.query db [sql| - SELECT display_name, full_name, short_descr, image, contact_link, chat_peer_type, simplex_name, preferences + SELECT display_name, full_name, short_descr, image, contact_link, chat_peer_type, contact_domain, preferences FROM contact_profiles WHERE user_id = ? |] (Only userId) where - toContactProfile :: (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe SimplexNameInfo, Maybe Preferences) -> Profile - toContactProfile (displayName, fullName, shortDescr, image, contactLink, peerType, simplexName, preferences) = Profile {displayName, fullName, shortDescr, image, contactLink, simplexName = mkSimplexNameClaim simplexName Nothing, peerType, preferences, badge = Nothing} + toContactProfile :: (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe SimplexDomain, Maybe Preferences) -> Profile + toContactProfile (displayName, fullName, shortDescr, image, contactLink, peerType, domain_, preferences) = Profile {displayName, fullName, shortDescr, image, contactLink, contactDomain = mkDomainClaim <$> domain_, peerType, preferences, badge = Nothing} createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> C.PrivateKeyEd25519 -> ExceptT StoreError IO () createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode linkPrivSigKey = @@ -558,12 +558,12 @@ getUserContactLinkByConnReq db User {userId} (cReqSchema1, cReqSchema2) = DB.query db (userContactLinkQuery <> " WHERE user_id = ? AND conn_req_contact IN (?,?)") (userId, cReqSchema1, cReqSchema2) getUserContactLinkViaTarget :: DB.Connection -> User -> ContactNameOrLink -> IO (Maybe UserContactLink) -getUserContactLinkViaTarget db User {userId, profile = LocalProfile {simplexName}} = \case +getUserContactLinkViaTarget db User {userId, profile = LocalProfile {contactDomain}} = \case CTLink shortLink -> maybeFirstRow toUserContactLink $ DB.query db (userContactLinkQuery <> " WHERE user_id = ? AND short_link_contact = ?") (userId, shortLink) CTName ni - | (claimName <$> simplexName) == Just ni -> + | (claimDomain <$> contactDomain) == Just (nameDomain ni) -> maybeFirstRow toUserContactLink $ DB.query db (userContactLinkQuery <> " WHERE user_id = ? AND group_id IS NULL AND short_link_contact IS NOT NULL") (Only userId) | otherwise -> pure Nothing diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20260603_simplex_name.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20260603_simplex_name.hs index ef40d5f3d5..e32b17872f 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/M20260603_simplex_name.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20260603_simplex_name.hs @@ -8,13 +8,13 @@ import Database.SQLite.Simple.QQ (sql) m20260603_simplex_name :: Query m20260603_simplex_name = [sql| -ALTER TABLE contact_profiles ADD COLUMN simplex_name TEXT; -ALTER TABLE contact_profiles ADD COLUMN simplex_name_verification INTEGER; -ALTER TABLE contact_profiles ADD COLUMN simplex_name_proof TEXT; -ALTER TABLE groups ADD COLUMN simplex_name_verification INTEGER; -ALTER TABLE group_profiles ADD COLUMN simplex_name TEXT; -ALTER TABLE group_profiles DROP COLUMN group_domain; -ALTER TABLE group_profiles ADD COLUMN simplex_name_proof TEXT; +ALTER TABLE contact_profiles ADD COLUMN contact_domain TEXT; +ALTER TABLE contact_profiles ADD COLUMN contact_domain_proof TEXT; +ALTER TABLE contact_profiles ADD COLUMN contact_domain_verified INTEGER; + +ALTER TABLE group_profiles ADD COLUMN group_domain_proof TEXT; +ALTER TABLE groups ADD COLUMN group_domain_verified INTEGER; + ALTER TABLE user_contact_links ADD COLUMN link_priv_sig_key BLOB; ALTER TABLE server_operators ADD COLUMN smp_role_names INTEGER NOT NULL DEFAULT 0; @@ -24,14 +24,14 @@ UPDATE server_operators SET smp_role_names = 1 WHERE server_operator_tag = 'simp down_m20260603_simplex_name :: Query down_m20260603_simplex_name = [sql| -ALTER TABLE server_operators DROP COLUMN smp_role_names; +ALTER TABLE contact_profiles DROP COLUMN contact_domain; +ALTER TABLE contact_profiles DROP COLUMN contact_domain_proof; +ALTER TABLE contact_profiles DROP COLUMN contact_domain_verified; + +ALTER TABLE group_profiles DROP COLUMN group_domain_proof; +ALTER TABLE groups DROP COLUMN group_domain_verified; ALTER TABLE user_contact_links DROP COLUMN link_priv_sig_key; -ALTER TABLE group_profiles DROP COLUMN simplex_name_proof; -ALTER TABLE group_profiles ADD COLUMN group_domain TEXT; -ALTER TABLE group_profiles DROP COLUMN simplex_name; -ALTER TABLE groups DROP COLUMN simplex_name_verification; -ALTER TABLE contact_profiles DROP COLUMN simplex_name_proof; -ALTER TABLE contact_profiles DROP COLUMN simplex_name_verification; -ALTER TABLE contact_profiles DROP COLUMN simplex_name; + +ALTER TABLE server_operators DROP COLUMN smp_role_names; |] diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt index 90ad383123..af2fb0162d 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -125,7 +125,7 @@ Query: 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, 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof, + 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, -- Connection c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -144,27 +144,26 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, g.business_chat, g.business_member_id, g.customer_member_id, g.use_relays, g.relay_own_status, - g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, + g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, g.group_domain_verified, g.root_priv_key, g.root_pub_key, g.member_priv_key, - g.simplex_name_verification, -- GroupInfo {membership} mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, -- GroupInfo {membership = GroupMember {memberProfile}} pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, - pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.simplex_name, pu.simplex_name_verification, pu.simplex_name_proof, + pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, -- from GroupMember m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link FROM group_members m @@ -402,7 +401,7 @@ Query: cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 contact_requests cr JOIN contact_profiles p USING (contact_profile_id) WHERE cr.user_id = ? @@ -465,7 +464,7 @@ Query: FROM user_contact_links ucl JOIN groups g ON g.group_id = ucl.group_id JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id - WHERE ucl.user_id = ? AND gp.simplex_name = ? + WHERE ucl.user_id = ? AND gp.group_domain = ? Plan: SEARCH ucl USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?) @@ -709,7 +708,7 @@ Query: 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 WHERE c.user_id = ? AND c.contact_id = ? AND c.contact_status = ? AND c.deleted = 0 @@ -1008,7 +1007,7 @@ SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_next (group_id=? A Query: SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, gp.preferences, gp.member_admission FROM group_profiles gp JOIN groups g ON gp.group_profile_id = g.group_profile_id @@ -1052,7 +1051,7 @@ Query: m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link FROM group_members m @@ -1283,7 +1282,7 @@ Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, group_type, group_link, public_group_id, - group_web_page, simplex_name, domain_web_page, allow_embedding, simplex_name_proof, + group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) @@ -1361,7 +1360,7 @@ Query: m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, -- quoted ChatItem @@ -1370,14 +1369,14 @@ Query: rm.group_member_id, rm.group_id, rm.index_in_group, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category, rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id, rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, - rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.simplex_name, rp.simplex_name_verification, rp.simplex_name_proof, + rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.contact_domain, rp.contact_domain_proof, rp.contact_domain_verified, rm.created_at, rm.updated_at, rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link, -- deleted by GroupMember dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category, dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id, dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, - dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.simplex_name, dbp.simplex_name_verification, dbp.simplex_name_proof, + dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.contact_domain, dbp.contact_domain_proof, dbp.contact_domain_verified, dbm.created_at, dbm.updated_at, dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link FROM chat_items i @@ -1431,7 +1430,7 @@ Query: 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof, + cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified, -- Connection c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -1825,7 +1824,7 @@ Query: UPDATE group_profiles SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, group_type = ?, group_link = ?, - group_web_page = ?, simplex_name = ?, domain_web_page = ?, allow_embedding = ?, simplex_name_proof = ?, + group_web_page = ?, group_domain = ?, domain_web_page = ?, allow_embedding = ?, group_domain_proof = ?, preferences = ?, member_admission = ?, updated_at = ? WHERE group_profile_id IN ( SELECT group_profile_id @@ -2012,7 +2011,7 @@ Query: 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof, + cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified, -- Connection c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -2098,7 +2097,7 @@ Query: cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 contact_requests cr JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id @@ -2128,7 +2127,7 @@ Query: cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 contact_requests cr JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id @@ -2158,7 +2157,7 @@ Query: cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + 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 contact_requests cr JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id @@ -3689,7 +3688,7 @@ SEARCH connections USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, -- , ct.user_preferences - 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof + 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 FROM contact_profiles cp WHERE cp.user_id = ? AND cp.contact_profile_id = ? @@ -3712,7 +3711,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT ct.contact_id, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect FROM contacts ct JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id - WHERE ct.user_id = ? AND cp.simplex_name = ? AND cp.simplex_name_verification = 1 AND ct.deleted = 0 + WHERE ct.user_id = ? AND cp.contact_domain = ? AND cp.contact_domain_verified = 1 AND ct.deleted = 0 Plan: SEARCH ct USING INDEX idx_contacts_chat_ts (user_id=?) @@ -3741,7 +3740,7 @@ SEARCH f USING PRIMARY KEY (file_id=?) SEARCH d USING INTEGER PRIMARY KEY (rowid=?) Query: - SELECT display_name, full_name, short_descr, image, contact_link, chat_peer_type, simplex_name, preferences + SELECT display_name, full_name, short_descr, image, contact_link, chat_peer_type, contact_domain, preferences FROM contact_profiles WHERE user_id = ? @@ -3796,7 +3795,7 @@ SEARCH files USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT g.group_id, g.conn_full_link_to_connect, g.conn_short_link_to_connect FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id - WHERE g.user_id = ? AND gp.simplex_name = ? AND g.simplex_name_verification = 1 + WHERE g.user_id = ? AND gp.group_domain = ? AND g.group_domain_verified = 1 Plan: SEARCH g USING INDEX sqlite_autoindex_groups_2 (user_id=?) @@ -3804,7 +3803,7 @@ SEARCH gp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT g.group_id, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id JOIN group_members mu ON mu.group_id = g.group_id AND mu.contact_id = ? @@ -5145,8 +5144,7 @@ Query: UPDATE contact_profiles SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?, badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?, - simplex_name = ?, - simplex_name_proof = ? + contact_domain = ?, contact_domain_proof = ? WHERE user_id = ? AND contact_profile_id = ? Plan: @@ -5156,8 +5154,7 @@ Query: UPDATE contact_profiles SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?, badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?, - simplex_name = ?, - simplex_name_proof = ? + contact_domain = ?, contact_domain_proof = ? WHERE user_id = ? AND contact_profile_id = ? Plan: @@ -5167,8 +5164,7 @@ Query: UPDATE contact_profiles SET display_name = ?, full_name = ?, short_descr = ?, image = ?, updated_at = ?, badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?, - simplex_name = ?, - simplex_name_proof = ? + contact_domain = ?, contact_domain_proof = ? WHERE user_id = ? AND contact_profile_id = ? Plan: @@ -5183,7 +5179,7 @@ Plan: SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?) Query: - UPDATE contact_profiles SET simplex_name_verification = ? + UPDATE contact_profiles SET contact_domain_verified = ? WHERE contact_profile_id IN (SELECT contact_profile_id FROM contacts WHERE user_id = ? AND contact_id = ?) Plan: @@ -5517,20 +5513,19 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, g.business_chat, g.business_member_id, g.customer_member_id, g.use_relays, g.relay_own_status, - g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, + g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, g.group_domain_verified, g.root_priv_key, g.root_pub_key, g.member_priv_key, - g.simplex_name_verification, -- GroupMember - membership mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, - pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.simplex_name, pu.simplex_name_verification, pu.simplex_name_proof, + pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link @@ -5556,20 +5551,19 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, g.business_chat, g.business_member_id, g.customer_member_id, g.use_relays, g.relay_own_status, - g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, + g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, g.group_domain_verified, g.root_priv_key, g.root_pub_key, g.member_priv_key, - g.simplex_name_verification, -- GroupMember - membership mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, - pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.simplex_name, pu.simplex_name_verification, pu.simplex_name_proof, + pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link @@ -5588,20 +5582,19 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, g.business_chat, g.business_member_id, g.customer_member_id, g.use_relays, g.relay_own_status, - g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, + g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, g.group_domain_verified, g.root_priv_key, g.root_pub_key, g.member_priv_key, - g.simplex_name_verification, -- GroupMember - membership mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, - pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.simplex_name, pu.simplex_name_verification, pu.simplex_name_proof, + pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link @@ -5625,7 +5618,7 @@ Query: cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + p.contact_domain, p.contact_domain_proof, p.contact_domain_verified FROM contact_requests cr JOIN contact_profiles p USING (contact_profile_id) WHERE cr.business_group_id = ? @@ -5642,7 +5635,7 @@ Query: cr.created_at, cr.updated_at, cr.peer_chat_min_version, cr.peer_chat_max_version, 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof + p.contact_domain, p.contact_domain_proof, p.contact_domain_verified FROM contact_requests cr JOIN contact_profiles p USING (contact_profile_id) WHERE cr.user_id = ? AND cr.contact_request_id = ? @@ -5654,7 +5647,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5682,7 +5675,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5703,7 +5696,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5723,7 +5716,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5743,7 +5736,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5763,7 +5756,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5783,7 +5776,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5803,7 +5796,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5823,7 +5816,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5843,7 +5836,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5863,7 +5856,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5883,7 +5876,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5903,7 +5896,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5923,7 +5916,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -5943,7 +5936,7 @@ Query: SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -6144,7 +6137,7 @@ SEARCH server_operators USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6157,7 +6150,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6171,7 +6164,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6185,7 +6178,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6200,7 +6193,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6214,7 +6207,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6228,7 +6221,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6242,7 +6235,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6256,7 +6249,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6269,7 +6262,7 @@ SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id @@ -6902,7 +6895,7 @@ Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, image Plan: SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?) -Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, simplex_name, simplex_name_proof) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) +Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, contact_domain, contact_domain_proof) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) Plan: SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?) @@ -7480,14 +7473,14 @@ Query: UPDATE connections_sync SET should_sync = 1 WHERE connections_sync_id = 1 Plan: SEARCH connections_sync USING INTEGER PRIMARY KEY (rowid=?) +Query: UPDATE contact_profiles SET contact_domain = ?, updated_at = ? WHERE user_id = ? AND contact_profile_id = ? +Plan: +SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?) + Query: UPDATE contact_profiles SET image = ? WHERE display_name = ? Plan: SEARCH contact_profiles USING INDEX contact_profiles_index (display_name=?) -Query: UPDATE contact_profiles SET simplex_name = ?, updated_at = ? WHERE user_id = ? AND contact_profile_id = ? -Plan: -SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?) - Query: UPDATE contact_requests SET business_group_id = ? WHERE contact_request_id = ? Plan: SEARCH contact_requests USING INTEGER PRIMARY KEY (rowid=?) @@ -7688,6 +7681,14 @@ Query: UPDATE groups SET enable_ntfs = ?, send_rcpts = ?, favorite = ? WHERE use Plan: SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) +Query: UPDATE groups SET group_domain_verified = ? WHERE user_id = ? AND group_id = ? +Plan: +SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) + +Query: UPDATE groups SET group_domain_verified = NULL WHERE user_id = ? AND group_id = ? +Plan: +SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) + Query: UPDATE groups SET local_alias = ?, updated_at = ? WHERE user_id = ? AND group_id = ? Plan: SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) @@ -7732,14 +7733,6 @@ Query: UPDATE groups SET send_rcpts = NULL Plan: SCAN groups -Query: UPDATE groups SET simplex_name_verification = ? WHERE user_id = ? AND group_id = ? -Plan: -SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) - -Query: UPDATE groups SET simplex_name_verification = NULL WHERE user_id = ? AND group_id = ? -Plan: -SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) - Query: UPDATE groups SET ui_themes = ?, updated_at = ? WHERE user_id = ? AND group_id = ? Plan: SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql index 0c039a30c2..954660dce5 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql @@ -29,9 +29,9 @@ CREATE TABLE contact_profiles( badge_master_key BLOB, badge_signature BLOB, badge_key_idx INTEGER, - simplex_name TEXT, - simplex_name_verification INTEGER, - simplex_name_proof TEXT + contact_domain TEXT, + contact_domain_proof TEXT, + contact_domain_verified INTEGER ) STRICT; CREATE TABLE users( user_id INTEGER PRIMARY KEY, @@ -140,10 +140,10 @@ CREATE TABLE group_profiles( group_link BLOB, public_group_id BLOB, group_web_page TEXT, + group_domain TEXT, domain_web_page INTEGER, allow_embedding INTEGER, - simplex_name TEXT, - simplex_name_proof TEXT + group_domain_proof TEXT ) STRICT; CREATE TABLE groups( group_id INTEGER PRIMARY KEY, -- local group ID @@ -204,7 +204,7 @@ CREATE TABLE groups( roster_sending_owner_gm_id INTEGER, roster_broker_ts TEXT, roster_blob BLOB, - simplex_name_verification INTEGER, -- received + group_domain_verified INTEGER, -- received FOREIGN KEY(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name) ON DELETE CASCADE diff --git a/src/Simplex/Chat/Store/Shared.hs b/src/Simplex/Chat/Store/Shared.hs index b04a4a59bb..0d01b2ac1d 100644 --- a/src/Simplex/Chat/Store/Shared.hs +++ b/src/Simplex/Chat/Store/Shared.hs @@ -33,14 +33,14 @@ import qualified Data.Text as T import Data.Time.Clock (UTCTime (..), getCurrentTime) import Data.Type.Equality import Simplex.Chat.Badges (BadgeRow, badgeToRow, rowToBadge, verifyBadge_) -import Simplex.Chat.Names (NameClaimProof, claimName, claimProof, mkSimplexNameClaim) +import Simplex.Chat.Names (SimplexDomainProof, SimplexDomainClaim (..), claimDomain) import Simplex.Chat.Messages import Simplex.Chat.Remote.Types import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Shared import Simplex.Chat.Types.UITheme -import Simplex.Messaging.Agent.Protocol (AConnShortLink (..), AConnectionRequestUri (..), ACreatedConnLink (..), ConnId, ConnShortLink, ConnectionRequestUri, CreatedConnLink (..), SimplexNameInfo, UserId, connMode) +import Simplex.Messaging.Agent.Protocol (AConnShortLink (..), AConnectionRequestUri (..), ACreatedConnLink (..), ConnId, ConnShortLink, ConnectionRequestUri, CreatedConnLink (..), SimplexDomain, UserId, connMode) import Simplex.Messaging.Agent.Store (AnyStoreError (..)) import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow) import Simplex.Messaging.Agent.Store.Common (withSavepoint) @@ -49,6 +49,7 @@ import qualified Simplex.Messaging.Agent.Store.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..)) import qualified Simplex.Messaging.Crypto.Ratchet as CR +import Simplex.Messaging.Encoding.String (StrJSON (..)) import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON) import Simplex.Messaging.Protocol (SubscriptionMode (..)) import Simplex.Messaging.Util (AnyError (..)) @@ -414,13 +415,13 @@ createContact db cxt user profile = do void $ createContact_ db cxt user profile emptyChatPrefs Nothing "" currentTs createContact_ :: DB.Connection -> StoreCxt -> User -> Profile -> Preferences -> Maybe (ACreatedConnLink, Maybe SharedMsgId) -> LocalAlias -> UTCTime -> ExceptT StoreError IO ContactId -createContact_ db cxt User {userId} Profile {displayName, fullName, shortDescr, image, contactLink, simplexName, peerType, badge, preferences} ctUserPreferences prepared localAlias currentTs = +createContact_ db cxt User {userId} Profile {displayName, fullName, shortDescr, image, contactLink, contactDomain, peerType, badge, preferences} ctUserPreferences prepared localAlias currentTs = ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do badgeVerified <- verifyBadge_ (badgeKeys cxt) badge DB.execute db - "INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, simplex_name, simplex_name_proof) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" - ((displayName, fullName, shortDescr, image, contactLink, peerType) :. (userId, localAlias, preferences, currentTs, currentTs) :. badgeToRow badge badgeVerified :. (claimName <$> simplexName, claimProof =<< simplexName)) + "INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, contact_domain, contact_domain_proof) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" + ((displayName, fullName, shortDescr, image, contactLink, peerType) :. (userId, localAlias, preferences, currentTs, currentTs) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain) profileId <- insertedRowId db DB.execute db @@ -487,13 +488,15 @@ 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 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 :. (Maybe SimplexNameInfo, Maybe BoolInt, Maybe NameClaimProof) +type ContactRow' = (ProfileId, ContactName, ContactName, 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 = 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, 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 :. (cpsimplexName, cpContactNameVerification, cpsimplexNameProof)) :. connRow) = - let profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = mkSimplexNameClaim cpsimplexName cpsimplexNameProof, simplexNameVerification = unBI <$> cpContactNameVerification, peerType, localBadge = rowToBadge now badgeRow, preferences, localAlias} +toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, 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) = + let profile = LocalProfile {profileId, displayName, fullName, shortDescr, 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 @@ -502,6 +505,15 @@ toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName 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} +rowToContactDomain :: ContactDomainRow -> Maybe SimplexDomainClaim +rowToContactDomain (domain_, domainProof_, _) = (`SimplexDomainClaim` domainProof_) . StrJSON <$> domain_ + +rowToDomainVerified :: ContactDomainRow -> Maybe Bool +rowToDomainVerified (_, _, domainVerification_) = unBI <$> domainVerification_ + +contactDomainToRow :: Maybe SimplexDomainClaim -> (Maybe SimplexDomain, Maybe SimplexDomainProof) +contactDomainToRow d = (claimDomain <$> d, proof =<< d) + toPreparedContact :: PreparedContactRow -> Maybe PreparedContact toPreparedContact (connFullLink, connShortLink, welcomeSharedMsgId, requestSharedMsgId) = (\cl@(ACCL m _) -> PreparedContact {connLinkToConnect = cl, uiConnLinkType = connMode m, welcomeSharedMsgId, requestSharedMsgId}) @@ -526,17 +538,17 @@ getProfileById db userId profileId = do db [sql| SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, -- , ct.user_preferences - 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.simplex_name, cp.simplex_name_verification, cp.simplex_name_proof + 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 FROM contact_profiles cp WHERE cp.user_id = ? AND cp.contact_profile_id = ? |] (userId, profileId) -type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat) :. BadgeRow :. (Maybe SimplexNameInfo, Maybe BoolInt, Maybe NameClaimProof) +type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, 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, image, contactLink, peerType, localAlias) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer) :. badgeRow :. (simplexName, simplexNameVerification, simplexNameProof)) = do - let profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = mkSimplexNameClaim simplexName simplexNameProof, simplexNameVerification = unBI <$> simplexNameVerification, peerType, preferences, localBadge = rowToBadge now badgeRow, localAlias} +toContactRequest now ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer) :. badgeRow :. domainRow) = do + let profile = LocalProfile {profileId, displayName, fullName, shortDescr, 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} @@ -545,17 +557,17 @@ userQuery = [sql| SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.chat_peer_type, ucp.preferences, u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.auto_accept_member_contacts, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.is_user_chat_relay, u.client_service, u.ui_themes, - ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.simplex_name, ucp.simplex_name_verification, ucp.simplex_name_proof + ucp.badge_proof, ucp.badge_pres_header, ucp.badge_expiry, ucp.badge_type, ucp.badge_verified, ucp.badge_extra, ucp.badge_master_key, ucp.badge_signature, ucp.badge_key_idx, ucp.contact_domain, ucp.contact_domain_proof, ucp.contact_domain_verified FROM users u JOIN contacts uct ON uct.contact_id = u.contact_id JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id |] -toUser :: UTCTime -> (UserId, UserId, ContactId, ProfileId, BoolInt, Int64) :. (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe Preferences) :. (BoolInt, BoolInt, BoolInt, BoolInt, Maybe B64UrlByteString, Maybe B64UrlByteString, Maybe UTCTime, BoolInt, BoolInt, Maybe UIThemeEntityOverrides) :. BadgeRow :. (Maybe SimplexNameInfo, Maybe BoolInt, Maybe NameClaimProof) -> User -toUser now ((userId, auId, userContactId, profileId, BI activeUser, activeOrder) :. (displayName, fullName, shortDescr, image, contactLink, peerType, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, BI autoAcceptMemberContacts, viewPwdHash_, viewPwdSalt_, userMemberProfileUpdatedAt, BI userChatRelay, BI clientService, uiThemes) :. badgeRow :. (simplexName, simplexNameVerification, simplexNameProof)) = +toUser :: UTCTime -> (UserId, UserId, ContactId, ProfileId, BoolInt, Int64) :. (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe Preferences) :. (BoolInt, BoolInt, BoolInt, BoolInt, Maybe B64UrlByteString, Maybe B64UrlByteString, Maybe UTCTime, BoolInt, BoolInt, Maybe UIThemeEntityOverrides) :. BadgeRow :. ContactDomainRow -> User +toUser now ((userId, auId, userContactId, profileId, BI activeUser, activeOrder) :. (displayName, fullName, shortDescr, image, contactLink, peerType, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, BI autoAcceptMemberContacts, viewPwdHash_, viewPwdSalt_, userMemberProfileUpdatedAt, BI userChatRelay, BI clientService, uiThemes) :. badgeRow :. domainRow) = User {userId, agentUserId = AgentUserId auId, userContactId, localDisplayName = displayName, profile, activeUser, activeOrder, fullPreferences, showNtfs, sendRcptsContacts, sendRcptsSmallGroups, autoAcceptMemberContacts, viewPwdHash, userMemberProfileUpdatedAt, userChatRelay = BoolDef userChatRelay, clientService = BoolDef clientService, uiThemes} where - profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = mkSimplexNameClaim simplexName simplexNameProof, simplexNameVerification = unBI <$> simplexNameVerification, peerType, localBadge = rowToBadge now badgeRow, preferences = userPreferences, localAlias = ""} + profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, preferences = userPreferences, localAlias = ""} fullPreferences = fullPreferences' userPreferences viewPwdHash = UserPwdHash <$> viewPwdHash_ <*> viewPwdSalt_ @@ -671,16 +683,16 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member type GroupKeysRow = (Maybe C.PrivateKeyEd25519, Maybe C.PublicKeyEd25519, Maybe C.PrivateKeyEd25519) -type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe VersionRoster, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupKeysRow :. Only (Maybe BoolInt) :. GroupMemberRow +type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe VersionRoster, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact, Maybe BoolInt) :. GroupKeysRow :. GroupMemberRow -type PublicGroupAccessRow = (Maybe Text, Maybe SimplexNameInfo, Maybe BoolInt, Maybe BoolInt, Maybe NameClaimProof) +type PublicGroupAccessRow = (Maybe Text, Maybe SimplexDomain, Maybe BoolInt, Maybe BoolInt, Maybe SimplexDomainProof) type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact) -type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences) :. BadgeRow :. (Maybe SimplexNameInfo, Maybe BoolInt, Maybe NameClaimProof) +type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow toGroupInfo :: UTCTime -> StoreCxt -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo -toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, rosterVersion, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. groupKeysRow :. Only simplexNameVerification :. userMemberRow) = +toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, rosterVersion, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri, groupDomainVerified) :. groupKeysRow :. userMemberRow) = let membership = (toGroupMember now userContactId userMemberRow) {memberChatVRange = vr cxt} chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite} fullGroupPreferences = mergeGroupPreferences groupPreferences @@ -690,7 +702,7 @@ toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayN businessChat = toBusinessChatInfo businessRow preparedGroup = toPreparedGroup preparedGroupRow groupSummary = GroupSummary {currentMembers, publicMemberCount} - in GroupInfo {groupId, useRelays = BoolDef useRelays, relayOwnStatus, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, groupSummary, rosterVersion, customData, membersRequireAttention, viaGroupLinkUri, groupKeys, simplexNameVerification = unBI <$> simplexNameVerification} + in GroupInfo {groupId, useRelays = BoolDef useRelays, relayOwnStatus, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, groupSummary, rosterVersion, customData, membersRequireAttention, viaGroupLinkUri, groupKeys, groupDomainVerified = unBI <$> groupDomainVerified} toPreparedGroup :: PreparedGroupRow -> Maybe PreparedGroup toPreparedGroup = \case @@ -705,14 +717,14 @@ toPublicGroupProfile _ _ _ _ = Nothing publicGroupAccessRow :: Maybe PublicGroupProfile -> PublicGroupAccessRow publicGroupAccessRow pgp = case pgp >>= publicGroupAccess of - Just PublicGroupAccess {groupWebPage, simplexName, domainWebPage, allowEmbedding} -> - (groupWebPage, claimName <$> simplexName, Just (BI domainWebPage), Just (BI allowEmbedding), claimProof =<< simplexName) + Just PublicGroupAccess {groupWebPage, groupDomainClaim, domainWebPage, allowEmbedding} -> + (groupWebPage, claimDomain <$> groupDomainClaim, Just (BI domainWebPage), Just (BI allowEmbedding), proof =<< groupDomainClaim) Nothing -> (Nothing, Nothing, Nothing, Nothing, Nothing) toPublicGroupAccess :: PublicGroupAccessRow -> Maybe PublicGroupAccess -toPublicGroupAccess (groupWebPage, simplexName, domainWebPage_, allowEmbedding_, simplexNameProof) - | isJust groupWebPage || isJust simplexName || domainWebPage || allowEmbedding = - Just PublicGroupAccess {groupWebPage, simplexName = mkSimplexNameClaim simplexName simplexNameProof, domainWebPage, allowEmbedding} +toPublicGroupAccess (groupWebPage, groupDomain_, domainWebPage_, allowEmbedding_, groupDomainProof_) + | isJust groupWebPage || isJust groupDomain_ || domainWebPage || allowEmbedding = + Just PublicGroupAccess {groupWebPage, groupDomainClaim = (`SimplexDomainClaim` groupDomainProof_) . StrJSON <$> groupDomain_, domainWebPage, allowEmbedding} | otherwise = Nothing where domainWebPage = maybe False unBI domainWebPage_ @@ -751,7 +763,7 @@ groupMemberQuery = SELECT m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, - 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.simplex_name, p.simplex_name_verification, p.simplex_name_proof, + 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, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, @@ -768,8 +780,8 @@ toContactMember now cxt User {userContactId} (memberRow :. connRow) = (toGroupMember now userContactId memberRow) {activeConn = toMaybeConnection cxt connRow} rowToLocalProfile :: UTCTime -> ProfileRow -> LocalProfile -rowToLocalProfile now ((profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, preferences) :. badgeRow :. (simplexName, simplexNameVerification, simplexNameProof)) = - LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = mkSimplexNameClaim simplexName simplexNameProof, simplexNameVerification = unBI <$> simplexNameVerification, peerType, localBadge = rowToBadge now badgeRow, localAlias, preferences} +rowToLocalProfile now ((profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, preferences) :. badgeRow :. domainRow) = + LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, localAlias, preferences} toBusinessChatInfo :: BusinessChatInfoRow -> Maybe BusinessChatInfo toBusinessChatInfo (Just chatType, Just businessId, Just customerId) = Just BusinessChatInfo {chatType, businessId, customerId} @@ -784,20 +796,19 @@ groupInfoQueryFields = SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, - gp.group_web_page, gp.simplex_name, gp.domain_web_page, gp.allow_embedding, gp.simplex_name_proof, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, g.business_chat, g.business_member_id, g.customer_member_id, g.use_relays, g.relay_own_status, - g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, + g.ui_themes, g.summary_current_members_count, g.public_member_count, g.roster_version, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri, g.group_domain_verified, g.root_priv_key, g.root_pub_key, g.member_priv_key, - g.simplex_name_verification, -- GroupMember - membership mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, - pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.simplex_name, pu.simplex_name_verification, pu.simplex_name_proof, + pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link |] diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index f4fdbd578d..25f8f378d2 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -53,7 +53,7 @@ import Data.Type.Equality (testEquality, (:~:) (Refl)) import Data.Typeable (Typeable) import Data.Word (Word16) import Simplex.Chat.Badges (BadgeInfo (..), BadgeProof (..), BadgeStatus (..), LocalBadge (..), localBadgeInfo, localBadgeStatus, mkBadgeStatus, verifyBadge) -import Simplex.Chat.Names (SimplexNameClaim, setClaimProof) +import Simplex.Chat.Names (SimplexDomainClaim (..)) import Simplex.Messaging.Crypto.BBS (BBSPublicKey) import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Shared @@ -499,7 +499,7 @@ data GroupInfo = GroupInfo membersRequireAttention :: Int, viaGroupLinkUri :: Maybe ConnReqContact, groupKeys :: Maybe GroupKeys, - simplexNameVerification :: Maybe Bool + groupDomainVerified :: Maybe Bool } deriving (Eq, Show) @@ -699,7 +699,7 @@ data Profile = Profile preferences :: Maybe Preferences, peerType :: Maybe ChatPeerType, badge :: Maybe BadgeProof, - simplexName :: Maybe SimplexNameClaim + contactDomain :: Maybe SimplexDomainClaim -- fields that should not be read into this data type to prevent sending them as part of profile to contacts: -- - contact_profile_id -- - incognito @@ -732,14 +732,14 @@ instance TextEncoding ChatPeerType where profileFromName :: ContactName -> Profile profileFromName displayName = - Profile {displayName, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing, peerType = Nothing, badge = Nothing, simplexName = Nothing} + Profile {displayName, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing, peerType = Nothing, badge = Nothing, contactDomain = Nothing} -- check if profiles match ignoring preferences profilesMatch :: LocalProfile -> LocalProfile -> Bool profilesMatch - LocalProfile {displayName = n1, fullName = fn1, image = i1} - LocalProfile {displayName = n2, fullName = fn2, image = i2} = - n1 == n2 && fn1 == fn2 && i1 == i2 + LocalProfile {displayName = n1, fullName = fn1, image = i1, shortDescr = d1} + LocalProfile {displayName = n2, fullName = fn2, image = i2, shortDescr = d2} = + n1 == n2 && fn1 == fn2 && i1 == i2 && d1 == d2 -- equal for profile-update detection: badge proofs are re-generated for every presentation, -- so compare badges by disclosed info (not proof bytes) - a re-presentation of the same badge is a no-op @@ -747,7 +747,7 @@ sameProfileContent :: Profile -> Profile -> Bool sameProfileContent p@Profile {badge = b} p'@Profile {badge = b'} = clearProofs p == clearProofs p' && (proofInfo <$> b) == (proofInfo <$> b') where - clearProofs pr@Profile {simplexName} = pr {badge = Nothing, simplexName = setClaimProof Nothing <$> simplexName} + clearProofs pr@Profile {contactDomain} = pr {badge = Nothing, contactDomain = (\d -> d {proof = Nothing} :: SimplexDomainClaim) <$> contactDomain} proofInfo :: BadgeProof -> BadgeInfo proofInfo (BadgeProof _ _ _ info) = info @@ -784,8 +784,8 @@ data LocalProfile = LocalProfile peerType :: Maybe ChatPeerType, localBadge :: Maybe LocalBadge, localAlias :: LocalAlias, - simplexName :: Maybe SimplexNameClaim, - simplexNameVerification :: Maybe Bool + contactDomain :: Maybe SimplexDomainClaim, + contactDomainVerified :: Maybe Bool } deriving (Eq, Show) @@ -793,15 +793,15 @@ localProfileId :: LocalProfile -> ProfileId localProfileId LocalProfile {profileId} = profileId toLocalProfile :: ProfileId -> Profile -> LocalAlias -> UTCTime -> Maybe Bool -> Maybe Bool -> LocalProfile -toLocalProfile profileId Profile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, badge, simplexName} localAlias now badgeVerified simplexNameVerification = - LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, preferences, peerType, localBadge, localAlias, simplexName, simplexNameVerification} +toLocalProfile profileId Profile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, badge, contactDomain} localAlias now badgeVerified contactDomainVerified = + LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, preferences, peerType, localBadge, localAlias, contactDomain, contactDomainVerified} where localBadge = (\b@(BadgeProof _ _ _ info) -> PeerBadge b (mkBadgeStatus now badgeVerified info)) <$> badge fromLocalProfile :: LocalProfile -> Profile -fromLocalProfile LocalProfile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, localBadge, simplexName} = +fromLocalProfile LocalProfile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, localBadge, contactDomain} = -- the name proof is re-signed on each send - Profile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, badge = localBadge >>= wireBadge, simplexName = setClaimProof Nothing <$> simplexName} + Profile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType, badge = localBadge >>= wireBadge, contactDomain = (\d -> d {proof = Nothing} :: SimplexDomainClaim) <$> contactDomain} where wireBadge :: LocalBadge -> Maybe BadgeProof wireBadge = \case @@ -846,16 +846,12 @@ instance ToField GroupType where toField = toField . textEncode data PublicGroupAccess = PublicGroupAccess { groupWebPage :: Maybe Text, - simplexName :: Maybe SimplexNameClaim, + groupDomainClaim :: Maybe SimplexDomainClaim, domainWebPage :: Bool, allowEmbedding :: Bool } deriving (Eq, Show) --- selector disambiguated from Profile/LocalProfile simplexName -publicGroupClaim :: PublicGroupAccess -> Maybe SimplexNameClaim -publicGroupClaim PublicGroupAccess {simplexName} = simplexName - data PublicGroupProfile = PublicGroupProfile { groupType :: GroupType, groupLink :: ShortLinkContact, diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 8c4a9d1845..38c45b9178 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -52,7 +52,7 @@ import Simplex.Chat.Remote.AppVersion (AppVersion (..), pattern AppVersionRange) import Simplex.Chat.Remote.Types import Simplex.Chat.Store (AddressSettings (..), AutoAccept (..), StoreError (..), UserContactLink (..)) import Simplex.Chat.Styled -import Simplex.Chat.Names (claimName, claimProof) +import Simplex.Chat.Names (SimplexDomainClaim (..), claimDomain) import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Shared @@ -148,8 +148,8 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte CRContactRatchetSyncStarted {} -> ["connection synchronization started"] CRGroupMemberRatchetSyncStarted {} -> ["connection synchronization started"] CRConnectionVerified u verified code -> ttyUser u [plain $ if verified then "connection verified" else "connection not verified, current code is " <> code] - CRContactNameVerified u (Contact {profile = LocalProfile {simplexName}}) result -> ttyUser u $ viewNameVerified (claimName <$> simplexName) result - CRGroupNameVerified u g result -> ttyUser u $ viewNameVerified (groupSimplexName g) result + CRContactNameVerified u (Contact {profile = LocalProfile {contactDomain}}) result -> ttyUser u $ viewNameVerified NTContact (claimDomain <$> contactDomain) result + CRGroupNameVerified u g result -> ttyUser u $ viewNameVerified NTPublicGroup (groupSimplexDomain g) result CRContactCode u ct code -> ttyUser u $ viewContactCode ct code testView CRGroupMemberCode u g m code -> ttyUser u $ viewGroupMemberCode g m code testView CRNewChatItems u chatItems -> viewChatItems ttyUser unmuted u chatItems ts tz testView @@ -1128,13 +1128,13 @@ simplexChatContact' = \case CLFull (CRContactUri crData) -> CLFull $ CRContactUri crData {crScheme = simplexChat} l@(CLShort _) -> l -groupSimplexName :: GroupInfo -> Maybe SimplexNameInfo -groupSimplexName GroupInfo {groupProfile = GroupProfile {publicGroup}} = - claimName <$> (publicGroup >>= publicGroupAccess >>= publicGroupClaim) +groupSimplexDomain :: GroupInfo -> Maybe SimplexDomain +groupSimplexDomain GroupInfo {groupProfile = GroupProfile {publicGroup}} = + claimDomain <$> (publicGroup >>= publicGroupAccess >>= groupDomainClaim) -viewNameVerified :: Maybe SimplexNameInfo -> Maybe Text -> [StyledString] -viewNameVerified name_ result = - let nameStr = maybe "name" (\ni -> "SimpleX name " <> shortNameInfoStr ni) name_ +viewNameVerified :: SimplexNameType -> Maybe SimplexDomain -> Maybe Text -> [StyledString] +viewNameVerified nameType domain_ result = + let nameStr = maybe "name" (\d -> "SimpleX name " <> shortNameInfoStr (SimplexNameInfo nameType d)) domain_ in case result of Nothing -> [plain nameStr <> " verified"] Just reason -> [plain nameStr <> " not verified: " <> plain reason] @@ -1142,16 +1142,16 @@ viewNameVerified name_ result = -- §4.7: show a peer's claimed name only with its verification context — "verified" / "verification -- failed" when a status is recorded, "unverified" when there is a proof but no status yet, and nothing -- at all when there is neither (an unproven, unverifiable claim is not shown). -simplexNameStatus :: Maybe SimplexNameInfo -> Maybe Bool -> Bool -> [StyledString] -simplexNameStatus Nothing _ _ = [] -simplexNameStatus (Just ni) status hasProof = case status of +simplexDomainLine :: SimplexNameType -> Maybe SimplexDomainClaim -> Maybe Bool -> [StyledString] +simplexDomainLine _ Nothing _ = [] +simplexDomainLine nameType (Just SimplexDomainClaim {domain, proof}) status = case status of Just True -> [line "verified"] Just False -> [line "verification failed"] Nothing - | hasProof -> [line "unverified"] + | isJust proof -> [line "unverified"] | otherwise -> [] where - line s = "SimpleX name: " <> plain (shortNameInfoStr ni) <> " (" <> s <> ")" + line s = plain $ "SimpleX name: " <> shortNameInfoStr (SimplexNameInfo nameType (unStrJSON domain)) <> " (" <> s <> ")" -- TODO [short links] show all settings viewAddressSettings :: AddressSettings -> [StyledString] @@ -1172,7 +1172,7 @@ groupLink_ intro g GroupLink {connLinkContact = CCLink cReq shortLink, acceptMem "", plain $ maybe cReqStr strEncode shortLink ] - <> ["SimpleX name: " <> plain (shortNameInfoStr ni) | Just ni <- [groupSimplexName g]] + <> [plain ("SimpleX name: " <> shortNameInfoStr (SimplexNameInfo NTPublicGroup d)) | Just d <- [groupSimplexDomain g]] <> [ "", "Anybody can connect to you and join group as " <> showRole acceptMemberRole <> " with: " <> highlight' "/c ", "to show it again: " <> highlight ("/show link #" <> viewGroupName g), @@ -1253,7 +1253,7 @@ viewGroupLinkRelaysUpdated g groupLink relays = [ "group link:", plain $ maybe cReqStr strEncode shortLink ] - <> ["SimpleX name: " <> plain (shortNameInfoStr ni) | Just ni <- [groupSimplexName g]] + <> [plain ("SimpleX name: " <> shortNameInfoStr (SimplexNameInfo NTPublicGroup d)) | Just d <- [groupSimplexDomain g]] where GroupLink {connLinkContact = CCLink cReq shortLink} = groupLink cReqStr = strEncode $ simplexChatContact cReq @@ -1809,12 +1809,12 @@ viewContactBadge = maybe [] $ \lb -> in [plain (textEncode badgeType <> " badge - " <> st), plain expiry] viewContactInfo :: Contact -> Maybe ConnectionStats -> Maybe Profile -> [StyledString] -viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink, localBadge, simplexName, simplexNameVerification}, activeConn, uiThemes, customData} stats incognitoProfile = +viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink, localBadge, contactDomain, contactDomainVerified}, activeConn, uiThemes, customData} stats incognitoProfile = ["contact ID: " <> sShow contactId] <> viewContactBadge localBadge <> maybe [] viewConnectionStats stats <> maybe [] (\l -> ["contact address: " <> plain (strEncode (simplexChatContact' l))]) contactLink - <> simplexNameStatus (claimName <$> simplexName) simplexNameVerification (isJust (claimProof =<< simplexName)) + <> simplexDomainLine NTContact contactDomain contactDomainVerified <> maybe ["you've shared main profile with this contact"] (\p -> ["you've shared incognito profile with this contact: " <> incognitoProfile' p]) @@ -2049,10 +2049,10 @@ viewGroupUpdated access = pg >>= publicGroupAccess access' = pg' >>= publicGroupAccess viewAccess Nothing = " removed" - viewAccess (Just PublicGroupAccess {groupWebPage, simplexName, domainWebPage, allowEmbedding}) = + viewAccess (Just PublicGroupAccess {groupWebPage, groupDomainClaim, domainWebPage, allowEmbedding}) = maybe "" (\u -> " web=" <> plain u) groupWebPage - <> maybe "" (\ni -> " name=" <> plain (strEncode ni)) (claimName <$> simplexName) - <> (if domainWebPage then " name_page=on" else "") + <> maybe "" (\ni -> " domain=" <> plain (strEncode ni)) (claimDomain <$> groupDomainClaim) + <> (if domainWebPage then " domain_page=on" else "") <> (if allowEmbedding then " embed=on" else "") viewGroupProfile :: GroupInfo -> [StyledString] @@ -2152,11 +2152,11 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case ILPConnecting Nothing -> [invLink "connecting"] ILPConnecting (Just ct) -> [invLink ("connecting to contact " <> ttyContact' ct)] ILPKnown ct - | nextConnectPrepared ct -> [invLink ("known prepared contact " <> ttyContact' ct)] <> contactNameLine ct - | contactDeleted ct -> [invLink ("known deleted contact " <> ttyContact' ct)] <> contactNameLine ct + | nextConnectPrepared ct -> [invLink ("known prepared contact " <> ttyContact' ct)] <> contactDomainLine ct + | contactDeleted ct -> [invLink ("known deleted contact " <> ttyContact' ct)] <> contactDomainLine ct | otherwise -> [invLink ("known contact " <> ttyContact' ct)] - <> contactNameLine ct + <> contactDomainLine ct <> ["use " <> ttyToContact' ct <> highlight' "" <> " to send messages"] where invLink = ("invitation link: " <>) @@ -2170,12 +2170,12 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case CAPConnectingConfirmReconnect -> [ctAddr "connecting, allowed to reconnect"] CAPConnectingProhibit ct -> [ctAddr ("connecting to contact " <> ttyContact' ct)] CAPKnown ct - | nextConnectPrepared ct -> [ctAddr ("known prepared contact " <> ttyContact' ct)] <> contactNameLine ct + | nextConnectPrepared ct -> [ctAddr ("known prepared contact " <> ttyContact' ct)] <> contactDomainLine ct | otherwise -> [ctAddr ("known contact " <> ttyContact' ct)] - <> contactNameLine ct + <> contactDomainLine ct <> ["use " <> ttyToContact' ct <> highlight' "" <> " to send messages"] - CAPContactViaAddress ct -> [ctAddr ("known contact without connection " <> ttyContact' ct)] <> contactNameLine ct + CAPContactViaAddress ct -> [ctAddr ("known contact without connection " <> ttyContact' ct)] <> contactDomainLine ct where ctAddr = ("contact address: " <>) addrOrBiz = \case @@ -2196,16 +2196,16 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case Just PreparedGroup {connLinkStartedConnection} -> case memberStatus m of GSMemUnknown | connLinkStartedConnection -> connecting g - | otherwise -> [knownGroup "prepared "] <> groupNameLine g + | otherwise -> [knownGroup "prepared "] <> groupDomainLine g GSMemAccepted -> connecting g _ - | memberRemoved m -> [knownGroup "deleted "] <> groupNameLine g -- it should not get here, as this plan is returned as GLPOk + | memberRemoved m -> [knownGroup "deleted "] <> groupDomainLine g -- it should not get here, as this plan is returned as GLPOk | otherwise -> knownActive _ -> knownActive where knownActive = [knownGroup ""] - <> groupNameLine g + <> groupDomainLine g <> ["use " <> ttyToGroup g Nothing <> highlight' "" <> " to send messages"] knownGroup prepared = grpOrBizLink g <> ": known " <> prepared <> grpOrBiz g <> " " <> ttyGroup' g GLPNoRelays _ -> [grpLink "channel has no active relays, please try to join later"] @@ -2224,12 +2224,13 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case nextConnectPrepared Contact {preparedContact, activeConn} = case preparedContact of Just _ -> maybe True (\c -> connStatus c == ConnPrepared) activeConn _ -> False - contactNameLine :: Contact -> [StyledString] - contactNameLine Contact {profile = LocalProfile {simplexName, simplexNameVerification}} = - simplexNameStatus (claimName <$> simplexName) simplexNameVerification (isJust (claimProof =<< simplexName)) - groupNameLine :: GroupInfo -> [StyledString] - groupNameLine g'@GroupInfo {simplexNameVerification, groupProfile = GroupProfile {publicGroup}} = - simplexNameStatus (groupSimplexName g') simplexNameVerification (isJust (claimProof =<< (publicGroup >>= publicGroupAccess >>= publicGroupClaim))) + contactDomainLine :: Contact -> [StyledString] + contactDomainLine Contact {profile = LocalProfile {contactDomain, contactDomainVerified}} = + simplexDomainLine NTContact contactDomain contactDomainVerified + groupDomainLine :: GroupInfo -> [StyledString] + groupDomainLine GroupInfo {groupDomainVerified, groupProfile = GroupProfile {publicGroup}} = do + let domain = publicGroup >>= publicGroupAccess >>= groupDomainClaim + in simplexDomainLine NTPublicGroup domain groupDomainVerified viewSigVerification = \case Just OVVerified -> ["owner signature: verified"] Just (OVFailed r) -> ["owner signature: FAILED (" <> plain r <> ")"] @@ -2681,11 +2682,11 @@ viewChatError isCmd logLevel testView = \case CEChatNotStopped -> ["error: chat not stopped"] CEChatStoreChanged -> ["error: chat store changed, please restart chat"] CEInvalidConnReq -> viewInvalidConnReq - CESimplexName ni nameErr -> - let reason = case nameErr of - SNENoValidLink -> "has no valid connection link" - SNEUnknownName -> "is not included in the connection link's profile" - in ["SimpleX name " <> plain (shortNameInfoStr ni) <> " " <> reason] + CESimplexDomain domain domainErr -> + let reason = case domainErr of + SDENoValidLink -> "has no valid connection link" + SDEUnknownDomain -> "is not included in the connection link's profile" + in [plain $ "SimpleX name " <> strEncode domain <> " " <> reason] CEUnsupportedConnReq -> [ "", "Connection link is not supported by the your app version, please ugrade it.", plain updateStr] CEInvalidChatMessage Connection {connId} msgMeta_ msg e -> [ plain $ diff --git a/tests/Bots/BroadcastTests.hs b/tests/Bots/BroadcastTests.hs index be58e1ad6c..bfe7b96c7d 100644 --- a/tests/Bots/BroadcastTests.hs +++ b/tests/Bots/BroadcastTests.hs @@ -33,7 +33,7 @@ withBroadcastBot opts test = bot = simplexChatCore testCfg (mkChatOpts opts) $ broadcastBot opts broadcastBotProfile :: Profile -broadcastBotProfile = Profile {displayName = "broadcast_bot", fullName = "Broadcast Bot", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences = Nothing, badge = Nothing, simplexName = Nothing} +broadcastBotProfile = Profile {displayName = "broadcast_bot", fullName = "Broadcast Bot", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences = Nothing, badge = Nothing, contactDomain = Nothing} mkBotOpts :: TestParams -> [KnownContact] -> BroadcastBotOpts mkBotOpts ps publishers = diff --git a/tests/Bots/DirectoryTests.hs b/tests/Bots/DirectoryTests.hs index a277d077ea..3b34aef66c 100644 --- a/tests/Bots/DirectoryTests.hs +++ b/tests/Bots/DirectoryTests.hs @@ -98,7 +98,7 @@ directoryServiceTests = do it "should update subscriber count periodically" testLinkCheckUpdatesCount directoryProfile :: Profile -directoryProfile = Profile {displayName = "SimpleX Directory", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences = Nothing, badge = Nothing, simplexName = Nothing} +directoryProfile = Profile {displayName = "SimpleX Directory", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Just CPTBot, preferences = Nothing, badge = Nothing, contactDomain = Nothing} mkDirectoryOpts :: TestParams -> [KnownContact] -> Maybe KnownGroup -> Maybe FilePath -> DirectoryOpts mkDirectoryOpts TestParams {tmpPath = ps} superUsers ownersGroup webFolder = diff --git a/tests/ChatTests/Names.hs b/tests/ChatTests/Names.hs index 120ef307b8..7c1ef40155 100644 --- a/tests/ChatTests/Names.hs +++ b/tests/ChatTests/Names.hs @@ -10,7 +10,7 @@ import ChatTests.Utils import Control.Concurrent.Async (concurrently_) import qualified Data.Text as T import NameResolver -import Simplex.Messaging.SimplexName (SimplexNameDomain (..), SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) +import Simplex.Messaging.SimplexName (SimplexDomain (..), SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) import Test.Hspec hiding (it) chatNamesTests :: SpecWith TestParams @@ -26,12 +26,12 @@ testConnectByName :: HasCallStack => TestParams -> IO () testConnectByName ps = withSmpServerAndNames $ \reg -> testChat2 aliceProfile bobProfile (test reg) ps where - aliceName = SimplexNameInfo NTContact (SimplexNameDomain TLDSimplex "alice" []) + aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" []) test reg alice bob = do alice ##> "/ad" (shortLink, _) <- getContactLinks alice True registerName reg aliceName (contactNameRecord "alice" (T.pack shortLink)) - alice ##> "/_set_name 1 @alice.simplex" + alice ##> "/_set domain 1 alice.simplex" alice <## "new contact address set" bob ##> "/c @alice.simplex" bob <## "alice: connection started" @@ -60,19 +60,19 @@ testConnectByNameNotClaimed :: HasCallStack => TestParams -> IO () testConnectByNameNotClaimed ps = withSmpServerAndNames $ \reg -> testChat2 aliceProfile bobProfile (test reg) ps where - aliceName = SimplexNameInfo NTContact (SimplexNameDomain TLDSimplex "alice" []) + aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" []) test reg alice bob = do alice ##> "/ad" (shortLink, _) <- getContactLinks alice True registerName reg aliceName (contactNameRecord "alice" (T.pack shortLink)) bob ##> "/c @alice.simplex" - bob <## "SimpleX name @alice.simplex is not included in the connection link's profile" + bob <## "SimpleX name alice.simplex is not included in the connection link's profile" testConnectByNameKnownContactNotClaimed :: HasCallStack => TestParams -> IO () testConnectByNameKnownContactNotClaimed ps = withSmpServerAndNames $ \reg -> testChat2 aliceProfile bobProfile (test reg) ps where - aliceName = SimplexNameInfo NTContact (SimplexNameDomain TLDSimplex "alice" []) + aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" []) test reg alice bob = do alice ##> "/ad" (shortLink, _) <- getContactLinks alice True @@ -88,7 +88,7 @@ testConnectByNameKnownContactNotClaimed ps = withSmpServerAndNames $ \reg -> (alice <## "bob (Bob): contact is connected") registerName reg aliceName (contactNameRecord "alice" (T.pack shortLink)) bob ##> "/c @alice.simplex" - bob <## "SimpleX name @alice.simplex is not included in the connection link's profile" + bob <## "SimpleX name alice.simplex is not included in the connection link's profile" testConnectByNameNotFound :: HasCallStack => TestParams -> IO () testConnectByNameNotFound ps = withSmpServerAndNames $ \_reg -> @@ -102,15 +102,15 @@ testSetNameNotOwnAddress :: HasCallStack => TestParams -> IO () testSetNameNotOwnAddress ps = withSmpServerAndNames $ \reg -> testChat2 aliceProfile bobProfile (test reg) ps where - aliceName = SimplexNameInfo NTContact (SimplexNameDomain TLDSimplex "alice" []) + aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" []) test reg alice bob = do bob ##> "/ad" (bobShortLink, _) <- getContactLinks bob True registerName reg aliceName (contactNameRecord "alice" (T.pack bobShortLink)) alice ##> "/ad" _ <- getContactLinks alice True - alice ##> "/_set_name 1 @alice.simplex" - alice <## "SimpleX name @alice.simplex has no valid connection link" + alice ##> "/_set domain 1 alice.simplex" + alice <## "SimpleX name alice.simplex has no valid connection link" testConnectByChannelName :: HasCallStack => TestParams -> IO () testConnectByChannelName ps = withSmpServerAndNames $ \reg -> @@ -119,10 +119,10 @@ testConnectByChannelName ps = withSmpServerAndNames $ \reg -> withNewTestChat ps "bob" bobProfile $ \bob -> do (shortLink, _) <- prepareChannel1Relay "team" alice cath registerName reg teamName (channelNameRecord "team" (T.pack shortLink)) - alice ##> "/public group access #team name=team.simplex" - alice <## "updated public group access: name=#team.simplex" + alice ##> "/public group access #team domain=team.simplex" + alice <## "updated public group access: domain=team.simplex" cath <## "alice updated group #team: (signed)" - cath <## "updated public group access: name=#team.simplex" + cath <## "updated public group access: domain=team.simplex" bob ##> "/c #team.simplex" bob <## "#team: connection started" concurrentlyN_ @@ -140,4 +140,4 @@ testConnectByChannelName ps = withSmpServerAndNames $ \reg -> bob <## "SimpleX name: #team (verified)" bob <## "use #team to send messages" where - teamName = SimplexNameInfo NTPublicGroup (SimplexNameDomain TLDSimplex "team" []) + teamName = SimplexNameInfo NTPublicGroup (SimplexDomain TLDSimplex "team" []) diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index abf15da83a..581aaec879 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -497,7 +497,7 @@ testMultiWordProfileNames = aliceProfile' = baseProfile {displayName = "Alice Jones"} bobProfile' = baseProfile {displayName = "Bob James"} cathProfile' = baseProfile {displayName = "Cath Johnson"} - baseProfile = Profile {displayName = "", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = defaultPrefs, badge = Nothing, simplexName = Nothing} + baseProfile = Profile {displayName = "", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = defaultPrefs, badge = Nothing, contactDomain = Nothing} testUserContactLink :: HasCallStack => TestParams -> IO () testUserContactLink = diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index cd24995db6..a613a13df6 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -88,7 +88,7 @@ serviceProfile :: Profile serviceProfile = mkProfile "service_user" "Service user" Nothing mkProfile :: T.Text -> T.Text -> Maybe ImageData -> Profile -mkProfile displayName descr image = Profile {displayName, fullName = "", shortDescr = Just descr, image, contactLink = Nothing, peerType = Nothing, preferences = defaultPrefs, badge = Nothing, simplexName = Nothing} +mkProfile displayName descr image = Profile {displayName, fullName = "", shortDescr = Just descr, image, contactLink = Nothing, peerType = Nothing, preferences = defaultPrefs, badge = Nothing, contactDomain = Nothing} it :: HasCallStack => String -> (ps -> Expectation) -> SpecWith (Arg (ps -> Expectation)) it name test = diff --git a/tests/MarkdownTests.hs b/tests/MarkdownTests.hs index 2a5328ff26..e315b59f5e 100644 --- a/tests/MarkdownTests.hs +++ b/tests/MarkdownTests.hs @@ -10,7 +10,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Markdown -import Simplex.Messaging.Agent.Protocol (SimplexNameDomain (..), SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) +import Simplex.Messaging.Agent.Protocol (SimplexDomain (..), SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Util ((<$$>)) import System.Console.ANSI.Types @@ -400,7 +400,7 @@ command' :: Text -> Text -> FormattedText command' = FormattedText . Just . Command sname :: SimplexNameType -> SimplexTLD -> Text -> [Text] -> Text -> Markdown -sname nt ns dom sub txt = markdown (SimplexName $ SimplexNameInfo nt (SimplexNameDomain ns dom sub)) (pfx <> txt) +sname nt ns dom sub txt = markdown (SimplexName $ SimplexNameInfo nt (SimplexDomain ns dom sub)) (pfx <> txt) where pfx = case nt of NTPublicGroup -> "#"; NTContact -> "@" diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs index 2b1e1eb64e..3cfb367382 100644 --- a/tests/ProtocolTests.hs +++ b/tests/ProtocolTests.hs @@ -108,7 +108,7 @@ testGroupPreferences :: Maybe GroupPreferences testGroupPreferences = Just GroupPreferences {timedMessages = Nothing, directMessages = Nothing, reactions = Just ReactionsGroupPreference {enable = FEOn}, voice = Just VoiceGroupPreference {enable = FEOn, role = Nothing}, files = Nothing, fullDelete = Nothing, simplexLinks = Nothing, history = Nothing, reports = Nothing, support = Nothing, sessions = Nothing, comments = Nothing, commands = Nothing} testProfile :: Profile -testProfile = Profile {displayName = "alice", fullName = "Alice", shortDescr = Nothing, image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), peerType = Nothing, contactLink = Nothing, preferences = testChatPreferences, badge = Nothing, simplexName = Nothing} +testProfile = Profile {displayName = "alice", fullName = "Alice", shortDescr = Nothing, image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), peerType = Nothing, contactLink = Nothing, preferences = testChatPreferences, badge = Nothing, contactDomain = Nothing} testGroupProfile :: GroupProfile testGroupProfile = GroupProfile {displayName = "team", fullName = "Team", description = Nothing, shortDescr = Nothing, image = Nothing, publicGroup = Nothing, groupPreferences = testGroupPreferences, memberAdmission = Nothing} @@ -241,7 +241,7 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do #==# XInfo testProfile it "x.info with empty full name" $ "{\"v\":\"1\",\"event\":\"x.info\",\"params\":{\"profile\":{\"fullName\":\"\",\"displayName\":\"alice\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}" - #==# XInfo Profile {displayName = "alice", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = testChatPreferences, badge = Nothing, simplexName = Nothing} + #==# XInfo Profile {displayName = "alice", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = testChatPreferences, badge = Nothing, contactDomain = Nothing} it "x.contact with xContactId" $ "{\"v\":\"1\",\"event\":\"x.contact\",\"params\":{\"contactReqId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}" #==# XContact testProfile (Just $ XContactId "\1\2\3\4") Nothing Nothing