mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-24 13:05:41 +00:00
Merge branch 'master' into master-android
This commit is contained in:
@@ -272,18 +272,6 @@ func apiGetAppSettings(settings: AppSettings) throws -> AppSettings {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSetPQEncryption(_ enable: Bool) throws {
|
||||
let r = chatSendCmdSync(.apiSetPQEncryption(enable: enable))
|
||||
if case .cmdOk = r { return }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSetContactPQ(_ contactId: Int64, _ enable: Bool) async throws -> Contact {
|
||||
let r = await chatSendCmd(.apiSetContactPQ(contactId: contactId, enable: enable))
|
||||
if case let .contactPQAllowed(_, contact, _) = r { return contact }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiExportArchive(config: ArchiveConfig) async throws {
|
||||
try await sendCommandOkResp(.apiExportArchive(config: config))
|
||||
}
|
||||
@@ -1320,7 +1308,6 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
|
||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||
try apiSetPQEncryption(pqExperimentalEnabledDefault.get())
|
||||
m.chatInitialized = true
|
||||
m.currentUser = try apiGetActiveUser()
|
||||
if m.currentUser == nil {
|
||||
|
||||
@@ -103,7 +103,6 @@ struct ChatInfoView: View {
|
||||
@State private var sendReceipts = SendReceipts.userDefault(true)
|
||||
@State private var sendReceiptsUserDefault = true
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false
|
||||
|
||||
enum ChatInfoViewAlert: Identifiable {
|
||||
case clearChatAlert
|
||||
@@ -111,7 +110,6 @@ struct ChatInfoView: View {
|
||||
case switchAddressAlert
|
||||
case abortSwitchAddressAlert
|
||||
case syncConnectionForceAlert
|
||||
case allowContactPQEncryptionAlert
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
@@ -121,7 +119,6 @@ struct ChatInfoView: View {
|
||||
case .switchAddressAlert: return "switchAddressAlert"
|
||||
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
|
||||
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
|
||||
case .allowContactPQEncryptionAlert: return "allowContactPQEncryptionAlert"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
@@ -168,19 +165,9 @@ struct ChatInfoView: View {
|
||||
}
|
||||
.disabled(!contact.ready || !contact.active)
|
||||
|
||||
if pqExperimentalEnabled,
|
||||
let conn = contact.activeConn {
|
||||
if let conn = contact.activeConn {
|
||||
Section {
|
||||
infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard")
|
||||
if !conn.pqEncryption {
|
||||
allowPQButton()
|
||||
}
|
||||
} header: {
|
||||
Text(String("Quantum resistant E2E encryption"))
|
||||
} footer: {
|
||||
if !conn.pqEncryption {
|
||||
Text(String("After allowing quantum resistant encryption, it will be enabled after several messages if your contact also allows it."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +243,6 @@ struct ChatInfoView: View {
|
||||
case .switchAddressAlert: return switchAddressAlert(switchContactAddress)
|
||||
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress)
|
||||
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) })
|
||||
case .allowContactPQEncryptionAlert: return allowContactPQEncryptionAlert()
|
||||
case let .error(title, error): return mkAlert(title: title, message: error)
|
||||
}
|
||||
}
|
||||
@@ -430,15 +416,6 @@ struct ChatInfoView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func allowPQButton() -> some View {
|
||||
Button {
|
||||
alert = .allowContactPQEncryptionAlert
|
||||
} label: {
|
||||
Label(String("Allow PQ encryption"), systemImage: "exclamationmark.triangle")
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
}
|
||||
|
||||
private func networkStatusRow() -> some View {
|
||||
HStack {
|
||||
Text("Network status")
|
||||
@@ -572,34 +549,6 @@ struct ChatInfoView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func allowContactPQEncryption() {
|
||||
Task {
|
||||
do {
|
||||
let ct = try await apiSetContactPQ(contact.apiId, true)
|
||||
contact = ct
|
||||
await MainActor.run {
|
||||
chatModel.updateContact(ct)
|
||||
dismiss()
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("allowContactPQEncryption apiSetContactPQ error: \(responseError(error))")
|
||||
let a = getErrorAlert(error, "Error allowing contact PQ encryption")
|
||||
await MainActor.run {
|
||||
alert = .error(title: a.title, error: a.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func allowContactPQEncryptionAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text(String("Allow quantum resistant encryption?")),
|
||||
message: Text(String("This is an experimental feature, it is not recommended to enable it for important chats.")),
|
||||
primaryButton: .destructive(Text(String("Allow")), action: allowContactPQEncryption),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func switchAddressAlert(_ switchAddress: @escaping () -> Void) -> Alert {
|
||||
|
||||
@@ -375,6 +375,31 @@ private let versionDescriptions: [VersionDescription] = [
|
||||
description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"
|
||||
),
|
||||
]
|
||||
),
|
||||
VersionDescription(
|
||||
version: "v5.7",
|
||||
features: [
|
||||
FeatureDescription(
|
||||
icon: "key",
|
||||
title: "Quantum resistant encryption",
|
||||
description: "Will be enabled in direct chats!"
|
||||
),
|
||||
FeatureDescription(
|
||||
icon: "arrowshape.turn.up.forward",
|
||||
title: "Forward and save messages",
|
||||
description: "Message source remains private."
|
||||
),
|
||||
FeatureDescription(
|
||||
icon: "music.note",
|
||||
title: "In-call sounds",
|
||||
description: "When connecting audio and video calls."
|
||||
),
|
||||
FeatureDescription(
|
||||
icon: "antenna.radiowaves.left.and.right",
|
||||
title: "Network management",
|
||||
description: "More reliable network connection."
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import SimpleXChat
|
||||
struct DeveloperView: View {
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false
|
||||
@AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
@@ -43,33 +42,9 @@ struct DeveloperView: View {
|
||||
} footer: {
|
||||
(developerTools ? Text("Show:") : Text("Hide:")) + Text(" ") + Text("Database IDs and Transport isolation option.")
|
||||
}
|
||||
|
||||
if developerTools {
|
||||
Section {
|
||||
settingsRow("key") {
|
||||
Toggle("Post-quantum E2EE", isOn: $pqExperimentalEnabled)
|
||||
.onChange(of: pqExperimentalEnabled) {
|
||||
setPQExperimentalEnabled($0)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(String("Experimental"))
|
||||
} footer: {
|
||||
Text(String("In this version applies only to new contacts."))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setPQExperimentalEnabled(_ enable: Bool) {
|
||||
do {
|
||||
try apiSetPQEncryption(enable)
|
||||
} catch let error {
|
||||
let err = responseError(error)
|
||||
logger.error("apiSetPQEncryption \(err)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeveloperView_Previews: PreviewProvider {
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C22178E2BD5CBAC00A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217892BD5CBAC00A8B0E7 /* libffi.a */; };
|
||||
5C22178F2BD5CBAC00A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178A2BD5CBAC00A8B0E7 /* libgmp.a */; };
|
||||
5C2217902BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178B2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */; };
|
||||
5C2217912BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178C2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */; };
|
||||
5C2217922BD5CBAC00A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178D2BD5CBAC00A8B0E7 /* libgmpxx.a */; };
|
||||
5C2217982BD6B0F200A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217932BD6B0F200A8B0E7 /* libgmp.a */; };
|
||||
5C2217992BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217942BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */; };
|
||||
5C22179A2BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217952BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */; };
|
||||
5C22179B2BD6B0F200A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217962BD6B0F200A8B0E7 /* libgmpxx.a */; };
|
||||
5C22179C2BD6B0F200A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217972BD6B0F200A8B0E7 /* libffi.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -284,11 +284,11 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C2217892BD5CBAC00A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C22178A2BD5CBAC00A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C22178B2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C22178C2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a"; sourceTree = "<group>"; };
|
||||
5C22178D2BD5CBAC00A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C2217932BD6B0F200A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C2217942BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C2217952BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a"; sourceTree = "<group>"; };
|
||||
5C2217962BD6B0F200A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C2217972BD6B0F200A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@@ -535,13 +535,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C22179B2BD6B0F200A8B0E7 /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5C2217902BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */,
|
||||
5C22178E2BD5CBAC00A8B0E7 /* libffi.a in Frameworks */,
|
||||
5C2217912BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */,
|
||||
5C22178F2BD5CBAC00A8B0E7 /* libgmp.a in Frameworks */,
|
||||
5C22179A2BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */,
|
||||
5C2217992BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */,
|
||||
5C22179C2BD6B0F200A8B0E7 /* libffi.a in Frameworks */,
|
||||
5C2217982BD6B0F200A8B0E7 /* libgmp.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5C2217922BD5CBAC00A8B0E7 /* libgmpxx.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -605,11 +605,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C2217892BD5CBAC00A8B0E7 /* libffi.a */,
|
||||
5C22178A2BD5CBAC00A8B0E7 /* libgmp.a */,
|
||||
5C22178D2BD5CBAC00A8B0E7 /* libgmpxx.a */,
|
||||
5C22178B2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */,
|
||||
5C22178C2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */,
|
||||
5C2217972BD6B0F200A8B0E7 /* libffi.a */,
|
||||
5C2217932BD6B0F200A8B0E7 /* libgmp.a */,
|
||||
5C2217962BD6B0F200A8B0E7 /* libgmpxx.a */,
|
||||
5C2217942BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */,
|
||||
5C2217952BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -32,8 +32,6 @@ public enum ChatCommand {
|
||||
case setTempFolder(tempFolder: String)
|
||||
case setFilesFolder(filesFolder: String)
|
||||
case apiSetEncryptLocalFiles(enable: Bool)
|
||||
case apiSetPQEncryption(enable: Bool)
|
||||
case apiSetContactPQ(contactId: Int64, enable: Bool)
|
||||
case apiExportArchive(config: ArchiveConfig)
|
||||
case apiImportArchive(config: ArchiveConfig)
|
||||
case apiDeleteStorage
|
||||
@@ -172,8 +170,6 @@ public enum ChatCommand {
|
||||
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
|
||||
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
|
||||
case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))"
|
||||
case let .apiSetPQEncryption(enable): return "/pq \(onOff(enable))"
|
||||
case let .apiSetContactPQ(contactId, enable): return "/_pq @\(contactId) \(onOff(enable))"
|
||||
case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))"
|
||||
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
|
||||
case .apiDeleteStorage: return "/_db delete"
|
||||
@@ -326,8 +322,6 @@ public enum ChatCommand {
|
||||
case .setTempFolder: return "setTempFolder"
|
||||
case .setFilesFolder: return "setFilesFolder"
|
||||
case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles"
|
||||
case .apiSetPQEncryption: return "apiSetPQEncryption"
|
||||
case .apiSetContactPQ: return "apiSetContactPQ"
|
||||
case .apiExportArchive: return "apiExportArchive"
|
||||
case .apiImportArchive: return "apiImportArchive"
|
||||
case .apiDeleteStorage: return "apiDeleteStorage"
|
||||
@@ -653,7 +647,6 @@ public enum ChatResponse: Decodable, Error {
|
||||
case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo)
|
||||
case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason)
|
||||
// pq
|
||||
case contactPQAllowed(user: UserRef, contact: Contact, pqEncryption: Bool)
|
||||
case contactPQEnabled(user: UserRef, contact: Contact, pqEnabled: Bool)
|
||||
// misc
|
||||
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
|
||||
@@ -809,8 +802,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .remoteCtrlSessionCode: return "remoteCtrlSessionCode"
|
||||
case .remoteCtrlConnected: return "remoteCtrlConnected"
|
||||
case .remoteCtrlStopped: return "remoteCtrlStopped"
|
||||
case .contactPQAllowed: return "contactPQAllowed"
|
||||
case .contactPQEnabled: return "contactPQAllowed"
|
||||
case .contactPQEnabled: return "contactPQEnabled"
|
||||
case .versionInfo: return "versionInfo"
|
||||
case .cmdOk: return "cmdOk"
|
||||
case .chatCmdError: return "chatCmdError"
|
||||
@@ -967,7 +959,6 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
|
||||
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
|
||||
case .remoteCtrlStopped: return noDetails
|
||||
case let .contactPQAllowed(u, contact, pqEncryption): return withUser(u, "contact: \(String(describing: contact))\npqEncryption: \(pqEncryption)")
|
||||
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
|
||||
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
|
||||
case .cmdOk: return noDetails
|
||||
|
||||
@@ -41,7 +41,7 @@ let GROUP_DEFAULT_STORE_DB_PASSPHRASE = "storeDBPassphrase"
|
||||
public let GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE = "initialRandomDBPassphrase"
|
||||
public let GROUP_DEFAULT_CONFIRM_DB_UPGRADES = "confirmDBUpgrades"
|
||||
public let GROUP_DEFAULT_CALL_KIT_ENABLED = "callKitEnabled"
|
||||
public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled"
|
||||
public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled" // no longer used
|
||||
|
||||
public let APP_GROUP_NAME = "group.chat.simplex.app"
|
||||
|
||||
@@ -199,8 +199,6 @@ public let confirmDBUpgradesGroupDefault = BoolDefault(defaults: groupDefaults,
|
||||
|
||||
public let callKitEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CALL_KIT_ENABLED)
|
||||
|
||||
public let pqExperimentalEnabledDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED)
|
||||
|
||||
public class DateDefault {
|
||||
var defaults: UserDefaults
|
||||
var key: String
|
||||
|
||||
+1
-17
@@ -163,7 +163,6 @@ class AppPreferences {
|
||||
val confirmDBUpgrades = mkBoolPreference(SHARED_PREFS_CONFIRM_DB_UPGRADES, false)
|
||||
val selfDestruct = mkBoolPreference(SHARED_PREFS_SELF_DESTRUCT, false)
|
||||
val selfDestructDisplayName = mkStrPreference(SHARED_PREFS_SELF_DESTRUCT_DISPLAY_NAME, null)
|
||||
val pqExperimentalEnabled = mkBoolPreference(SHARED_PREFS_PQ_EXPERIMENTAL_ENABLED, false)
|
||||
|
||||
val currentTheme = mkStrPreference(SHARED_PREFS_CURRENT_THEME, DefaultTheme.SYSTEM.name)
|
||||
val systemDarkTheme = mkStrPreference(SHARED_PREFS_SYSTEM_DARK_THEME, DefaultTheme.SIMPLEX.name)
|
||||
@@ -328,7 +327,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_CONFIRM_DB_UPGRADES = "ConfirmDBUpgrades"
|
||||
private const val SHARED_PREFS_SELF_DESTRUCT = "LocalAuthenticationSelfDestruct"
|
||||
private const val SHARED_PREFS_SELF_DESTRUCT_DISPLAY_NAME = "LocalAuthenticationSelfDestructDisplayName"
|
||||
private const val SHARED_PREFS_PQ_EXPERIMENTAL_ENABLED = "PQExperimentalEnabled"
|
||||
private const val SHARED_PREFS_PQ_EXPERIMENTAL_ENABLED = "PQExperimentalEnabled" // no longer used
|
||||
private const val SHARED_PREFS_CURRENT_THEME = "CurrentTheme"
|
||||
private const val SHARED_PREFS_SYSTEM_DARK_THEME = "SystemDarkTheme"
|
||||
private const val SHARED_PREFS_THEMES = "Themes"
|
||||
@@ -680,15 +679,6 @@ object ChatController {
|
||||
throw Exception("failed to get app settings: ${r.responseType} ${r.details}")
|
||||
}
|
||||
|
||||
suspend fun apiSetPQEncryption(enable: Boolean) = sendCommandOkResp(null, CC.ApiSetPQEncryption(enable))
|
||||
|
||||
suspend fun apiSetContactPQ(rh: Long?, contactId: Long, enable: Boolean): Contact? {
|
||||
val r = sendCmd(rh, CC.ApiSetContactPQ(contactId, enable))
|
||||
if (r is CR.ContactPQAllowed) return r.contact
|
||||
apiErrorAlert("apiSetContactPQ", "Error allowing contact PQ", r)
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiExportArchive(config: ArchiveConfig) {
|
||||
val r = sendCmd(null, CC.ApiExportArchive(config))
|
||||
if (r is CR.CmdOk) return
|
||||
@@ -2393,8 +2383,6 @@ sealed class CC {
|
||||
class SetFilesFolder(val filesFolder: String): CC()
|
||||
class SetRemoteHostsFolder(val remoteHostsFolder: String): CC()
|
||||
class ApiSetEncryptLocalFiles(val enable: Boolean): CC()
|
||||
class ApiSetPQEncryption(val enable: Boolean): CC()
|
||||
class ApiSetContactPQ(val contactId: Long, val enable: Boolean): CC()
|
||||
class ApiExportArchive(val config: ArchiveConfig): CC()
|
||||
class ApiImportArchive(val config: ArchiveConfig): CC()
|
||||
class ApiDeleteStorage: CC()
|
||||
@@ -2532,8 +2520,6 @@ sealed class CC {
|
||||
is SetFilesFolder -> "/_files_folder $filesFolder"
|
||||
is SetRemoteHostsFolder -> "/remote_hosts_folder $remoteHostsFolder"
|
||||
is ApiSetEncryptLocalFiles -> "/_files_encrypt ${onOff(enable)}"
|
||||
is ApiSetPQEncryption -> "/pq ${onOff(enable)}"
|
||||
is ApiSetContactPQ -> "/_pq @$contactId ${onOff(enable)}"
|
||||
is ApiExportArchive -> "/_db export ${json.encodeToString(config)}"
|
||||
is ApiImportArchive -> "/_db import ${json.encodeToString(config)}"
|
||||
is ApiDeleteStorage -> "/_db delete"
|
||||
@@ -2676,8 +2662,6 @@ sealed class CC {
|
||||
is SetFilesFolder -> "setFilesFolder"
|
||||
is SetRemoteHostsFolder -> "setRemoteHostsFolder"
|
||||
is ApiSetEncryptLocalFiles -> "apiSetEncryptLocalFiles"
|
||||
is ApiSetPQEncryption -> "apiSetPQEncryption"
|
||||
is ApiSetContactPQ -> "apiSetContactPQ"
|
||||
is ApiExportArchive -> "apiExportArchive"
|
||||
is ApiImportArchive -> "apiImportArchive"
|
||||
is ApiDeleteStorage -> "apiDeleteStorage"
|
||||
|
||||
@@ -94,7 +94,6 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
|
||||
controller.apiSetRemoteHostsFolder(remoteHostsDir.absolutePath)
|
||||
}
|
||||
controller.apiSetEncryptLocalFiles(controller.appPrefs.privacyEncryptLocalFiles.get())
|
||||
controller.apiSetPQEncryption(controller.appPrefs.pqExperimentalEnabled.get())
|
||||
// If we migrated successfully means previous re-encryption process on database level finished successfully too
|
||||
if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null)
|
||||
val user = chatController.apiGetActiveUser(null)
|
||||
|
||||
+2
-44
@@ -58,7 +58,6 @@ fun ChatInfoView(
|
||||
val currentUser = remember { chatModel.currentUser }.value
|
||||
val connStats = remember(contact.id, connectionStats) { mutableStateOf(connectionStats) }
|
||||
val developerTools = chatModel.controller.appPrefs.developerTools.get()
|
||||
val pqExperimentalEnabled = chatModel.controller.appPrefs.pqExperimentalEnabled.get()
|
||||
if (chat != null && currentUser != null) {
|
||||
val contactNetworkStatus = remember(chatModel.networkStatuses.toMap(), contact) {
|
||||
mutableStateOf(chatModel.contactNetworkStatus(contact))
|
||||
@@ -81,7 +80,6 @@ fun ChatInfoView(
|
||||
localAlias,
|
||||
connectionCode,
|
||||
developerTools,
|
||||
pqExperimentalEnabled,
|
||||
onLocalAliasChanged = {
|
||||
setContactAlias(chat, it, chatModel)
|
||||
},
|
||||
@@ -140,17 +138,6 @@ fun ChatInfoView(
|
||||
}
|
||||
})
|
||||
},
|
||||
allowContactPQ = {
|
||||
showAllowContactPQAlert(allowContactPQ = {
|
||||
withBGApi {
|
||||
val ct = chatModel.controller.apiSetContactPQ(chatRh, contact.contactId, true)
|
||||
if (ct != null) {
|
||||
chatModel.updateContact(chatRh, ct)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
})
|
||||
},
|
||||
verifyClicked = {
|
||||
ModalManager.end.showModalCloseable { close ->
|
||||
remember { derivedStateOf { (chatModel.getContactChat(contact.contactId)?.chatInfo as? ChatInfo.Direct)?.contact } }.value?.let { ct ->
|
||||
@@ -301,7 +288,6 @@ fun ChatInfoLayout(
|
||||
localAlias: String,
|
||||
connectionCode: String?,
|
||||
developerTools: Boolean,
|
||||
pqExperimentalEnabled: Boolean,
|
||||
onLocalAliasChanged: (String) -> Unit,
|
||||
openPreferences: () -> Unit,
|
||||
deleteContact: () -> Unit,
|
||||
@@ -310,7 +296,6 @@ fun ChatInfoLayout(
|
||||
abortSwitchContactAddress: () -> Unit,
|
||||
syncContactConnection: () -> Unit,
|
||||
syncContactConnectionForce: () -> Unit,
|
||||
allowContactPQ: () -> Unit,
|
||||
verifyClicked: () -> Unit,
|
||||
) {
|
||||
val cStats = connStats.value
|
||||
@@ -360,13 +345,9 @@ fun ChatInfoLayout(
|
||||
}
|
||||
|
||||
val conn = contact.activeConn
|
||||
if (pqExperimentalEnabled && conn != null) {
|
||||
SectionView("Quantum resistant E2E encryption") {
|
||||
if (conn != null) {
|
||||
SectionView {
|
||||
InfoRow("E2E encryption", if (conn.connPQEnabled) "Quantum resistant" else "Standard")
|
||||
if (!conn.pqEncryption) {
|
||||
AllowContactPQButton(allowContactPQ)
|
||||
SectionTextFooter("After allowing quantum resistant e2e encryption, it will be enabled after several messages if your contact also allows it.")
|
||||
}
|
||||
SectionDividerSpaced()
|
||||
}
|
||||
}
|
||||
@@ -627,17 +608,6 @@ fun SynchronizeConnectionButtonForce(syncConnectionForce: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AllowContactPQButton(allowContactPQ: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_warning),
|
||||
"Allow PQ encryption",
|
||||
click = allowContactPQ,
|
||||
textColor = WarningOrange,
|
||||
iconColor = WarningOrange
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VerifyCodeButton(contactVerified: Boolean, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
@@ -741,16 +711,6 @@ fun showSyncConnectionForceAlert(syncConnectionForce: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
fun showAllowContactPQAlert(allowContactPQ: () -> Unit) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = "Allow quantum resistant encryption?",
|
||||
text = "This is an experimental feature, it is not recommended to enable it for important chats.",
|
||||
confirmText = "Allow",
|
||||
onConfirm = allowContactPQ,
|
||||
destructive = true,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewChatInfoLayout() {
|
||||
@@ -768,7 +728,6 @@ fun PreviewChatInfoLayout() {
|
||||
localAlias = "",
|
||||
connectionCode = "123",
|
||||
developerTools = false,
|
||||
pqExperimentalEnabled = false,
|
||||
connStats = remember { mutableStateOf(null) },
|
||||
contactNetworkStatus = NetworkStatus.Connected(),
|
||||
onLocalAliasChanged = {},
|
||||
@@ -780,7 +739,6 @@ fun PreviewChatInfoLayout() {
|
||||
abortSwitchContactAddress = {},
|
||||
syncContactConnection = {},
|
||||
syncContactConnectionForce = {},
|
||||
allowContactPQ = {},
|
||||
verifyClicked = {},
|
||||
)
|
||||
}
|
||||
|
||||
-8
@@ -57,14 +57,6 @@ fun DeveloperView(
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_report), stringResource(MR.strings.show_internal_errors), appPreferences.showInternalErrors)
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_avg_pace), stringResource(MR.strings.show_slow_api_calls), appPreferences.showSlowApiCalls)
|
||||
}
|
||||
|
||||
SectionSpacer()
|
||||
SectionView("Experimental".uppercase()) {
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_vpn_key_filled), "Post-quantum E2EE", m.controller.appPrefs.pqExperimentalEnabled, onChange = { enable ->
|
||||
withBGApi { m.controller.apiSetPQEncryption(enable) }
|
||||
})
|
||||
SectionTextFooter("In this version applies only to new contacts.")
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: b08314722d2c8f4d640dd5baba73c71e09ff0dda
|
||||
tag: fe28e02be779fb0357a7f650bd327a75f4aaab95
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."b08314722d2c8f4d640dd5baba73c71e09ff0dda" = "18jzn66sx7iaaz0l12xbkwa2qvvz1mrj8b7i8rkxrcr2rhilabga";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."fe28e02be779fb0357a7f650bd327a75f4aaab95" = "1hwh0kx6ljskjx7svxpwmga79dfa2vz9qq0mwbqr6kkb9kf6qdd3";
|
||||
"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";
|
||||
|
||||
+63
-81
@@ -103,7 +103,7 @@ import Simplex.Messaging.Client (defaultNetworkConfig)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKNoPQ, pattern IKPQOff, pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import Simplex.Messaging.Encoding
|
||||
import Simplex.Messaging.Encoding.String
|
||||
@@ -250,7 +250,6 @@ newChatController
|
||||
encryptLocalFiles <- newTVarIO False
|
||||
tempDirectory <- newTVarIO optTempDirectory
|
||||
contactMergeEnabled <- newTVarIO True
|
||||
pqExperimentalEnabled <- newTVarIO PQSupportOff
|
||||
pure
|
||||
ChatController
|
||||
{ firstTime,
|
||||
@@ -287,8 +286,7 @@ newChatController
|
||||
encryptLocalFiles,
|
||||
tempDirectory,
|
||||
logFilePath = logFile,
|
||||
contactMergeEnabled,
|
||||
pqExperimentalEnabled
|
||||
contactMergeEnabled
|
||||
}
|
||||
where
|
||||
configServers :: DefaultAgentServers
|
||||
@@ -408,7 +406,7 @@ subscribeUsers onlyNeeded users = do
|
||||
subscribe vr us
|
||||
subscribe vr us'
|
||||
where
|
||||
subscribe :: (PQSupport -> VersionRangeChat) -> [User] -> CM' ()
|
||||
subscribe :: VersionRangeChat -> [User] -> CM' ()
|
||||
subscribe vr = mapM_ $ runExceptT . subscribeUserConnections vr onlyNeeded Agent.subscribeConnections
|
||||
|
||||
startFilesToReceive :: [User] -> CM' ()
|
||||
@@ -492,7 +490,7 @@ processChatCommand cmd =
|
||||
chatVersionRange >>= (`processChatCommand'` cmd)
|
||||
{-# INLINE processChatCommand #-}
|
||||
|
||||
processChatCommand' :: (PQSupport -> VersionRangeChat) -> ChatCommand -> CM ChatResponse
|
||||
processChatCommand' :: VersionRangeChat -> ChatCommand -> CM ChatResponse
|
||||
processChatCommand' vr = \case
|
||||
ShowActiveUser -> withUser' $ pure . CRActiveUser
|
||||
CreateActiveUser NewUser {profile, sameServers, pastTimestamp} -> do
|
||||
@@ -634,20 +632,6 @@ processChatCommand' vr = \case
|
||||
ok_
|
||||
APISetEncryptLocalFiles on -> chatWriteVar encryptLocalFiles on >> ok_
|
||||
SetContactMergeEnabled onOff -> chatWriteVar contactMergeEnabled onOff >> ok_
|
||||
APISetPQEncryption onOff -> chatWriteVar pqExperimentalEnabled onOff >> ok_
|
||||
APISetContactPQ ctId pqEnc -> withUser $ \user -> do
|
||||
ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user ctId
|
||||
case activeConn of
|
||||
Just conn@Connection {connId, pqSupport, pqEncryption}
|
||||
| pqEncryption == pqEnc -> pure $ CRContactPQAllowed user ct pqEnc
|
||||
| otherwise -> do
|
||||
let pqSup = PQSupport $ pqEnc == PQEncOn || pqSupport == PQSupportOn
|
||||
conn' = conn {pqSupport = pqSup, pqEncryption = pqEnc} :: Connection
|
||||
ct' = ct {activeConn = Just conn'} :: Contact
|
||||
withStore' $ \db -> updateConnSupportPQ db connId pqSup pqEnc
|
||||
pure $ CRContactPQAllowed user ct' pqEnc
|
||||
Nothing -> throwChatError $ CEContactNotActive ct
|
||||
SetContactPQ cName pqEnc -> withContactName cName (`APISetContactPQ` pqEnc)
|
||||
APIExportArchive cfg -> checkChatStopped $ lift (exportArchive cfg) >> ok_
|
||||
ExportArchive -> do
|
||||
ts <- liftIO getCurrentTime
|
||||
@@ -1470,10 +1454,9 @@ processChatCommand' vr = \case
|
||||
-- [incognito] generate profile for connection
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
pqSup <- chatReadVar pqExperimentalEnabled
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing (IKNoPQ pqSup) subMode
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOn subMode
|
||||
-- TODO PQ pass minVersion from the current range
|
||||
conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion pqSup
|
||||
conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion PQSupportOn
|
||||
pure $ CRInvitation user cReq conn
|
||||
AddContact incognito -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIAddContact userId incognito
|
||||
@@ -1499,8 +1482,7 @@ processChatCommand' vr = \case
|
||||
-- [incognito] generate profile to send
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
let profileToSend = userProfileToSend user incognitoProfile Nothing False
|
||||
pqSup <- chatReadVar pqExperimentalEnabled
|
||||
lift (withAgent' $ \a -> connRequestPQSupport a pqSup cReq) >>= \case
|
||||
lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOn cReq) >>= \case
|
||||
Nothing -> throwChatError CEInvalidConnReq
|
||||
-- TODO PQ the error above should be CEIncompatibleConnReqVersion, also the same API should be called in Plan
|
||||
Just (agentV, pqSup') -> do
|
||||
@@ -1541,8 +1523,7 @@ processChatCommand' vr = \case
|
||||
processChatCommand $ APIListContacts userId
|
||||
APICreateMyAddress userId -> withUserId userId $ \user -> procCmd $ do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- TODO v5.7 pass IPPQOn
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing IKPQOff subMode
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing IKPQOn subMode
|
||||
withStore $ \db -> createUserContactLink db user connId cReq subMode
|
||||
pure $ CRUserContactLinkCreated user cReq
|
||||
CreateMyAddress -> withUser $ \User {userId} ->
|
||||
@@ -1671,8 +1652,8 @@ processChatCommand' vr = \case
|
||||
addContactConn ct ctConns = case contactSendConn_ ct of
|
||||
Right conn | directOrUsed ct -> (ct, conn) : ctConns
|
||||
_ -> ctConns
|
||||
ctSndEvent :: (Contact, Connection) -> (ConnOrGroupId, PQSupport, ChatMsgEvent 'Json)
|
||||
ctSndEvent (_, Connection {connId, pqSupport}) = (ConnectionId connId, pqSupport, XMsgNew $ MCSimple (extMsgContent mc Nothing))
|
||||
ctSndEvent :: (Contact, Connection) -> (ConnOrGroupId, ChatMsgEvent 'Json)
|
||||
ctSndEvent (_, Connection {connId}) = (ConnectionId connId, XMsgNew $ MCSimple (extMsgContent mc Nothing))
|
||||
ctMsgReq :: (Contact, Connection) -> SndMessage -> MsgReq
|
||||
ctMsgReq (_, conn) SndMessage {msgId, msgBody} = (conn, MsgFlags {notification = hasNotification XMsgNew_}, msgBody, msgId)
|
||||
zipWith3' :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
|
||||
@@ -1762,7 +1743,7 @@ processChatCommand' vr = \case
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
dm <- encodeConnInfo $ XGrpAcpt membershipMemId
|
||||
agentConnId <- withAgent $ \a -> joinConnection a (aUserId user) True connRequest dm PQSupportOff subMode
|
||||
let chatV = vr PQSupportOff `peerConnChatVersion` peerChatVRange
|
||||
let chatV = vr `peerConnChatVersion` peerChatVRange
|
||||
withStore' $ \db -> do
|
||||
createMemberConnection db userId fromMember agentConnId chatV peerChatVRange subMode
|
||||
updateGroupMemberStatus db userId fromMember GSMemAccepted
|
||||
@@ -1925,6 +1906,7 @@ processChatCommand' vr = \case
|
||||
unless (maxVersion peerChatVRange >= groupDirectInvVersion) $ throwChatError CEPeerChatVRangeIncompatible
|
||||
when (isJust $ memberContactId m) $ throwChatError $ CECommandError "member contact already exists"
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- TODO PQ should negotitate contact connection with PQSupportOn?
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOff subMode
|
||||
-- [incognito] reuse membership incognito profile
|
||||
ct <- withStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode
|
||||
@@ -2289,7 +2271,7 @@ processChatCommand' vr = \case
|
||||
connect' (Just gLinkId) cReqHash xContactId True
|
||||
where
|
||||
connect' groupLinkId cReqHash xContactId inGroup = do
|
||||
pqSup <- if inGroup then pure PQSupportOff else chatReadVar pqExperimentalEnabled
|
||||
let pqSup = if inGroup then PQSupportOff else PQSupportOn
|
||||
(connId, incognitoProfile, subMode, chatV) <- requestContact user incognito cReq xContactId inGroup pqSup
|
||||
conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup
|
||||
pure $ CRSentInvitation user conn incognitoProfile
|
||||
@@ -2297,7 +2279,7 @@ processChatCommand' vr = \case
|
||||
connectContactViaAddress user incognito ct cReq =
|
||||
withInvitationLock "connectContactViaAddress" (strEncode cReq) $ do
|
||||
newXContactId <- XContactId <$> drgRandomBytes 16
|
||||
pqSup <- chatReadVar pqExperimentalEnabled
|
||||
let pqSup = PQSupportOn
|
||||
(connId, incognitoProfile, subMode, chatV) <- requestContact user incognito cReq newXContactId False pqSup
|
||||
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
|
||||
ct' <- withStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash newXContactId incognitoProfile subMode chatV pqSup
|
||||
@@ -2370,8 +2352,8 @@ processChatCommand' vr = \case
|
||||
mergedProfile = userProfileToSend user Nothing (Just ct) False
|
||||
ct' = updateMergedPreferences user' ct
|
||||
mergedProfile' = userProfileToSend user' Nothing (Just ct') False
|
||||
ctSndEvent :: ChangedProfileContact -> (ConnOrGroupId, PQSupport, ChatMsgEvent 'Json)
|
||||
ctSndEvent ChangedProfileContact {mergedProfile', conn = Connection {connId, pqSupport}} = (ConnectionId connId, pqSupport, XInfo mergedProfile')
|
||||
ctSndEvent :: ChangedProfileContact -> (ConnOrGroupId, ChatMsgEvent 'Json)
|
||||
ctSndEvent ChangedProfileContact {mergedProfile', conn = Connection {connId}} = (ConnectionId connId, XInfo mergedProfile')
|
||||
ctMsgReq :: ChangedProfileContact -> Either ChatError SndMessage -> Either ChatError MsgReq
|
||||
ctMsgReq ChangedProfileContact {conn} =
|
||||
fmap $ \SndMessage {msgId, msgBody} ->
|
||||
@@ -3116,10 +3098,10 @@ getRcvFilePath fileId fPath_ fn keepHandle = case fPath_ of
|
||||
acceptContactRequest :: User -> UserContactRequest -> Maybe IncognitoProfile -> Bool -> CM Contact
|
||||
acceptContactRequest user UserContactRequest {agentInvitationId = AgentInvId invId, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId, xContactId, pqSupport} incognitoProfile contactUsed = do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
pqSup <- chatReadVar pqExperimentalEnabled
|
||||
let pqSup = PQSupportOn
|
||||
vr <- chatVersionRange
|
||||
let profileToSend = profileToSendOnAccept user incognitoProfile False
|
||||
chatV = vr pqSup `peerConnChatVersion` cReqChatVRange
|
||||
chatV = vr `peerConnChatVersion` cReqChatVRange
|
||||
pqSup' = pqSup `CR.pqSupportAnd` pqSupport
|
||||
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
|
||||
acId <- withAgent $ \a -> acceptContact a True invId dm pqSup' subMode
|
||||
@@ -3130,7 +3112,7 @@ acceptContactRequestAsync user UserContactRequest {agentInvitationId = AgentInvI
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let profileToSend = profileToSendOnAccept user incognitoProfile False
|
||||
vr <- chatVersionRange
|
||||
chatV <- (\pq -> vr pq `peerConnChatVersion` cReqChatVRange) <$> chatReadVar pqExperimentalEnabled
|
||||
let chatV = vr `peerConnChatVersion` cReqChatVRange
|
||||
(cmdId, acId) <- agentAcceptContactAsync user True invId (XInfo profileToSend) subMode pqSup chatV
|
||||
withStore' $ \db -> do
|
||||
ct@Contact {activeConn} <- createAcceptedContact db user acId chatV cReqChatVRange cName profileId p userContactLinkId xContactId incognitoProfile subMode pqSup contactUsed
|
||||
@@ -3160,7 +3142,7 @@ acceptGroupJoinRequestAsync
|
||||
}
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
vr <- chatVersionRange
|
||||
chatV <- (\pq -> vr pq `peerConnChatVersion` cReqChatVRange) <$> chatReadVar pqExperimentalEnabled
|
||||
let chatV = vr `peerConnChatVersion` cReqChatVRange
|
||||
connIds <- agentAcceptContactAsync user True invId msg subMode PQSupportOff chatV
|
||||
withStore $ \db -> do
|
||||
liftIO $ createAcceptedMemberConnection db user connIds chatV ucr groupMemberId subMode
|
||||
@@ -3206,7 +3188,7 @@ agentSubscriber = do
|
||||
|
||||
type AgentBatchSubscribe = AgentClient -> [ConnId] -> ExceptT AgentErrorType IO (Map ConnId (Either AgentErrorType ()))
|
||||
|
||||
subscribeUserConnections :: (PQSupport -> VersionRangeChat) -> Bool -> AgentBatchSubscribe -> User -> CM ()
|
||||
subscribeUserConnections :: VersionRangeChat -> Bool -> AgentBatchSubscribe -> User -> CM ()
|
||||
subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
-- get user connections
|
||||
ce <- asks $ subscriptionEvents . config
|
||||
@@ -3500,7 +3482,7 @@ expireChatItems user@User {userId} ttl sync = do
|
||||
cancelFilesInProgress user filesInfo
|
||||
deleteFilesLocally filesInfo
|
||||
withStore' $ \db -> deleteContactExpiredCIs db user ct expirationDate
|
||||
processGroup :: (PQSupport -> VersionRangeChat) -> UTCTime -> UTCTime -> GroupInfo -> CM ()
|
||||
processGroup :: VersionRangeChat -> UTCTime -> UTCTime -> GroupInfo -> CM ()
|
||||
processGroup vr expirationDate createdAtCutoff gInfo = do
|
||||
lift waitChatStartedAndActivated
|
||||
filesInfo <- withStore' $ \db -> getGroupExpiredFileInfo db user gInfo expirationDate createdAtCutoff
|
||||
@@ -3658,7 +3640,7 @@ processAgentMsgSndFile _corrId aFileId msg = do
|
||||
case L.nonEmpty fds of
|
||||
Just fds' -> loopSend fds'
|
||||
Nothing -> pure msgDeliveryId
|
||||
sendFileError :: Text -> (PQSupport -> VersionRangeChat) -> FileTransferMeta -> CM ()
|
||||
sendFileError :: Text -> VersionRangeChat -> FileTransferMeta -> CM ()
|
||||
sendFileError err vr ft = do
|
||||
logError $ "Sent file error: " <> err
|
||||
ci <- withStore $ \db -> do
|
||||
@@ -3724,7 +3706,7 @@ processAgentMsgRcvFile _corrId aFileId msg = do
|
||||
agentXFTPDeleteRcvFile aFileId fileId
|
||||
toView $ CRRcvFileError user ci e ft
|
||||
|
||||
processAgentMessageConn :: (PQSupport -> VersionRangeChat) -> User -> ACorrId -> ConnId -> ACommand 'Agent 'AEConn -> CM ()
|
||||
processAgentMessageConn :: VersionRangeChat -> User -> ACorrId -> ConnId -> ACommand 'Agent 'AEConn -> CM ()
|
||||
processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = do
|
||||
-- Missing connection/entity errors here will be sent to the view but not shown as CRITICAL alert,
|
||||
-- as in this case no need to ACK message - we can't process messages for this connection anyway.
|
||||
@@ -4105,7 +4087,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
profileToSend = profileToSendOnAccept user profileMode True
|
||||
void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId
|
||||
sendIntroductions members = do
|
||||
intros <- withStore' $ \db -> createIntroductions db (maxVersion $ vr PQSupportOff) members m
|
||||
intros <- withStore' $ \db -> createIntroductions db (maxVersion vr) members m
|
||||
shuffledIntros <- liftIO $ shuffleIntros intros
|
||||
if m `supportsVersion` batchSendVersion
|
||||
then do
|
||||
@@ -4557,9 +4539,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
Nothing -> do
|
||||
-- [incognito] generate profile to send, create connection with incognito profile
|
||||
incognitoProfile <- if acceptIncognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
|
||||
pqSup <- chatReadVar pqExperimentalEnabled
|
||||
let pqSup' = pqSup `CR.pqSupportAnd` reqPQSup
|
||||
ct <- acceptContactRequestAsync user cReq incognitoProfile True pqSup'
|
||||
ct <- acceptContactRequestAsync user cReq incognitoProfile True reqPQSup
|
||||
toView $ CRAcceptingContactRequest user ct
|
||||
Just groupId -> do
|
||||
gInfo <- withStore $ \db -> getGroupInfo db vr user groupId
|
||||
@@ -5644,8 +5624,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
| maxVersion mcvr >= groupDirectInvVersion -> pure Nothing
|
||||
| otherwise -> Just <$> createConn subMode
|
||||
let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo
|
||||
vr' = vr PQSupportOff
|
||||
chatV = maybe (minVersion vr') (\peerVR -> vr' `peerConnChatVersion` fromChatVRange peerVR) memChatVRange
|
||||
chatV = maybe (minVersion vr) (\peerVR -> vr `peerConnChatVersion` fromChatVRange peerVR) memChatVRange
|
||||
void $ withStore $ \db -> createIntroReMember db user gInfo m chatV memInfo memRestrictions groupConnIds directConnIds customUserProfileId subMode
|
||||
_ -> messageError "x.grp.mem.intro can be only sent by host member"
|
||||
where
|
||||
@@ -5693,7 +5672,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
directConnIds <- forM directConnReq $ \dcr -> joinAgentConnectionAsync user True dcr dm subMode
|
||||
let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo
|
||||
mcvr = maybe chatInitialVRange fromChatVRange memChatVRange
|
||||
chatV = vr PQSupportOff `peerConnChatVersion` mcvr
|
||||
chatV = vr `peerConnChatVersion` mcvr
|
||||
withStore' $ \db -> createIntroToMemberContact db user m toMember chatV mcvr groupConnIds directConnIds customUserProfileId subMode
|
||||
|
||||
xGrpMemRole :: GroupInfo -> GroupMember -> MemberId -> GroupMemberRole -> RcvMessage -> UTCTime -> CM ()
|
||||
@@ -5892,6 +5871,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
joinConn subMode = do
|
||||
-- [incognito] send membership incognito profile
|
||||
let p = userProfileToSend user (fromLocalProfile <$> incognitoMembershipProfile g) Nothing False
|
||||
-- TODO PQ should negotitate contact connection with PQSupportOn? (use encodeConnInfoPQ)
|
||||
dm <- encodeConnInfo $ XInfo p
|
||||
joinAgentConnectionAsync user True connReq dm subMode
|
||||
createItems mCt' m' = do
|
||||
@@ -6048,19 +6028,24 @@ metaBrokerTs MsgMeta {broker = (_, brokerTs)} = brokerTs
|
||||
sameMemberId :: MemberId -> GroupMember -> Bool
|
||||
sameMemberId memId GroupMember {memberId} = memId == memberId
|
||||
|
||||
-- TODO v5.7 for contacts only version upgrade should trigger enabling PQ support/encryption
|
||||
updatePeerChatVRange :: Connection -> VersionRangeChat -> CM Connection
|
||||
updatePeerChatVRange conn@Connection {connId, connChatVersion = v, peerChatVRange, pqSupport} msgVRange = do
|
||||
v' <- lift $ upgradedConnVersion pqSupport v msgVRange
|
||||
if msgVRange /= peerChatVRange || v' /= v
|
||||
updatePeerChatVRange conn@Connection {connId, connChatVersion = v, peerChatVRange, connType, pqSupport, pqEncryption} msgVRange = do
|
||||
v' <- lift $ upgradedConnVersion v msgVRange
|
||||
conn' <- if msgVRange /= peerChatVRange || v' /= v
|
||||
then do
|
||||
withStore' $ \db -> setPeerChatVRange db connId v' msgVRange
|
||||
pure conn {connChatVersion = v', peerChatVRange = msgVRange}
|
||||
else pure conn
|
||||
-- TODO v6.0 remove/review: for contacts only version upgrade should trigger enabling PQ support/encryption
|
||||
if connType == ConnContact && v' >= pqEncryptionCompressionVersion && (pqSupport /= PQSupportOn || pqEncryption /= PQEncOn)
|
||||
then do
|
||||
withStore' $ \db -> updateConnSupportPQ db connId PQSupportOn PQEncOn
|
||||
pure conn' {pqSupport = PQSupportOn, pqEncryption = PQEncOn}
|
||||
else pure conn'
|
||||
|
||||
updateMemberChatVRange :: GroupMember -> Connection -> VersionRangeChat -> CM (GroupMember, Connection)
|
||||
updateMemberChatVRange mem@GroupMember {groupMemberId} conn@Connection {connId, connChatVersion = v, peerChatVRange} msgVRange = do
|
||||
v' <- lift $ upgradedConnVersion PQSupportOff v msgVRange
|
||||
v' <- lift $ upgradedConnVersion v msgVRange
|
||||
if msgVRange /= peerChatVRange || v' /= v
|
||||
then do
|
||||
withStore' $ \db -> do
|
||||
@@ -6070,11 +6055,11 @@ updateMemberChatVRange mem@GroupMember {groupMemberId} conn@Connection {connId,
|
||||
pure (mem {memberChatVRange = msgVRange, activeConn = Just conn'}, conn')
|
||||
else pure (mem, conn)
|
||||
|
||||
upgradedConnVersion :: PQSupport -> VersionChat -> VersionRangeChat -> CM' VersionChat
|
||||
upgradedConnVersion pqSup v peerVR = do
|
||||
upgradedConnVersion :: VersionChat -> VersionRangeChat -> CM' VersionChat
|
||||
upgradedConnVersion v peerVR = do
|
||||
vr <- chatVersionRange'
|
||||
-- don't allow reducing agreed connection version
|
||||
pure $ maybe v (\(Compatible v') -> max v v') $ vr pqSup `compatibleVersion` peerVR
|
||||
pure $ maybe v (\(Compatible v') -> max v v') $ vr `compatibleVersion` peerVR
|
||||
|
||||
parseFileDescription :: FilePartyI p => Text -> CM (ValidFileDescription p)
|
||||
parseFileDescription =
|
||||
@@ -6241,7 +6226,7 @@ cancelSndFileTransfer user@User {userId} ft@SndFileTransfer {fileId, connId, age
|
||||
Just _ -> do
|
||||
vr <- chatVersionRange
|
||||
(sharedMsgId, conn) <- withStore $ \db -> (,) <$> getSharedMsgIdByFileId db userId fileId <*> getConnectionById db vr user connId
|
||||
void $ sendDirectMessage_ conn PQSupportOff (BFileChunk sharedMsgId FileChunkCancel) (ConnectionId connId)
|
||||
void $ sendDirectMessage_ conn (BFileChunk sharedMsgId FileChunkCancel) (ConnectionId connId)
|
||||
_ -> withAgent $ \a -> void . sendMessage a acId PQEncOff SMP.noMsgFlags $ smpEncode FileChunkCancel
|
||||
pure fileConnId
|
||||
fileConnId = if isNothing fileInline then Just acId else Nothing
|
||||
@@ -6281,8 +6266,8 @@ deleteOrUpdateMemberRecord user@User {userId} member =
|
||||
|
||||
sendDirectContactMessage :: MsgEncodingI e => User -> Contact -> ChatMsgEvent e -> CM (SndMessage, Int64)
|
||||
sendDirectContactMessage user ct chatMsgEvent = do
|
||||
conn@Connection {connId, pqSupport} <- liftEither $ contactSendConn_ ct
|
||||
r <- sendDirectMessage_ conn pqSupport chatMsgEvent (ConnectionId connId)
|
||||
conn@Connection {connId} <- liftEither $ contactSendConn_ ct
|
||||
r <- sendDirectMessage_ conn chatMsgEvent (ConnectionId connId)
|
||||
let (sndMessage, msgDeliveryId, pqEnc') = r
|
||||
void $ createContactPQSndItem user ct conn pqEnc'
|
||||
pure (sndMessage, msgDeliveryId)
|
||||
@@ -6301,37 +6286,37 @@ contactSendConn_ ct@Contact {activeConn} = case activeConn of
|
||||
-- unlike sendGroupMemberMessage, this function will not store message as pending
|
||||
-- TODO v5.8 we could remove pending messages once all clients support forwarding
|
||||
sendDirectMemberMessage :: MsgEncodingI e => Connection -> ChatMsgEvent e -> GroupId -> CM (SndMessage, Int64, PQEncryption)
|
||||
sendDirectMemberMessage conn chatMsgEvent groupId = sendDirectMessage_ conn PQSupportOff chatMsgEvent (GroupId groupId)
|
||||
sendDirectMemberMessage conn chatMsgEvent groupId = sendDirectMessage_ conn chatMsgEvent (GroupId groupId)
|
||||
|
||||
sendDirectMessage_ :: MsgEncodingI e => Connection -> PQSupport -> ChatMsgEvent e -> ConnOrGroupId -> CM (SndMessage, Int64, PQEncryption)
|
||||
sendDirectMessage_ conn pqSup chatMsgEvent connOrGroupId = do
|
||||
sendDirectMessage_ :: MsgEncodingI e => Connection -> ChatMsgEvent e -> ConnOrGroupId -> CM (SndMessage, Int64, PQEncryption)
|
||||
sendDirectMessage_ conn chatMsgEvent connOrGroupId = do
|
||||
when (connDisabled conn) $ throwChatError (CEConnectionDisabled conn)
|
||||
msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent connOrGroupId pqSup
|
||||
msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent connOrGroupId
|
||||
-- TODO move compressed body to SndMessage and compress in createSndMessage
|
||||
(msgDeliveryId, pqEnc') <- deliverMessage conn (toCMEventTag chatMsgEvent) msgBody msgId
|
||||
pure (msg, msgDeliveryId, pqEnc')
|
||||
|
||||
createSndMessage :: MsgEncodingI e => ChatMsgEvent e -> ConnOrGroupId -> PQSupport -> CM SndMessage
|
||||
createSndMessage chatMsgEvent connOrGroupId pqSup =
|
||||
liftEither . runIdentity =<< lift (createSndMessages $ Identity (connOrGroupId, pqSup, chatMsgEvent))
|
||||
createSndMessage :: MsgEncodingI e => ChatMsgEvent e -> ConnOrGroupId -> CM SndMessage
|
||||
createSndMessage chatMsgEvent connOrGroupId =
|
||||
liftEither . runIdentity =<< lift (createSndMessages $ Identity (connOrGroupId, chatMsgEvent))
|
||||
|
||||
createSndMessages :: forall e t. (MsgEncodingI e, Traversable t) => t (ConnOrGroupId, PQSupport, ChatMsgEvent e) -> CM' (t (Either ChatError SndMessage))
|
||||
createSndMessages :: forall e t. (MsgEncodingI e, Traversable t) => t (ConnOrGroupId, ChatMsgEvent e) -> CM' (t (Either ChatError SndMessage))
|
||||
createSndMessages idsEvents = do
|
||||
g <- asks random
|
||||
vr <- chatVersionRange'
|
||||
withStoreBatch $ \db -> fmap (createMsg db g vr) idsEvents
|
||||
where
|
||||
createMsg :: DB.Connection -> TVar ChaChaDRG -> (PQSupport -> VersionRangeChat) -> (ConnOrGroupId, PQSupport, ChatMsgEvent e) -> IO (Either ChatError SndMessage)
|
||||
createMsg db g vr (connOrGroupId, pqSup, evnt) = runExceptT $ do
|
||||
createMsg :: DB.Connection -> TVar ChaChaDRG -> VersionRangeChat -> (ConnOrGroupId, ChatMsgEvent e) -> IO (Either ChatError SndMessage)
|
||||
createMsg db g vr (connOrGroupId, evnt) = runExceptT $ do
|
||||
withExceptT ChatErrorStore $ createNewSndMessage db g connOrGroupId evnt encodeMessage
|
||||
where
|
||||
encodeMessage sharedMsgId =
|
||||
encodeChatMessage maxEncodedMsgLength ChatMessage {chatVRange = vr pqSup, msgId = Just sharedMsgId, chatMsgEvent = evnt}
|
||||
encodeChatMessage maxEncodedMsgLength ChatMessage {chatVRange = vr, msgId = Just sharedMsgId, chatMsgEvent = evnt}
|
||||
|
||||
sendGroupMemberMessages :: forall e. MsgEncodingI e => User -> Connection -> NonEmpty (ChatMsgEvent e) -> GroupId -> CM ()
|
||||
sendGroupMemberMessages user conn events groupId = do
|
||||
when (connDisabled conn) $ throwChatError (CEConnectionDisabled conn)
|
||||
let idsEvts = L.map (GroupId groupId,PQSupportOff,) events
|
||||
let idsEvts = L.map (GroupId groupId,) events
|
||||
(errs, msgs) <- lift $ partitionEithers . L.toList <$> createSndMessages idsEvts
|
||||
unless (null errs) $ toView $ CRChatErrors (Just user) errs
|
||||
forM_ (L.nonEmpty msgs) $ \msgs' -> do
|
||||
@@ -6367,12 +6352,12 @@ batchSndMessagesJSON = batchMessages maxEncodedMsgLength . L.toList
|
||||
encodeConnInfo :: MsgEncodingI e => ChatMsgEvent e -> CM ByteString
|
||||
encodeConnInfo chatMsgEvent = do
|
||||
vr <- chatVersionRange
|
||||
encodeConnInfoPQ PQSupportOff (maxVersion $ vr PQSupportOff) chatMsgEvent
|
||||
encodeConnInfoPQ PQSupportOff (maxVersion vr) chatMsgEvent
|
||||
|
||||
encodeConnInfoPQ :: MsgEncodingI e => PQSupport -> VersionChat -> ChatMsgEvent e -> CM ByteString
|
||||
encodeConnInfoPQ pqSup v chatMsgEvent = do
|
||||
vr <- chatVersionRange
|
||||
let info = ChatMessage {chatVRange = vr pqSup, msgId = Nothing, chatMsgEvent}
|
||||
let info = ChatMessage {chatVRange = vr, msgId = Nothing, chatMsgEvent}
|
||||
case encodeChatMessage maxEncodedInfoLength info of
|
||||
ECMEncoded connInfo -> case pqSup of
|
||||
PQSupportOn | v >= pqEncryptionCompressionVersion && B.length connInfo > maxCompressedInfoLength -> do
|
||||
@@ -6459,7 +6444,7 @@ sendGroupMessage user gInfo members chatMsgEvent = do
|
||||
|
||||
sendGroupMessage' :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, [GroupMember])
|
||||
sendGroupMessage' user GroupInfo {groupId} members chatMsgEvent = do
|
||||
msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent (GroupId groupId) PQSupportOff
|
||||
msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent (GroupId groupId)
|
||||
recipientMembers <- liftIO $ shuffleMembers (filter memberCurrent members)
|
||||
let msgFlags = MsgFlags {notification = hasNotification $ toCMEventTag chatMsgEvent}
|
||||
(toSend, pending) = foldr addMember ([], []) recipientMembers
|
||||
@@ -6514,7 +6499,7 @@ memberSendAction chatMsgEvent members m@GroupMember {invitedByGroupMemberId} = c
|
||||
|
||||
sendGroupMemberMessage :: MsgEncodingI e => User -> GroupMember -> ChatMsgEvent e -> Int64 -> Maybe Int64 -> CM () -> CM ()
|
||||
sendGroupMemberMessage user m@GroupMember {groupMemberId} chatMsgEvent groupId introId_ postDeliver = do
|
||||
msg <- createSndMessage chatMsgEvent (GroupId groupId) PQSupportOff
|
||||
msg <- createSndMessage chatMsgEvent (GroupId groupId)
|
||||
messageMember msg `catchChatError` (\e -> toView (CRChatError (Just user) e))
|
||||
where
|
||||
messageMember :: SndMessage -> CM ()
|
||||
@@ -6931,11 +6916,11 @@ waitChatStartedAndActivated = do
|
||||
activated <- readTVar chatActivated
|
||||
unless (isJust started && activated) retry
|
||||
|
||||
chatVersionRange :: CM (PQSupport -> VersionRangeChat)
|
||||
chatVersionRange :: CM VersionRangeChat
|
||||
chatVersionRange = lift chatVersionRange'
|
||||
{-# INLINE chatVersionRange #-}
|
||||
|
||||
chatVersionRange' :: CM' (PQSupport -> VersionRangeChat)
|
||||
chatVersionRange' :: CM' VersionRangeChat
|
||||
chatVersionRange' = do
|
||||
ChatConfig {chatVRange} <- asks config
|
||||
pure chatVRange
|
||||
@@ -6983,9 +6968,6 @@ chatCommandP =
|
||||
"/remote_hosts_folder " *> (SetRemoteHostsFolder <$> filePath),
|
||||
"/_files_encrypt " *> (APISetEncryptLocalFiles <$> onOffP),
|
||||
"/contact_merge " *> (SetContactMergeEnabled <$> onOffP),
|
||||
"/_pq @" *> (APISetContactPQ <$> A.decimal <* A.space <*> (PQEncryption <$> onOffP)),
|
||||
"/pq @" *> (SetContactPQ <$> displayName <* A.space <*> (PQEncryption <$> onOffP)),
|
||||
"/pq " *> (APISetPQEncryption . PQSupport <$> onOffP),
|
||||
"/_db export " *> (APIExportArchive <$> jsonP),
|
||||
"/db export" $> ExportArchive,
|
||||
"/_db import " *> (APIImportArchive <$> jsonP),
|
||||
|
||||
@@ -76,7 +76,7 @@ import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption, PQSupport (..))
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption)
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
|
||||
@@ -126,7 +126,7 @@ coreVersionInfo simplexmqCommit =
|
||||
|
||||
data ChatConfig = ChatConfig
|
||||
{ agentConfig :: AgentConfig,
|
||||
chatVRange :: PQSupport -> VersionRangeChat,
|
||||
chatVRange :: VersionRangeChat,
|
||||
confirmMigrations :: MigrationConfirmation,
|
||||
defaultServers :: DefaultAgentServers,
|
||||
tbqSize :: Natural,
|
||||
@@ -230,8 +230,7 @@ data ChatController = ChatController
|
||||
encryptLocalFiles :: TVar Bool,
|
||||
tempDirectory :: TVar (Maybe FilePath),
|
||||
logFilePath :: Maybe FilePath,
|
||||
contactMergeEnabled :: TVar Bool,
|
||||
pqExperimentalEnabled :: TVar PQSupport -- TODO v5.7 remove
|
||||
contactMergeEnabled :: TVar Bool
|
||||
}
|
||||
|
||||
data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSRemote | HSSettings | HSDatabase
|
||||
@@ -268,9 +267,6 @@ data ChatCommand
|
||||
| SetRemoteHostsFolder FilePath
|
||||
| APISetEncryptLocalFiles Bool
|
||||
| SetContactMergeEnabled Bool
|
||||
| APISetPQEncryption PQSupport
|
||||
| APISetContactPQ ContactId PQEncryption
|
||||
| SetContactPQ ContactName PQEncryption
|
||||
| APIExportArchive ArchiveConfig
|
||||
| ExportArchive
|
||||
| APIImportArchive ArchiveConfig
|
||||
@@ -736,7 +732,6 @@ data ChatResponse
|
||||
| CRRemoteCtrlSessionCode {remoteCtrl_ :: Maybe RemoteCtrlInfo, sessionCode :: Text}
|
||||
| CRRemoteCtrlConnected {remoteCtrl :: RemoteCtrlInfo}
|
||||
| CRRemoteCtrlStopped {rcsState :: RemoteCtrlSessionState, rcStopReason :: RemoteCtrlStopReason}
|
||||
| CRContactPQAllowed {user :: User, contact :: Contact, pqEncryption :: PQEncryption}
|
||||
| CRContactPQEnabled {user :: User, contact :: Contact, pqEnabled :: PQEncryption}
|
||||
| CRSQLResult {rows :: [Text]}
|
||||
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
|
||||
|
||||
@@ -49,7 +49,6 @@ import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.Util
|
||||
import Simplex.Messaging.Agent.Protocol (VersionSMPA, pqdrSMPAgentVersion)
|
||||
import Simplex.Messaging.Compression (compress1, decompressBatch)
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQSupport (..), pattern PQSupportOff, pattern PQSupportOn)
|
||||
import Simplex.Messaging.Encoding
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, fromTextField_, fstToLower, parseAll, sumTypeJSON, taggedObjectJSON)
|
||||
@@ -70,14 +69,11 @@ import Simplex.Messaging.Version hiding (version)
|
||||
-- This indirection is needed for backward/forward compatibility testing.
|
||||
-- Testing with real app versions is still needed, as tests use the current code with different version ranges, not the old code.
|
||||
currentChatVersion :: VersionChat
|
||||
currentChatVersion = VersionChat 7
|
||||
currentChatVersion = VersionChat 8
|
||||
|
||||
-- This should not be used directly in code, instead use `chatVRange` from ChatConfig (see comment above)
|
||||
-- TODO remove parameterization in 5.7
|
||||
supportedChatVRange :: PQSupport -> VersionRangeChat
|
||||
supportedChatVRange pq = mkVersionRange initialChatVersion $ case pq of
|
||||
PQSupportOn -> pqEncryptionCompressionVersion
|
||||
PQSupportOff -> currentChatVersion
|
||||
supportedChatVRange :: VersionRangeChat
|
||||
supportedChatVRange = mkVersionRange initialChatVersion currentChatVersion
|
||||
{-# INLINE supportedChatVRange #-}
|
||||
|
||||
-- version range that supports skipping establishing direct connections in a group and establishing direct connection via x.grp.direct.inv
|
||||
|
||||
@@ -36,7 +36,6 @@ import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId)
|
||||
import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow', maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQSupport)
|
||||
import Simplex.Messaging.Util (eitherToMaybe)
|
||||
|
||||
getChatLockEntity :: DB.Connection -> AgentConnId -> ExceptT StoreError IO ChatLockEntity
|
||||
@@ -64,7 +63,7 @@ getChatLockEntity db agentConnId = do
|
||||
ExceptT . firstRow fromOnly (SEInternalError "group member connection group_id not found") $
|
||||
DB.query db "SELECT group_id FROM group_members WHERE group_member_id = ?" (Only groupMemberId)
|
||||
|
||||
getConnectionEntity :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> AgentConnId -> ExceptT StoreError IO ConnectionEntity
|
||||
getConnectionEntity :: DB.Connection -> VersionRangeChat -> User -> AgentConnId -> ExceptT StoreError IO ConnectionEntity
|
||||
getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
c@Connection {connType, entityId} <- getConnection_
|
||||
case entityId of
|
||||
@@ -185,7 +184,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
userContact_ [(cReq, groupId)] = Right UserContact {userContactLinkId, connReqContact = cReq, groupId}
|
||||
userContact_ _ = Left SEUserContactLinkNotFound
|
||||
|
||||
getConnectionEntityByConnReq :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity)
|
||||
getConnectionEntityByConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity)
|
||||
getConnectionEntityByConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
connId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
@@ -196,7 +195,7 @@ getConnectionEntityByConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2)
|
||||
-- multiple connections can have same via_contact_uri_hash if request was repeated;
|
||||
-- this function searches for latest connection with contact so that "known contact" plan would be chosen;
|
||||
-- deleted connections are filtered out to allow re-connecting via same contact address
|
||||
getContactConnEntityByConnReqHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity)
|
||||
getContactConnEntityByConnReqHash :: DB.Connection -> VersionRangeChat -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity)
|
||||
getContactConnEntityByConnReqHash db vr user@User {userId} (cReqHash1, cReqHash2) = do
|
||||
connId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
@@ -216,7 +215,7 @@ getContactConnEntityByConnReqHash db vr user@User {userId} (cReqHash1, cReqHash2
|
||||
(userId, cReqHash1, cReqHash2, ConnDeleted)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db vr user) connId_
|
||||
|
||||
getConnectionsToSubscribe :: DB.Connection -> (PQSupport -> VersionRangeChat) -> IO ([ConnId], [ConnectionEntity])
|
||||
getConnectionsToSubscribe :: DB.Connection -> VersionRangeChat -> IO ([ConnId], [ConnectionEntity])
|
||||
getConnectionsToSubscribe db vr = do
|
||||
aConnIds <- map fromOnly <$> DB.query_ db "SELECT agent_conn_id FROM connections where to_subscribe = 1"
|
||||
entities <- forM aConnIds $ \acId -> do
|
||||
|
||||
@@ -126,7 +126,7 @@ deletePendingContactConnection db userId connId =
|
||||
|]
|
||||
(userId, connId, ConnContact)
|
||||
|
||||
createAddressContactConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> ExceptT StoreError IO Contact
|
||||
createAddressContactConnection :: DB.Connection -> VersionRangeChat -> User -> Contact -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> ExceptT StoreError IO Contact
|
||||
createAddressContactConnection db vr user@User {userId} Contact {contactId} acId cReqHash xContactId incognitoProfile subMode chatV pqSup = do
|
||||
PendingContactConnection {pccConnId} <- liftIO $ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile Nothing subMode chatV pqSup
|
||||
liftIO $ DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, pccConnId)
|
||||
@@ -153,7 +153,7 @@ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile grou
|
||||
pccConnId <- insertedRowId db
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = True, viaUserContactLink = Nothing, groupLinkId, customUserProfileId, connReqInv = Nothing, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
|
||||
getConnReqContactXContactId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ConnReqUriHash -> IO (Maybe Contact, Maybe XContactId)
|
||||
getConnReqContactXContactId :: DB.Connection -> VersionRangeChat -> User -> ConnReqUriHash -> IO (Maybe Contact, Maybe XContactId)
|
||||
getConnReqContactXContactId db vr user@User {userId} cReqHash = do
|
||||
getContactByConnReqHash db vr user cReqHash >>= \case
|
||||
c@(Just _) -> pure (c, Nothing)
|
||||
@@ -167,7 +167,7 @@ getConnReqContactXContactId db vr user@User {userId} cReqHash = do
|
||||
"SELECT xcontact_id FROM connections WHERE user_id = ? AND via_contact_uri_hash = ? LIMIT 1"
|
||||
(userId, cReqHash)
|
||||
|
||||
getContactByConnReqHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ConnReqUriHash -> IO (Maybe Contact)
|
||||
getContactByConnReqHash :: DB.Connection -> VersionRangeChat -> User -> ConnReqUriHash -> IO (Maybe Contact)
|
||||
getContactByConnReqHash db vr user@User {userId} cReqHash =
|
||||
maybeFirstRow (toContact vr user) $
|
||||
DB.query
|
||||
@@ -279,12 +279,12 @@ setContactDeleted db user@User {userId} ct@Contact {contactId} = do
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute db "UPDATE contacts SET deleted = 1, updated_at = ? WHERE user_id = ? AND contact_id = ?" (currentTs, userId, contactId)
|
||||
|
||||
getDeletedContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [Contact]
|
||||
getDeletedContacts :: DB.Connection -> VersionRangeChat -> User -> IO [Contact]
|
||||
getDeletedContacts db vr user@User {userId} = do
|
||||
contactIds <- map fromOnly <$> DB.query db "SELECT contact_id FROM contacts WHERE user_id = ? AND deleted = 1" (Only userId)
|
||||
rights <$> mapM (runExceptT . getDeletedContact db vr user) contactIds
|
||||
|
||||
getDeletedContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO Contact
|
||||
getDeletedContact :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO Contact
|
||||
getDeletedContact db vr user contactId = getContact_ db vr user contactId True
|
||||
|
||||
deleteContactProfile_ :: DB.Connection -> UserId -> ContactId -> IO ()
|
||||
@@ -521,18 +521,18 @@ updateContactLDN_ db user@User {userId} contactId displayName newName updatedAt
|
||||
(newName, updatedAt, userId, contactId)
|
||||
safeDeleteLDN db user displayName
|
||||
|
||||
getContactByName :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactName -> ExceptT StoreError IO Contact
|
||||
getContactByName :: DB.Connection -> VersionRangeChat -> User -> ContactName -> ExceptT StoreError IO Contact
|
||||
getContactByName db vr user localDisplayName = do
|
||||
cId <- getContactIdByName db user localDisplayName
|
||||
getContact db vr user cId
|
||||
|
||||
getUserContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [Contact]
|
||||
getUserContacts :: DB.Connection -> VersionRangeChat -> User -> IO [Contact]
|
||||
getUserContacts db vr user@User {userId} = do
|
||||
contactIds <- map fromOnly <$> DB.query db "SELECT contact_id FROM contacts WHERE user_id = ? AND deleted = 0" (Only userId)
|
||||
contacts <- rights <$> mapM (runExceptT . getContact db vr user) contactIds
|
||||
pure $ filter (\Contact {activeConn} -> isJust activeConn) contacts
|
||||
|
||||
createOrUpdateContactRequest :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> PQSupport -> ExceptT StoreError IO ContactOrRequest
|
||||
createOrUpdateContactRequest :: DB.Connection -> VersionRangeChat -> User -> Int64 -> InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> PQSupport -> ExceptT StoreError IO ContactOrRequest
|
||||
createOrUpdateContactRequest db vr user@User {userId} userContactLinkId invId (VersionRange minV maxV) Profile {displayName, fullName, image, contactLink, preferences} xContactId_ pqSup =
|
||||
liftIO (maybeM getContact' xContactId_) >>= \case
|
||||
Just contact -> pure $ CORContact contact
|
||||
@@ -732,10 +732,10 @@ getContactIdByName db User {userId} cName =
|
||||
ExceptT . firstRow fromOnly (SEContactNotFoundByName cName) $
|
||||
DB.query db "SELECT contact_id FROM contacts WHERE user_id = ? AND local_display_name = ? AND deleted = 0" (userId, cName)
|
||||
|
||||
getContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO Contact
|
||||
getContact :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO Contact
|
||||
getContact db vr user contactId = getContact_ db vr user contactId False
|
||||
|
||||
getContact_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> Bool -> ExceptT StoreError IO Contact
|
||||
getContact_ :: DB.Connection -> VersionRangeChat -> User -> Int64 -> Bool -> ExceptT StoreError IO Contact
|
||||
getContact_ db vr user@User {userId} contactId deleted =
|
||||
ExceptT . firstRow (toContact vr user) (SEContactNotFound contactId) $
|
||||
DB.query
|
||||
@@ -791,7 +791,7 @@ getPendingContactConnections db User {userId} = do
|
||||
|]
|
||||
[":user_id" := userId, ":conn_type" := ConnContact]
|
||||
|
||||
getContactConnections :: DB.Connection -> (PQSupport -> VersionRangeChat) -> UserId -> Contact -> IO [Connection]
|
||||
getContactConnections :: DB.Connection -> VersionRangeChat -> UserId -> Contact -> IO [Connection]
|
||||
getContactConnections db vr userId Contact {contactId} =
|
||||
connections =<< liftIO getConnections_
|
||||
where
|
||||
@@ -811,7 +811,7 @@ getContactConnections db vr userId Contact {contactId} =
|
||||
connections [] = pure []
|
||||
connections rows = pure $ map (toConnection vr) rows
|
||||
|
||||
getConnectionById :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO Connection
|
||||
getConnectionById :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO Connection
|
||||
getConnectionById db vr User {userId} connId = ExceptT $ do
|
||||
firstRow (toConnection vr) (SEConnectionNotFoundById connId) $
|
||||
DB.query
|
||||
|
||||
@@ -174,7 +174,7 @@ getPendingSndChunks db fileId connId =
|
||||
|]
|
||||
(fileId, connId)
|
||||
|
||||
createSndDirectFTConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> (CommandId, ConnId) -> SubscriptionMode -> IO ()
|
||||
createSndDirectFTConnection :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> SubscriptionMode -> IO ()
|
||||
createSndDirectFTConnection db vr user@User {userId} fileId (cmdId, acId) subMode = do
|
||||
currentTs <- getCurrentTime
|
||||
Connection {connId} <- createSndFileConnection_ db vr userId fileId acId subMode
|
||||
@@ -194,7 +194,7 @@ createSndGroupFileTransfer db userId GroupInfo {groupId} filePath FileInvitation
|
||||
fileId <- insertedRowId db
|
||||
pure FileTransferMeta {fileId, xftpSndFile = Nothing, xftpRedirectFor = Nothing, fileName, filePath, fileSize, fileInline, chunkSize, cancelled = False}
|
||||
|
||||
createSndGroupFileTransferConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> (CommandId, ConnId) -> GroupMember -> SubscriptionMode -> IO ()
|
||||
createSndGroupFileTransferConnection :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> GroupMember -> SubscriptionMode -> IO ()
|
||||
createSndGroupFileTransferConnection db vr user@User {userId} fileId (cmdId, acId) GroupMember {groupMemberId} subMode = do
|
||||
currentTs <- getCurrentTime
|
||||
Connection {connId} <- createSndFileConnection_ db vr userId fileId acId subMode
|
||||
@@ -430,10 +430,10 @@ lookupChatRefByFileId db User {userId} fileId =
|
||||
(userId, fileId)
|
||||
|
||||
-- TODO v6.0 remove
|
||||
createSndFileConnection_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> UserId -> Int64 -> ConnId -> SubscriptionMode -> IO Connection
|
||||
createSndFileConnection_ :: DB.Connection -> VersionRangeChat -> UserId -> Int64 -> ConnId -> SubscriptionMode -> IO Connection
|
||||
createSndFileConnection_ db vr userId fileId agentConnId subMode = do
|
||||
currentTs <- getCurrentTime
|
||||
createConnection_ db userId ConnSndFile (Just fileId) agentConnId (minVersion $ vr PQSupportOff) chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff
|
||||
createConnection_ db userId ConnSndFile (Just fileId) agentConnId (minVersion vr) chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff
|
||||
|
||||
updateSndFileStatus :: DB.Connection -> SndFileTransfer -> FileStatus -> IO ()
|
||||
updateSndFileStatus db SndFileTransfer {fileId, connId} status = do
|
||||
@@ -695,7 +695,7 @@ getRcvFileTransfer_ db userId fileId = do
|
||||
_ -> pure Nothing
|
||||
cancelled = fromMaybe False cancelled_
|
||||
|
||||
acceptRcvFileTransfer :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> (CommandId, ConnId) -> ConnStatus -> FilePath -> SubscriptionMode -> ExceptT StoreError IO AChatItem
|
||||
acceptRcvFileTransfer :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> ConnStatus -> FilePath -> SubscriptionMode -> ExceptT StoreError IO AChatItem
|
||||
acceptRcvFileTransfer db vr user@User {userId} fileId (cmdId, acId) connStatus filePath subMode = ExceptT $ do
|
||||
currentTs <- getCurrentTime
|
||||
acceptRcvFT_ db user fileId filePath Nothing currentTs
|
||||
@@ -707,7 +707,7 @@ acceptRcvFileTransfer db vr user@User {userId} fileId (cmdId, acId) connStatus f
|
||||
setCommandConnId db user cmdId connId
|
||||
runExceptT $ getChatItemByFileId db vr user fileId
|
||||
|
||||
getContactByFileId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> FileTransferId -> ExceptT StoreError IO Contact
|
||||
getContactByFileId :: DB.Connection -> VersionRangeChat -> User -> FileTransferId -> ExceptT StoreError IO Contact
|
||||
getContactByFileId db vr user@User {userId} fileId = do
|
||||
cId <- getContactIdByFileId
|
||||
getContact db vr user cId
|
||||
@@ -716,7 +716,7 @@ getContactByFileId db vr user@User {userId} fileId = do
|
||||
ExceptT . firstRow fromOnly (SEContactNotFoundByFileId fileId) $
|
||||
DB.query db "SELECT contact_id FROM files WHERE user_id = ? AND file_id = ?" (userId, fileId)
|
||||
|
||||
acceptRcvInlineFT :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem
|
||||
acceptRcvInlineFT :: DB.Connection -> VersionRangeChat -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem
|
||||
acceptRcvInlineFT db vr user fileId filePath = do
|
||||
liftIO $ acceptRcvFT_ db user fileId filePath (Just IFMOffer) =<< getCurrentTime
|
||||
getChatItemByFileId db vr user fileId
|
||||
@@ -725,7 +725,7 @@ startRcvInlineFT :: DB.Connection -> User -> RcvFileTransfer -> FilePath -> Mayb
|
||||
startRcvInlineFT db user RcvFileTransfer {fileId} filePath rcvFileInline =
|
||||
acceptRcvFT_ db user fileId filePath rcvFileInline =<< getCurrentTime
|
||||
|
||||
xftpAcceptRcvFT :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem
|
||||
xftpAcceptRcvFT :: DB.Connection -> VersionRangeChat -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem
|
||||
xftpAcceptRcvFT db vr user fileId filePath = do
|
||||
liftIO $ acceptRcvFT_ db user fileId filePath Nothing =<< getCurrentTime
|
||||
getChatItemByFileId db vr user fileId
|
||||
@@ -1000,7 +1000,7 @@ getLocalCryptoFile db userId fileId sent =
|
||||
pure $ CryptoFile filePath fileCryptoArgs
|
||||
_ -> throwError $ SEFileNotFound fileId
|
||||
|
||||
updateDirectCIFileStatus :: forall d. MsgDirectionI d => DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> CIFileStatus d -> ExceptT StoreError IO AChatItem
|
||||
updateDirectCIFileStatus :: forall d. MsgDirectionI d => DB.Connection -> VersionRangeChat -> User -> Int64 -> CIFileStatus d -> ExceptT StoreError IO AChatItem
|
||||
updateDirectCIFileStatus db vr user fileId fileStatus = do
|
||||
aci@(AChatItem cType d cInfo ci) <- getChatItemByFileId db vr user fileId
|
||||
case (cType, testEquality d $ msgDirection @d) of
|
||||
|
||||
@@ -145,7 +145,7 @@ import Simplex.Messaging.Agent.Protocol (ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQSupport, pattern PQEncOff, pattern PQSupportOff)
|
||||
import Simplex.Messaging.Crypto.Ratchet (pattern PQEncOff, pattern PQSupportOff)
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>))
|
||||
import Simplex.Messaging.Version
|
||||
@@ -157,9 +157,9 @@ type GroupMemberRow = ((Int64, Int64, MemberId, VersionChat, VersionChat, GroupM
|
||||
|
||||
type MaybeGroupMemberRow = ((Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe Bool, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId, Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe ImageData, Maybe ConnReqContact, Maybe LocalAlias, Maybe Preferences))
|
||||
|
||||
toGroupInfo :: (PQSupport -> VersionRangeChat) -> Int64 -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo :: VersionRangeChat -> Int64 -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo vr userContactId ((groupId, localDisplayName, displayName, fullName, description, image, hostConnCustomUserProfileId, enableNtfs_, sendRcpts, favorite, groupPreferences) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt, customData) :. userMemberRow) =
|
||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr PQSupportOff}
|
||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts, favorite}
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
groupProfile = GroupProfile {displayName, fullName, description, image, groupPreferences}
|
||||
@@ -191,7 +191,7 @@ createGroupLink db User {userId} groupInfo@GroupInfo {groupId, localDisplayName}
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode PQSupportOff
|
||||
|
||||
getGroupLinkConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> ExceptT StoreError IO Connection
|
||||
getGroupLinkConnection :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> ExceptT StoreError IO Connection
|
||||
getGroupLinkConnection db vr User {userId} groupInfo@GroupInfo {groupId} =
|
||||
ExceptT . firstRow (toConnection vr) (SEGroupLinkNotFound groupInfo) $
|
||||
DB.query
|
||||
@@ -264,7 +264,7 @@ setGroupLinkMemberRole :: DB.Connection -> User -> Int64 -> GroupMemberRole -> I
|
||||
setGroupLinkMemberRole db User {userId} userContactLinkId memberRole =
|
||||
DB.execute db "UPDATE user_contact_links SET group_link_member_role = ? WHERE user_id = ? AND user_contact_link_id = ?" (memberRole, userId, userContactLinkId)
|
||||
|
||||
getGroupAndMember :: DB.Connection -> User -> Int64 -> (PQSupport -> VersionRangeChat) -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember :: DB.Connection -> User -> Int64 -> VersionRangeChat -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember db User {userId, userContactId} groupMemberId vr =
|
||||
ExceptT . firstRow toGroupAndMember (SEInternalError "referenced group member not found") $
|
||||
DB.query
|
||||
@@ -309,7 +309,7 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr =
|
||||
in (groupInfo, (member :: GroupMember) {activeConn = toMaybeConnection vr connRow})
|
||||
|
||||
-- | creates completely new group with a single member - the current user
|
||||
createNewGroup :: DB.Connection -> (PQSupport -> VersionRangeChat) -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
|
||||
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
|
||||
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do
|
||||
let GroupProfile {displayName, fullName, description, image, groupPreferences} = groupProfile
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
@@ -352,7 +352,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
}
|
||||
|
||||
-- | creates a new group record for the group the current user was invited to, or returns an existing one
|
||||
createGroupInvitation :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> GroupInvitation -> Maybe ProfileId -> ExceptT StoreError IO (GroupInfo, GroupMemberId)
|
||||
createGroupInvitation :: DB.Connection -> VersionRangeChat -> User -> Contact -> GroupInvitation -> Maybe ProfileId -> ExceptT StoreError IO (GroupInfo, GroupMemberId)
|
||||
createGroupInvitation _ _ _ Contact {localDisplayName, activeConn = Nothing} _ _ = throwError $ SEContactNotReady localDisplayName
|
||||
createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activeConn = Just Connection {customUserProfileId, peerChatVRange}} GroupInvitation {fromMember, invitedMember, connRequest, groupProfile} incognitoProfileId = do
|
||||
liftIO getInvitationGroupId_ >>= \case
|
||||
@@ -397,7 +397,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
|]
|
||||
(profileId, localDisplayName, connRequest, customUserProfileId, userId, True, currentTs, currentTs, currentTs, currentTs)
|
||||
insertedRowId db
|
||||
let hostVRange = const $ adjustedMemberVRange vr peerChatVRange
|
||||
let hostVRange = adjustedMemberVRange vr peerChatVRange
|
||||
GroupMember {groupMemberId} <- createContactMemberInv_ db user groupId Nothing contact fromMember GCHostMember GSMemInvited IBUnknown Nothing currentTs hostVRange
|
||||
membership <- createContactMemberInv_ db user groupId (Just groupMemberId) user invitedMember GCUserMember GSMemInvited (IBContact contactId) incognitoProfileId currentTs vr
|
||||
let chatSettings = ChatSettings {enableNtfs = MFAll, sendRcpts = Nothing, favorite = False}
|
||||
@@ -419,9 +419,9 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
groupMemberId
|
||||
)
|
||||
|
||||
adjustedMemberVRange :: (PQSupport -> VersionRangeChat) -> VersionRangeChat -> VersionRangeChat
|
||||
adjustedMemberVRange getVR vr@(VersionRange minV maxV) =
|
||||
let maxV' = min maxV (maxVersion $ getVR PQSupportOff)
|
||||
adjustedMemberVRange :: VersionRangeChat -> VersionRangeChat -> VersionRangeChat
|
||||
adjustedMemberVRange chatVR vr@(VersionRange minV maxV) =
|
||||
let maxV' = min maxV (maxVersion chatVR)
|
||||
in fromMaybe vr $ safeVersionRange minV (max minV maxV')
|
||||
|
||||
getHostMemberId_ :: DB.Connection -> User -> GroupId -> ExceptT StoreError IO GroupMemberId
|
||||
@@ -429,7 +429,7 @@ getHostMemberId_ db User {userId} groupId =
|
||||
ExceptT . firstRow fromOnly (SEHostMemberIdNotFound groupId) $
|
||||
DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND group_id = ? AND member_category = ?" (userId, groupId, GCHostMember)
|
||||
|
||||
createContactMemberInv_ :: IsContact a => DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> Maybe ProfileId -> UTCTime -> (PQSupport -> VersionRangeChat) -> ExceptT StoreError IO GroupMember
|
||||
createContactMemberInv_ :: IsContact a => DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> Maybe ProfileId -> UTCTime -> VersionRangeChat -> ExceptT StoreError IO GroupMember
|
||||
createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMemberId userOrContact MemberIdRole {memberId, memberRole} memberCategory memberStatus invitedBy incognitoProfileId createdAt vr = do
|
||||
incognitoProfile <- forM incognitoProfileId $ \profileId -> getProfileById db userId profileId
|
||||
(localDisplayName, memberProfile) <- case (incognitoProfile, incognitoProfileId) of
|
||||
@@ -457,7 +457,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe
|
||||
memberChatVRange
|
||||
}
|
||||
where
|
||||
memberChatVRange@(VersionRange minV maxV) = vr PQSupportOff
|
||||
memberChatVRange@(VersionRange minV maxV) = vr
|
||||
insertMember_ :: IO ContactName
|
||||
insertMember_ = do
|
||||
let localDisplayName = localDisplayName' userOrContact
|
||||
@@ -493,7 +493,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe
|
||||
)
|
||||
pure $ Right incognitoLdn
|
||||
|
||||
createGroupInvitedViaLink :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Connection -> GroupLinkInvitation -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
createGroupInvitedViaLink :: DB.Connection -> VersionRangeChat -> User -> Connection -> GroupLinkInvitation -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
createGroupInvitedViaLink
|
||||
db
|
||||
vr
|
||||
@@ -564,7 +564,7 @@ setGroupInvitationChatItemId db User {userId} groupId chatItemId = do
|
||||
|
||||
-- TODO return the last connection that is ready, not any last connection
|
||||
-- requires updating connection status
|
||||
getGroup :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> ExceptT StoreError IO Group
|
||||
getGroup :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO Group
|
||||
getGroup db vr user groupId = do
|
||||
gInfo <- getGroupInfo db vr user groupId
|
||||
members <- liftIO $ getGroupMembers db vr user gInfo
|
||||
@@ -619,12 +619,12 @@ deleteGroupProfile_ db userId groupId =
|
||||
|]
|
||||
(userId, groupId)
|
||||
|
||||
getUserGroups :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [Group]
|
||||
getUserGroups :: DB.Connection -> VersionRangeChat -> User -> IO [Group]
|
||||
getUserGroups db vr user@User {userId} = do
|
||||
groupIds <- map fromOnly <$> DB.query db "SELECT group_id FROM groups WHERE user_id = ?" (Only userId)
|
||||
rights <$> mapM (runExceptT . getGroup db vr user) groupIds
|
||||
|
||||
getUserGroupDetails :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfo]
|
||||
getUserGroupDetails :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfo]
|
||||
getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ =
|
||||
map (toGroupInfo vr userContactId)
|
||||
<$> DB.query
|
||||
@@ -647,7 +647,7 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ =
|
||||
where
|
||||
search = fromMaybe "" search_
|
||||
|
||||
getUserGroupsWithSummary :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Maybe ContactId -> Maybe String -> IO [(GroupInfo, GroupSummary)]
|
||||
getUserGroupsWithSummary :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [(GroupInfo, GroupSummary)]
|
||||
getUserGroupsWithSummary db vr user _contactId_ search_ =
|
||||
getUserGroupDetails db vr user _contactId_ search_
|
||||
>>= mapM (\g@GroupInfo {groupId} -> (g,) <$> getGroupSummary db user groupId)
|
||||
@@ -688,7 +688,7 @@ checkContactHasGroups :: DB.Connection -> User -> Contact -> IO (Maybe GroupId)
|
||||
checkContactHasGroups db User {userId} Contact {contactId} =
|
||||
maybeFirstRow fromOnly $ DB.query db "SELECT group_id FROM group_members WHERE user_id = ? AND contact_id = ? LIMIT 1" (userId, contactId)
|
||||
|
||||
getGroupInfoByName :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupName -> ExceptT StoreError IO GroupInfo
|
||||
getGroupInfoByName :: DB.Connection -> VersionRangeChat -> User -> GroupName -> ExceptT StoreError IO GroupInfo
|
||||
getGroupInfoByName db vr user gName = do
|
||||
gId <- getGroupIdByName db user gName
|
||||
getGroupInfo db vr user gId
|
||||
@@ -712,7 +712,7 @@ groupMemberQuery =
|
||||
)
|
||||
|]
|
||||
|
||||
getGroupMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> GroupMemberId -> ExceptT StoreError IO GroupMember
|
||||
getGroupMember :: DB.Connection -> VersionRangeChat -> User -> GroupId -> GroupMemberId -> ExceptT StoreError IO GroupMember
|
||||
getGroupMember db vr user@User {userId} groupId groupMemberId =
|
||||
ExceptT . firstRow (toContactMember vr user) (SEGroupMemberNotFound groupMemberId) $
|
||||
DB.query
|
||||
@@ -720,7 +720,7 @@ getGroupMember db vr user@User {userId} groupId groupMemberId =
|
||||
(groupMemberQuery <> " WHERE m.group_id = ? AND m.group_member_id = ? AND m.user_id = ?")
|
||||
(userId, groupId, groupMemberId, userId)
|
||||
|
||||
getGroupMemberById :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMemberId -> ExceptT StoreError IO GroupMember
|
||||
getGroupMemberById :: DB.Connection -> VersionRangeChat -> User -> GroupMemberId -> ExceptT StoreError IO GroupMember
|
||||
getGroupMemberById db vr user@User {userId} groupMemberId =
|
||||
ExceptT . firstRow (toContactMember vr user) (SEGroupMemberNotFound groupMemberId) $
|
||||
DB.query
|
||||
@@ -728,7 +728,7 @@ getGroupMemberById db vr user@User {userId} groupMemberId =
|
||||
(groupMemberQuery <> " WHERE m.group_member_id = ? AND m.user_id = ?")
|
||||
(userId, groupMemberId, userId)
|
||||
|
||||
getGroupMemberByMemberId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> MemberId -> ExceptT StoreError IO GroupMember
|
||||
getGroupMemberByMemberId :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> ExceptT StoreError IO GroupMember
|
||||
getGroupMemberByMemberId db vr user@User {userId} GroupInfo {groupId} memberId =
|
||||
ExceptT . firstRow (toContactMember vr user) (SEGroupMemberNotFoundByMemberId memberId) $
|
||||
DB.query
|
||||
@@ -736,7 +736,7 @@ getGroupMemberByMemberId db vr user@User {userId} GroupInfo {groupId} memberId =
|
||||
(groupMemberQuery <> " WHERE m.group_id = ? AND m.member_id = ?")
|
||||
(userId, groupId, memberId)
|
||||
|
||||
getGroupMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> IO [GroupMember]
|
||||
getGroupMembers :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember]
|
||||
getGroupMembers db vr user@User {userId, userContactId} GroupInfo {groupId} = do
|
||||
map (toContactMember vr user)
|
||||
<$> DB.query
|
||||
@@ -744,7 +744,7 @@ getGroupMembers db vr user@User {userId, userContactId} GroupInfo {groupId} = do
|
||||
(groupMemberQuery <> " WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)")
|
||||
(userId, groupId, userId, userContactId)
|
||||
|
||||
getGroupMembersForExpiration :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> IO [GroupMember]
|
||||
getGroupMembersForExpiration :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember]
|
||||
getGroupMembersForExpiration db vr user@User {userId, userContactId} GroupInfo {groupId} = do
|
||||
map (toContactMember vr user)
|
||||
<$> DB.query
|
||||
@@ -760,7 +760,7 @@ getGroupMembersForExpiration db vr user@User {userId, userContactId} GroupInfo {
|
||||
)
|
||||
(userId, groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted, GSMemUnknown)
|
||||
|
||||
toContactMember :: (PQSupport -> VersionRangeChat) -> User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember
|
||||
toContactMember :: VersionRangeChat -> User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember
|
||||
toContactMember vr User {userContactId} (memberRow :. connRow) =
|
||||
(toGroupMember userContactId memberRow) {activeConn = toMaybeConnection vr connRow}
|
||||
|
||||
@@ -778,7 +778,7 @@ getGroupCurrentMembersCount db User {userId} GroupInfo {groupId} = do
|
||||
(groupId, userId)
|
||||
pure $ length $ filter memberCurrent' statuses
|
||||
|
||||
getGroupInvitation :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> ExceptT StoreError IO ReceivedGroupInvitation
|
||||
getGroupInvitation :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO ReceivedGroupInvitation
|
||||
getGroupInvitation db vr user groupId =
|
||||
getConnRec_ user >>= \case
|
||||
Just connRequest -> do
|
||||
@@ -913,7 +913,7 @@ createAcceptedMemberConnection
|
||||
Connection {connId} <- createConnection_ db userId ConnMember (Just groupMemberId) agentConnId chatV cReqChatVRange Nothing (Just userContactLinkId) Nothing 0 createdAt subMode PQSupportOff
|
||||
setCommandConnId db user cmdId connId
|
||||
|
||||
getContactViaMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> ExceptT StoreError IO Contact
|
||||
getContactViaMember :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> ExceptT StoreError IO Contact
|
||||
getContactViaMember db vr user@User {userId} GroupMember {groupMemberId} = do
|
||||
contactId <-
|
||||
ExceptT $
|
||||
@@ -1174,7 +1174,7 @@ getIntroduction db reMember toMember = ExceptT $ do
|
||||
in Right GroupMemberIntro {introId, reMember, toMember, introStatus, introInvitation}
|
||||
toIntro _ = Left SEIntroNotFound
|
||||
|
||||
getForwardIntroducedMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> Bool -> IO [GroupMember]
|
||||
getForwardIntroducedMembers :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> Bool -> IO [GroupMember]
|
||||
getForwardIntroducedMembers db vr user invitee highlyAvailable = do
|
||||
memberIds <- map fromOnly <$> query
|
||||
filter memberCurrent . rights <$> mapM (runExceptT . getGroupMemberById db vr user) memberIds
|
||||
@@ -1194,7 +1194,7 @@ getForwardIntroducedMembers db vr user invitee highlyAvailable = do
|
||||
WHERE to_group_member_id = ? AND intro_status NOT IN (?,?,?)
|
||||
|]
|
||||
|
||||
getForwardInvitedMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> Bool -> IO [GroupMember]
|
||||
getForwardInvitedMembers :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> Bool -> IO [GroupMember]
|
||||
getForwardInvitedMembers db vr user forwardMember highlyAvailable = do
|
||||
memberIds <- map fromOnly <$> query
|
||||
filter memberCurrent . rights <$> mapM (runExceptT . getGroupMemberById db vr user) memberIds
|
||||
@@ -1288,7 +1288,7 @@ createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> Version
|
||||
createMemberConnection_ db userId groupMemberId agentConnId chatV peerChatVRange viaContact connLevel currentTs subMode =
|
||||
createConnection_ db userId ConnMember (Just groupMemberId) agentConnId chatV peerChatVRange viaContact Nothing Nothing connLevel currentTs subMode PQSupportOff
|
||||
|
||||
getViaGroupMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> IO (Maybe (GroupInfo, GroupMember))
|
||||
getViaGroupMember :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO (Maybe (GroupInfo, GroupMember))
|
||||
getViaGroupMember db vr User {userId, userContactId} Contact {contactId} =
|
||||
maybeFirstRow toGroupAndMember $
|
||||
DB.query
|
||||
@@ -1333,7 +1333,7 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} =
|
||||
member = toGroupMember userContactId memberRow
|
||||
in (groupInfo, (member :: GroupMember) {activeConn = toMaybeConnection vr connRow})
|
||||
|
||||
getViaGroupContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> IO (Maybe Contact)
|
||||
getViaGroupContact :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> IO (Maybe Contact)
|
||||
getViaGroupContact db vr user@User {userId} GroupMember {groupMemberId} = do
|
||||
contactId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
@@ -1384,7 +1384,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
|
||||
(ldn, currentTs, userId, groupId)
|
||||
safeDeleteLDN db user localDisplayName
|
||||
|
||||
getGroupInfo :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO GroupInfo
|
||||
getGroupInfo :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO GroupInfo
|
||||
getGroupInfo db vr User {userId, userContactId} groupId =
|
||||
ExceptT . firstRow (toGroupInfo vr userContactId) (SEGroupNotFound groupId) $
|
||||
DB.query
|
||||
@@ -1407,7 +1407,7 @@ getGroupInfo db vr User {userId, userContactId} groupId =
|
||||
|]
|
||||
(groupId, userId, userContactId)
|
||||
|
||||
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
groupId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
@@ -1421,7 +1421,7 @@ getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReq
|
||||
(userId, cReqSchema1, cReqSchema2)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db vr user) groupId_
|
||||
|
||||
getGroupInfoByGroupLinkHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByGroupLinkHash :: DB.Connection -> VersionRangeChat -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByGroupLinkHash db vr user@User {userId, userContactId} (groupLinkHash1, groupLinkHash2) = do
|
||||
groupId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
@@ -1448,7 +1448,7 @@ getGroupMemberIdByName db User {userId} groupId groupMemberName =
|
||||
ExceptT . firstRow fromOnly (SEGroupMemberNameNotFound groupId groupMemberName) $
|
||||
DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND group_id = ? AND local_display_name = ?" (userId, groupId, groupMemberName)
|
||||
|
||||
getActiveMembersByName :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactName -> ExceptT StoreError IO [(GroupInfo, GroupMember)]
|
||||
getActiveMembersByName :: DB.Connection -> VersionRangeChat -> User -> ContactName -> ExceptT StoreError IO [(GroupInfo, GroupMember)]
|
||||
getActiveMembersByName db vr user@User {userId} groupMemberName = do
|
||||
groupMemberIds :: [(GroupId, GroupMemberId)] <-
|
||||
liftIO $
|
||||
@@ -1469,7 +1469,7 @@ getActiveMembersByName db vr user@User {userId} groupMemberName = do
|
||||
where
|
||||
ts GroupInfo {chatTs, updatedAt} = fromMaybe updatedAt chatTs
|
||||
|
||||
getMatchingContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> IO [Contact]
|
||||
getMatchingContacts :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO [Contact]
|
||||
getMatchingContacts db vr user@User {userId} Contact {contactId, profile = LocalProfile {displayName, fullName, image}} = do
|
||||
contactIds <-
|
||||
map fromOnly <$> case image of
|
||||
@@ -1489,7 +1489,7 @@ getMatchingContacts db vr user@User {userId} Contact {contactId, profile = Local
|
||||
AND p.display_name = ? AND p.full_name = ?
|
||||
|]
|
||||
|
||||
getMatchingMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> IO [GroupMember]
|
||||
getMatchingMembers :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO [GroupMember]
|
||||
getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {displayName, fullName, image}} = do
|
||||
memberIds <-
|
||||
map fromOnly <$> case image of
|
||||
@@ -1508,7 +1508,7 @@ getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {dis
|
||||
AND p.display_name = ? AND p.full_name = ?
|
||||
|]
|
||||
|
||||
getMatchingMemberContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> IO [Contact]
|
||||
getMatchingMemberContacts :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> IO [Contact]
|
||||
getMatchingMemberContacts _ _ _ GroupMember {memberContactId = Just _} = pure []
|
||||
getMatchingMemberContacts db vr user@User {userId} GroupMember {memberProfile = LocalProfile {displayName, fullName, image}} = do
|
||||
contactIds <-
|
||||
@@ -1547,7 +1547,7 @@ createSentProbeHash db userId probeId to = do
|
||||
"INSERT INTO sent_probe_hashes (sent_probe_id, contact_id, group_member_id, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)"
|
||||
(probeId, ctId, gmId, userId, currentTs, currentTs)
|
||||
|
||||
matchReceivedProbe :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactOrMember -> Probe -> IO [ContactOrMember]
|
||||
matchReceivedProbe :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> Probe -> IO [ContactOrMember]
|
||||
matchReceivedProbe db vr user@User {userId} from (Probe probe) = do
|
||||
let probeHash = C.sha256Hash probe
|
||||
cgmIds <-
|
||||
@@ -1579,7 +1579,7 @@ matchReceivedProbe db vr user@User {userId} from (Probe probe) = do
|
||||
(x : _) -> [x]
|
||||
ctIds' <> memIds
|
||||
|
||||
matchReceivedProbeHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactOrMember -> ProbeHash -> IO (Maybe (ContactOrMember, Probe))
|
||||
matchReceivedProbeHash :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> ProbeHash -> IO (Maybe (ContactOrMember, Probe))
|
||||
matchReceivedProbeHash db vr user@User {userId} from (ProbeHash probeHash) = do
|
||||
probeIds <-
|
||||
maybeFirstRow id $
|
||||
@@ -1602,7 +1602,7 @@ matchReceivedProbeHash db vr user@User {userId} from (ProbeHash probeHash) = do
|
||||
(ctId, gmId, probeHash, userId, currentTs, currentTs)
|
||||
pure probeIds $>>= \(Only probe :. cgmIds) -> (,Probe probe) <$$> getContactOrMember_ db vr user cgmIds
|
||||
|
||||
matchSentProbe :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactOrMember -> Probe -> IO (Maybe ContactOrMember)
|
||||
matchSentProbe :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> Probe -> IO (Maybe ContactOrMember)
|
||||
matchSentProbe db vr user@User {userId} _from (Probe probe) = do
|
||||
cgmIds $>>= getContactOrMember_ db vr user
|
||||
where
|
||||
@@ -1623,7 +1623,7 @@ matchSentProbe db vr user@User {userId} _from (Probe probe) = do
|
||||
|]
|
||||
(userId, probe, ctId, gmId)
|
||||
|
||||
getContactOrMember_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId) -> IO (Maybe ContactOrMember)
|
||||
getContactOrMember_ :: DB.Connection -> VersionRangeChat -> User -> (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId) -> IO (Maybe ContactOrMember)
|
||||
getContactOrMember_ db vr user ids =
|
||||
fmap eitherToMaybe . runExceptT $ case ids of
|
||||
(Just ctId, _, _) -> COMContact <$> getContact db vr user ctId
|
||||
@@ -1631,7 +1631,7 @@ getContactOrMember_ db vr user ids =
|
||||
_ -> throwError $ SEInternalError ""
|
||||
|
||||
-- if requested merge direction is overruled (toFromContacts), keepLDN is kept
|
||||
mergeContactRecords :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> Contact -> ExceptT StoreError IO Contact
|
||||
mergeContactRecords :: DB.Connection -> VersionRangeChat -> User -> Contact -> Contact -> ExceptT StoreError IO Contact
|
||||
mergeContactRecords db vr user@User {userId} to@Contact {localDisplayName = keepLDN} from = do
|
||||
let (toCt, fromCt) = toFromContacts to from
|
||||
Contact {contactId = toContactId, localDisplayName = toLDN} = toCt
|
||||
@@ -1721,7 +1721,7 @@ associateMemberWithContactRecord
|
||||
when (memProfileId /= profileId) $ deleteUnusedProfile_ db userId memProfileId
|
||||
when (memLDN /= localDisplayName) $ deleteUnusedDisplayName_ db userId memLDN
|
||||
|
||||
associateContactWithMemberRecord :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> Contact -> ExceptT StoreError IO Contact
|
||||
associateContactWithMemberRecord :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> Contact -> ExceptT StoreError IO Contact
|
||||
associateContactWithMemberRecord
|
||||
db
|
||||
vr
|
||||
@@ -1958,7 +1958,7 @@ createMemberContact
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, customData = Nothing}
|
||||
|
||||
getMemberContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation)
|
||||
getMemberContact :: DB.Connection -> VersionRangeChat -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation)
|
||||
getMemberContact db vr user contactId = do
|
||||
ct <- getContact db vr user contactId
|
||||
let Contact {contactGroupMemberId, activeConn} = ct
|
||||
@@ -2138,7 +2138,7 @@ setXGrpLinkMemReceived db mId xGrpLinkMemReceived = do
|
||||
"UPDATE group_members SET xgrplinkmem_received = ?, updated_at = ? WHERE group_member_id = ?"
|
||||
(xGrpLinkMemReceived, currentTs, mId)
|
||||
|
||||
createNewUnknownGroupMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> MemberId -> Text -> ExceptT StoreError IO GroupMember
|
||||
createNewUnknownGroupMember :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> Text -> ExceptT StoreError IO GroupMember
|
||||
createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {groupId} memberId memberName = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
let memberProfile = profileFromName memberName
|
||||
@@ -2160,9 +2160,9 @@ createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {g
|
||||
insertedRowId db
|
||||
getGroupMemberById db vr user groupMemberId
|
||||
where
|
||||
VersionRange minV maxV = vr PQSupportOff
|
||||
VersionRange minV maxV = vr
|
||||
|
||||
updateUnknownMemberAnnounced :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> GroupMember -> MemberInfo -> ExceptT StoreError IO GroupMember
|
||||
updateUnknownMemberAnnounced :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> GroupMember -> MemberInfo -> ExceptT StoreError IO GroupMember
|
||||
updateUnknownMemberAnnounced db vr user@User {userId} invitingMember unknownMember@GroupMember {groupMemberId, memberChatVRange} MemberInfo {memberRole, v, profile} = do
|
||||
_ <- updateMemberProfile db user unknownMember profile
|
||||
currentTs <- liftIO getCurrentTime
|
||||
|
||||
@@ -484,7 +484,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
||||
ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing
|
||||
ciQuoteGroup ((Only itemId :. memberRow) : _) = ciQuote itemId . CIQGroupRcv . Just $ toGroupMember userContactId memberRow
|
||||
|
||||
getChatPreviews :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Bool -> PaginationByTime -> ChatListQuery -> IO [Either StoreError AChat]
|
||||
getChatPreviews :: DB.Connection -> VersionRangeChat -> User -> Bool -> PaginationByTime -> ChatListQuery -> IO [Either StoreError AChat]
|
||||
getChatPreviews db vr user withPCC pagination query = do
|
||||
directChats <- findDirectChatPreviews_ db user pagination query
|
||||
groupChats <- findGroupChatPreviews_ db user pagination query
|
||||
@@ -621,7 +621,7 @@ findDirectChatPreviews_ db User {userId} pagination clq =
|
||||
)
|
||||
([":user_id" := userId, ":rcv_new" := CISRcvNew, ":search" := search] <> pagParams)
|
||||
|
||||
getDirectChatPreview_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatPreviewData 'CTDirect -> ExceptT StoreError IO AChat
|
||||
getDirectChatPreview_ :: DB.Connection -> VersionRangeChat -> User -> ChatPreviewData 'CTDirect -> ExceptT StoreError IO AChat
|
||||
getDirectChatPreview_ db vr user (DirectChatPD _ contactId lastItemId_ stats) = do
|
||||
contact <- getContact db vr user contactId
|
||||
lastItem <- case lastItemId_ of
|
||||
@@ -717,7 +717,7 @@ findGroupChatPreviews_ db User {userId} pagination clq =
|
||||
)
|
||||
([":user_id" := userId, ":rcv_new" := CISRcvNew, ":search" := search] <> pagParams)
|
||||
|
||||
getGroupChatPreview_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatPreviewData 'CTGroup -> ExceptT StoreError IO AChat
|
||||
getGroupChatPreview_ :: DB.Connection -> VersionRangeChat -> User -> ChatPreviewData 'CTGroup -> ExceptT StoreError IO AChat
|
||||
getGroupChatPreview_ db vr user (GroupChatPD _ groupId lastItemId_ stats) = do
|
||||
groupInfo <- getGroupInfo db vr user groupId
|
||||
lastItem <- case lastItemId_ of
|
||||
@@ -923,7 +923,7 @@ getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
aChat = AChat SCTContactConnection $ Chat (ContactConnection conn) [] stats
|
||||
in ACPD SCTContactConnection $ ContactConnectionPD updatedAt aChat
|
||||
|
||||
getDirectChat :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect)
|
||||
getDirectChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect)
|
||||
getDirectChat db vr user contactId pagination search_ = do
|
||||
let search = fromMaybe "" search_
|
||||
ct <- getContact db vr user contactId
|
||||
@@ -1043,7 +1043,7 @@ getDirectChatBefore_ db user@User {userId} ct@Contact {contactId} beforeChatItem
|
||||
|]
|
||||
(userId, contactId, search, beforeChatItemId, count)
|
||||
|
||||
getGroupChat :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChat db vr user groupId pagination search_ = do
|
||||
let search = fromMaybe "" search_
|
||||
g <- getGroupInfo db vr user groupId
|
||||
@@ -1526,7 +1526,7 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir,
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
getAllChatItems :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem]
|
||||
getAllChatItems :: DB.Connection -> VersionRangeChat -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem]
|
||||
getAllChatItems db vr user@User {userId} pagination search_ = do
|
||||
itemRefs <-
|
||||
rights . map toChatItemRef <$> case pagination of
|
||||
@@ -2179,7 +2179,7 @@ deleteLocalChatItem db User {userId} NoteFolder {noteFolderId} ci = do
|
||||
|]
|
||||
(userId, noteFolderId, itemId)
|
||||
|
||||
getChatItemByFileId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO AChatItem
|
||||
getChatItemByFileId :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO AChatItem
|
||||
getChatItemByFileId db vr user@User {userId} fileId = do
|
||||
(chatRef, itemId) <-
|
||||
ExceptT . firstRow' toChatItemRef (SEChatItemNotFoundByFileId fileId) $
|
||||
@@ -2195,13 +2195,13 @@ getChatItemByFileId db vr user@User {userId} fileId = do
|
||||
(userId, fileId)
|
||||
getAChatItem db vr user chatRef itemId
|
||||
|
||||
lookupChatItemByFileId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO (Maybe AChatItem)
|
||||
lookupChatItemByFileId :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO (Maybe AChatItem)
|
||||
lookupChatItemByFileId db vr user fileId = do
|
||||
fmap Just (getChatItemByFileId db vr user fileId) `catchError` \case
|
||||
SEChatItemNotFoundByFileId {} -> pure Nothing
|
||||
e -> throwError e
|
||||
|
||||
getChatItemByGroupId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> ExceptT StoreError IO AChatItem
|
||||
getChatItemByGroupId :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO AChatItem
|
||||
getChatItemByGroupId db vr user@User {userId} groupId = do
|
||||
(chatRef, itemId) <-
|
||||
ExceptT . firstRow' toChatItemRef (SEChatItemNotFoundByGroupId groupId) $
|
||||
@@ -2227,7 +2227,7 @@ getChatRefViaItemId db User {userId} itemId = do
|
||||
(Nothing, Just groupId) -> Right $ ChatRef CTGroup groupId
|
||||
(_, _) -> Left $ SEBadChatItem itemId Nothing
|
||||
|
||||
getAChatItem :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatRef -> ChatItemId -> ExceptT StoreError IO AChatItem
|
||||
getAChatItem :: DB.Connection -> VersionRangeChat -> User -> ChatRef -> ChatItemId -> ExceptT StoreError IO AChatItem
|
||||
getAChatItem db vr user chatRef itemId = case chatRef of
|
||||
ChatRef CTDirect contactId -> do
|
||||
ct <- getContact db vr user contactId
|
||||
@@ -2476,7 +2476,7 @@ createCIModeration db GroupInfo {groupId} moderatorMember itemMemberId itemShare
|
||||
|]
|
||||
(groupId, groupMemberId' moderatorMember, itemMemberId, itemSharedMId, msgId, moderatedAtTs)
|
||||
|
||||
getCIModeration :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> MemberId -> Maybe SharedMsgId -> IO (Maybe CIModeration)
|
||||
getCIModeration :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> Maybe SharedMsgId -> IO (Maybe CIModeration)
|
||||
getCIModeration _ _ _ _ _ Nothing = pure Nothing
|
||||
getCIModeration db vr user GroupInfo {groupId} itemMemberId (Just sharedMsgId) = do
|
||||
r_ <-
|
||||
|
||||
@@ -328,7 +328,7 @@ createUserContactLink db User {userId} agentConnId cReq subMode =
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff
|
||||
|
||||
getUserAddressConnections :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ExceptT StoreError IO [Connection]
|
||||
getUserAddressConnections :: DB.Connection -> VersionRangeChat -> User -> ExceptT StoreError IO [Connection]
|
||||
getUserAddressConnections db vr User {userId} = do
|
||||
cs <- liftIO getUserAddressConnections_
|
||||
if null cs then throwError SEUserContactLinkNotFound else pure cs
|
||||
@@ -349,7 +349,7 @@ getUserAddressConnections db vr User {userId} = do
|
||||
|]
|
||||
(userId, userId)
|
||||
|
||||
getUserContactLinks :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [(Connection, UserContact)]
|
||||
getUserContactLinks :: DB.Connection -> VersionRangeChat -> User -> IO [(Connection, UserContact)]
|
||||
getUserContactLinks db vr User {userId} =
|
||||
map toUserContactConnection
|
||||
<$> DB.query
|
||||
@@ -475,7 +475,7 @@ getUserContactLinkByConnReq db User {userId} (cReqSchema1, cReqSchema2) =
|
||||
|]
|
||||
(userId, cReqSchema1, cReqSchema2)
|
||||
|
||||
getContactWithoutConnViaAddress :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact)
|
||||
getContactWithoutConnViaAddress :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact)
|
||||
getContactWithoutConnViaAddress db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
ctId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
|
||||
@@ -166,12 +166,12 @@ type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, Maybe Int64, Bool, Maybe
|
||||
|
||||
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe Bool, Maybe GroupLinkId, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Bool, Maybe LocalAlias) :. EntityIdsRow :. (Maybe UTCTime, Maybe Text, Maybe UTCTime, Maybe PQSupport, Maybe PQEncryption, Maybe PQEncryption, Maybe PQEncryption, Maybe Int, Maybe VersionChat, Maybe VersionChat, Maybe VersionChat)
|
||||
|
||||
toConnection :: (PQSupport -> VersionRangeChat) -> ConnectionRow -> Connection
|
||||
toConnection :: VersionRangeChat -> ConnectionRow -> Connection
|
||||
toConnection vr ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter, chatV, minVer, maxVer)) =
|
||||
Connection
|
||||
{ connId,
|
||||
agentConnId = AgentConnId acId,
|
||||
connChatVersion = fromMaybe (vr pqSupport `peerConnChatVersion` peerChatVRange) chatV,
|
||||
connChatVersion = fromMaybe (vr `peerConnChatVersion` peerChatVRange) chatV,
|
||||
peerChatVRange = peerChatVRange,
|
||||
connLevel,
|
||||
viaContact,
|
||||
@@ -201,7 +201,7 @@ toConnection vr ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGr
|
||||
entityId_ ConnSndFile = sndFileId
|
||||
entityId_ ConnUserContact = userContactLinkId
|
||||
|
||||
toMaybeConnection :: (PQSupport -> VersionRangeChat) -> MaybeConnectionRow -> Maybe Connection
|
||||
toMaybeConnection :: VersionRangeChat -> MaybeConnectionRow -> Maybe Connection
|
||||
toMaybeConnection vr ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just viaGroupLink, groupLinkId, customUserProfileId, Just connStatus, Just connType, Just contactConnInitiated, Just localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (Just createdAt, code_, verifiedAt_, Just pqSupport, Just pqEncryption, pqSndEnabled_, pqRcvEnabled_, Just authErrCounter, connChatVersion, Just minVer, Just maxVer)) =
|
||||
Just $ toConnection vr ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, pqSupport, pqEncryption, pqSndEnabled_, pqRcvEnabled_, authErrCounter, connChatVersion, minVer, maxVer))
|
||||
toMaybeConnection _ _ = Nothing
|
||||
@@ -382,7 +382,7 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId =
|
||||
|
||||
type ContactRow = (ContactId, ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, Bool, ContactStatus) :. (Maybe MsgFilter, Maybe Bool, Bool, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime, Maybe GroupMemberId, Bool, Maybe CustomData)
|
||||
|
||||
toContact :: (PQSupport -> VersionRangeChat) -> User -> ContactRow :. MaybeConnectionRow -> Contact
|
||||
toContact :: VersionRangeChat -> User -> ContactRow :. MaybeConnectionRow -> Contact
|
||||
toContact vr user (((contactId, profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, favorite, preferences, userPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, customData)) :. connRow) =
|
||||
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
|
||||
activeConn = toMaybeConnection vr connRow
|
||||
|
||||
@@ -342,7 +342,6 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} ->
|
||||
["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName]
|
||||
CRRemoteCtrlStopped {} -> ["remote controller stopped"]
|
||||
CRContactPQAllowed u c (CR.PQEncryption pqOn) -> ttyUser u [ttyContact' c <> ": enable " <> (if pqOn then "quantum resistant" else "standard") <> " end-to-end encryption"]
|
||||
CRContactPQEnabled u c (CR.PQEncryption pqOn) -> ttyUser u [ttyContact' c <> ": " <> (if pqOn then "quantum resistant" else "standard") <> " end-to-end encryption enabled"]
|
||||
CRSQLResult rows -> map plain rows
|
||||
CRSlowSQLQueries {chatQueries, agentQueries} ->
|
||||
|
||||
@@ -809,7 +809,9 @@ testRestoreDirectory tmp = do
|
||||
groupFoundN 3 bob "privacy"
|
||||
groupFound bob "security"
|
||||
groupFoundN 3 cath "privacy"
|
||||
groupFound cath "security"
|
||||
cath #> "@SimpleX-Directory security"
|
||||
cath <## "SimpleX-Directory: quantum resistant end-to-end encryption enabled"
|
||||
groupFoundN' 2 cath "security"
|
||||
|
||||
listGroups :: HasCallStack => TestCC -> TestCC -> TestCC -> IO ()
|
||||
listGroups superUser bob cath = do
|
||||
@@ -1055,6 +1057,10 @@ groupFound = groupFoundN 2
|
||||
groupFoundN :: Int -> TestCC -> String -> IO ()
|
||||
groupFoundN count u name = do
|
||||
u #> ("@SimpleX-Directory " <> name)
|
||||
groupFoundN' count u name
|
||||
|
||||
groupFoundN' :: Int -> TestCC -> String -> IO ()
|
||||
groupFoundN' count u name = do
|
||||
u <# ("SimpleX-Directory> > " <> name)
|
||||
u <## " Found 1 group(s)."
|
||||
u <#. ("SimpleX-Directory> " <> name)
|
||||
|
||||
+12
-12
@@ -44,7 +44,7 @@ import Simplex.Messaging.Agent.RetryInterval
|
||||
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig)
|
||||
import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange, pattern PQSupportOff)
|
||||
import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange)
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import Simplex.Messaging.Server (runSMPServerBlocking)
|
||||
import Simplex.Messaging.Server.Env.STM
|
||||
@@ -146,8 +146,8 @@ testAgentCfgVPrev :: AgentConfig
|
||||
testAgentCfgVPrev =
|
||||
testAgentCfg
|
||||
{ smpClientVRange = prevRange $ smpClientVRange testAgentCfg,
|
||||
smpAgentVRange = \_ -> prevRange $ supportedSMPAgentVRange PQSupportOff,
|
||||
e2eEncryptVRange = \_ -> prevRange $ supportedE2EEncryptVRange PQSupportOff,
|
||||
smpAgentVRange = prevRange supportedSMPAgentVRange,
|
||||
e2eEncryptVRange = prevRange supportedE2EEncryptVRange,
|
||||
smpCfg = (smpCfg testAgentCfg) {serverVRange = prevRange $ serverVRange $ smpCfg testAgentCfg}
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ testAgentCfgVNext :: AgentConfig
|
||||
testAgentCfgVNext =
|
||||
testAgentCfg
|
||||
{ smpClientVRange = nextRange $ smpClientVRange testAgentCfg,
|
||||
smpAgentVRange = \_ -> mkVersionRange duplexHandshakeSMPAgentVersion $ max pqdrSMPAgentVersion currentSMPAgentVersion,
|
||||
e2eEncryptVRange = \_ -> mkVersionRange CR.kdfX3DHE2EEncryptVersion $ max CR.pqRatchetE2EEncryptVersion CR.currentE2EEncryptVersion,
|
||||
smpAgentVRange = mkVersionRange duplexHandshakeSMPAgentVersion $ max pqdrSMPAgentVersion currentSMPAgentVersion,
|
||||
e2eEncryptVRange = mkVersionRange CR.kdfX3DHE2EEncryptVersion $ max CR.pqRatchetE2EEncryptVersion CR.currentE2EEncryptVersion,
|
||||
smpCfg = (smpCfg testAgentCfg) {serverVRange = nextRange $ serverVRange $ smpCfg testAgentCfg}
|
||||
}
|
||||
|
||||
@@ -164,29 +164,29 @@ testAgentCfgV1 :: AgentConfig
|
||||
testAgentCfgV1 =
|
||||
testAgentCfg
|
||||
{ smpClientVRange = v1Range,
|
||||
smpAgentVRange = \_ -> versionToRange duplexHandshakeSMPAgentVersion,
|
||||
e2eEncryptVRange = \_ -> versionToRange CR.kdfX3DHE2EEncryptVersion,
|
||||
smpAgentVRange = versionToRange duplexHandshakeSMPAgentVersion,
|
||||
e2eEncryptVRange = versionToRange CR.kdfX3DHE2EEncryptVersion,
|
||||
smpCfg = (smpCfg testAgentCfg) {serverVRange = versionToRange batchCmdsSMPVersion}
|
||||
}
|
||||
|
||||
testCfgVPrev :: ChatConfig
|
||||
testCfgVPrev =
|
||||
testCfg
|
||||
{ chatVRange = \_ -> prevRange $ chatVRange testCfg PQSupportOff,
|
||||
{ chatVRange = prevRange $ chatVRange testCfg,
|
||||
agentConfig = testAgentCfgVPrev
|
||||
}
|
||||
|
||||
testCfgVNext :: ChatConfig
|
||||
testCfgVNext =
|
||||
testCfg
|
||||
{ chatVRange = \_ -> mkVersionRange initialChatVersion $ max pqEncryptionCompressionVersion currentChatVersion,
|
||||
{ chatVRange = mkVersionRange initialChatVersion $ max pqEncryptionCompressionVersion currentChatVersion,
|
||||
agentConfig = testAgentCfgVNext
|
||||
}
|
||||
|
||||
testCfgV1 :: ChatConfig
|
||||
testCfgV1 =
|
||||
testCfg
|
||||
{ chatVRange = const v1Range,
|
||||
{ chatVRange = v1Range,
|
||||
agentConfig = testAgentCfgV1
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ testCfgCreateGroupDirect =
|
||||
mkCfgCreateGroupDirect testCfg
|
||||
|
||||
mkCfgCreateGroupDirect :: ChatConfig -> ChatConfig
|
||||
mkCfgCreateGroupDirect cfg = cfg {chatVRange = const groupCreateDirectVRange}
|
||||
mkCfgCreateGroupDirect cfg = cfg {chatVRange = groupCreateDirectVRange}
|
||||
|
||||
groupCreateDirectVRange :: VersionRangeChat
|
||||
groupCreateDirectVRange = mkVersionRange (VersionChat 1) (VersionChat 1)
|
||||
@@ -220,7 +220,7 @@ testCfgGroupLinkViaContact =
|
||||
mkCfgGroupLinkViaContact testCfg
|
||||
|
||||
mkCfgGroupLinkViaContact :: ChatConfig -> ChatConfig
|
||||
mkCfgGroupLinkViaContact cfg = cfg {chatVRange = const groupLinkViaContactVRange}
|
||||
mkCfgGroupLinkViaContact cfg = cfg {chatVRange = groupLinkViaContactVRange}
|
||||
|
||||
groupLinkViaContactVRange :: VersionRangeChat
|
||||
groupLinkViaContactVRange = mkVersionRange (VersionChat 1) (VersionChat 2)
|
||||
|
||||
+81
-322
@@ -10,7 +10,7 @@ import ChatClient
|
||||
import ChatTests.Utils
|
||||
import Control.Concurrent (threadDelay)
|
||||
import Control.Concurrent.Async (concurrently_)
|
||||
import Control.Monad (forM_, when)
|
||||
import Control.Monad (forM_)
|
||||
import Data.Aeson (ToJSON)
|
||||
import qualified Data.Aeson as J
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
@@ -21,11 +21,10 @@ import qualified Simplex.Chat.AppSettings as AS
|
||||
import Simplex.Chat.Call
|
||||
import Simplex.Chat.Controller (ChatConfig (..))
|
||||
import Simplex.Chat.Options (ChatOpts (..))
|
||||
import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion, supportedChatVRange)
|
||||
import Simplex.Chat.Protocol (supportedChatVRange)
|
||||
import Simplex.Chat.Store (agentStoreFile, chatStoreFile)
|
||||
import Simplex.Chat.Types (VersionRangeChat, authErrDisableCount, sameVerificationCode, verificationCode, VersionChat, pattern VersionChat)
|
||||
import Simplex.Chat.Types (VersionRangeChat, authErrDisableCount, sameVerificationCode, verificationCode, pattern VersionChat)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import Simplex.Messaging.Util (safeDecodeUtf8)
|
||||
import Simplex.Messaging.Version
|
||||
import System.Directory (copyFile, doesDirectoryExist, doesFileExist)
|
||||
@@ -47,6 +46,7 @@ chatDirectTests = do
|
||||
it "direct timed message" testDirectTimedMessage
|
||||
it "repeat AUTH errors disable contact" testRepeatAuthErrorsDisableContact
|
||||
it "should send multiline message" testMultilineMessage
|
||||
it "send large message" testLargeMessage
|
||||
describe "duplicate contacts" $ do
|
||||
it "duplicate contacts are separate (contacts don't merge)" testDuplicateContactsSeparate
|
||||
it "new contact is separate with multiple duplicate contacts (contacts don't merge)" testDuplicateContactsMultipleSeparate
|
||||
@@ -116,25 +116,18 @@ chatDirectTests = do
|
||||
it "should send delivery receipts depending on configuration" testConfigureDeliveryReceipts
|
||||
describe "negotiate connection peer chat protocol version range" $ do
|
||||
describe "peer version range correctly set for new connection via invitation" $ do
|
||||
testInvVRange (supportedChatVRange PQSupportOff) (supportedChatVRange PQSupportOff)
|
||||
testInvVRange (supportedChatVRange PQSupportOff) vr11
|
||||
testInvVRange vr11 (supportedChatVRange PQSupportOff)
|
||||
testInvVRange supportedChatVRange supportedChatVRange
|
||||
testInvVRange supportedChatVRange vr11
|
||||
testInvVRange vr11 supportedChatVRange
|
||||
testInvVRange vr11 vr11
|
||||
describe "peer version range correctly set for new connection via contact request" $ do
|
||||
testReqVRange (supportedChatVRange PQSupportOff) (supportedChatVRange PQSupportOff)
|
||||
testReqVRange (supportedChatVRange PQSupportOff) vr11
|
||||
testReqVRange vr11 (supportedChatVRange PQSupportOff)
|
||||
testReqVRange supportedChatVRange supportedChatVRange
|
||||
testReqVRange supportedChatVRange vr11
|
||||
testReqVRange vr11 supportedChatVRange
|
||||
testReqVRange vr11 vr11
|
||||
it "update peer version range on received messages" testUpdatePeerChatVRange
|
||||
describe "network statuses" $ do
|
||||
it "should get network statuses" testGetNetworkStatuses
|
||||
describe "PQ tests" $ do
|
||||
describe "enable PQ before connection, connect via invitation link" $ pqMatrix2 runTestPQConnectViaLink
|
||||
describe "enable PQ before connection, connect via contact address" $ pqMatrix2 runTestPQConnectViaAddress
|
||||
describe "connect via invitation link with PQ encryption enabled" $ pqVersionTestMatrix2 runTestPQVersionsViaLink
|
||||
describe "connect via contact address with PQ encryption enabled" $ pqVersionTestMatrix2 runTestPQVersionsViaAddress
|
||||
it "should enable PQ after several messages in connection without PQ" testPQEnableContact
|
||||
it "should enable PQ, reduce envelope size and enable compression" testPQEnableContactCompression
|
||||
where
|
||||
testInvVRange vr1 vr2 = it (vRangeStr vr1 <> " - " <> vRangeStr vr2) $ testConnInvChatVRange vr1 vr2
|
||||
testReqVRange vr1 vr2 = it (vRangeStr vr1 <> " - " <> vRangeStr vr2) $ testConnReqChatVRange vr1 vr2
|
||||
@@ -142,7 +135,7 @@ chatDirectTests = do
|
||||
testAddContact :: HasCallStack => SpecWith FilePath
|
||||
testAddContact = versionTestMatrix2 runTestAddContact
|
||||
where
|
||||
runTestAddContact alice bob = do
|
||||
runTestAddContact pqExpected alice bob = do
|
||||
alice ##> "/_connect 1"
|
||||
inv <- getInvitation alice
|
||||
bob ##> ("/_connect 1 " <> inv)
|
||||
@@ -151,46 +144,50 @@ testAddContact = versionTestMatrix2 runTestAddContact
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
threadDelay 100000
|
||||
chatsEmpty alice bob
|
||||
chatsEmpty
|
||||
alice #> "@bob hello there 🙂"
|
||||
bob <# "alice> hello there 🙂"
|
||||
alice ##> "/_unread chat @2 on"
|
||||
alice <## "ok"
|
||||
alice ##> "/_unread chat @2 off"
|
||||
alice <## "ok"
|
||||
chatsOneMessage alice bob
|
||||
chatsOneMessage
|
||||
bob #> "@alice hello there"
|
||||
alice <# "bob> hello there"
|
||||
bob #> "@alice how are you?"
|
||||
alice <# "bob> how are you?"
|
||||
chatsManyMessages alice bob
|
||||
chatsEmpty alice bob = do
|
||||
alice @@@ [("@bob", lastChatFeature)]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
bob @@@ [("@alice", lastChatFeature)]
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures)
|
||||
chatsOneMessage alice bob = do
|
||||
alice @@@ [("@bob", "hello there 🙂")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hello there 🙂")])
|
||||
bob @@@ [("@alice", "hello there 🙂")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hello there 🙂")])
|
||||
chatsManyMessages alice bob = do
|
||||
alice @@@ [("@bob", "how are you?")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hello there 🙂"), (0, "hello there"), (0, "how are you?")])
|
||||
bob @@@ [("@alice", "how are you?")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hello there 🙂"), (1, "hello there"), (1, "how are you?")])
|
||||
-- pagination
|
||||
alice #$> ("/_get chat @2 after=" <> itemId 1 <> " count=100", chat, [(0, "hello there"), (0, "how are you?")])
|
||||
alice #$> ("/_get chat @2 before=" <> itemId 2 <> " count=100", chat, chatFeatures <> [(1, "hello there 🙂")])
|
||||
-- search
|
||||
alice #$> ("/_get chat @2 count=100 search=ello ther", chat, [(1, "hello there 🙂"), (0, "hello there")])
|
||||
-- read messages
|
||||
alice #$> ("/_read chat @2 from=1 to=100", id, "ok")
|
||||
bob #$> ("/_read chat @2 from=1 to=100", id, "ok")
|
||||
alice #$> ("/_read chat @2", id, "ok")
|
||||
bob #$> ("/_read chat @2", id, "ok")
|
||||
alice #$> ("/read user", id, "ok")
|
||||
alice #$> ("/_read user 1", id, "ok")
|
||||
chatsManyMessages
|
||||
where
|
||||
chatsEmpty = do
|
||||
alice @@@ [("@bob", lastChatFeature)]
|
||||
alice #$> ("/_get chat @2 count=100", chat, features)
|
||||
bob @@@ [("@alice", lastChatFeature)]
|
||||
bob #$> ("/_get chat @2 count=100", chat, features)
|
||||
chatsOneMessage = do
|
||||
alice @@@ [("@bob", "hello there 🙂")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, features <> [(1, "hello there 🙂")])
|
||||
bob @@@ [("@alice", "hello there 🙂")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, features <> [(0, "hello there 🙂")])
|
||||
chatsManyMessages = do
|
||||
alice @@@ [("@bob", "how are you?")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, features <> [(1, "hello there 🙂"), (0, "hello there"), (0, "how are you?")])
|
||||
bob @@@ [("@alice", "how are you?")]
|
||||
bob #$> ("/_get chat @2 count=100", chat, features <> [(0, "hello there 🙂"), (1, "hello there"), (1, "how are you?")])
|
||||
-- pagination
|
||||
alice #$> ("/_get chat @2 after=" <> itemId 1 <> " count=100", chat, [(0, "hello there"), (0, "how are you?")])
|
||||
alice #$> ("/_get chat @2 before=" <> itemId 2 <> " count=100", chat, features <> [(1, "hello there 🙂")])
|
||||
-- search
|
||||
alice #$> ("/_get chat @2 count=100 search=ello ther", chat, [(1, "hello there 🙂"), (0, "hello there")])
|
||||
-- read messages
|
||||
alice #$> ("/_read chat @2 from=1 to=100", id, "ok")
|
||||
bob #$> ("/_read chat @2 from=1 to=100", id, "ok")
|
||||
alice #$> ("/_read chat @2", id, "ok")
|
||||
bob #$> ("/_read chat @2", id, "ok")
|
||||
alice #$> ("/read user", id, "ok")
|
||||
alice #$> ("/_read user 1", id, "ok")
|
||||
features = if pqExpected
|
||||
then chatFeatures
|
||||
else (0, e2eeInfoNoPQStr) : tail chatFeatures
|
||||
|
||||
testDuplicateContactsSeparate :: HasCallStack => FilePath -> IO ()
|
||||
testDuplicateContactsSeparate =
|
||||
@@ -728,6 +725,20 @@ testMultilineMessage = testChat3 aliceProfile bobProfile cathProfile $ \alice bo
|
||||
cath <# "alice> hello"
|
||||
cath <## "there"
|
||||
|
||||
testLargeMessage :: HasCallStack => FilePath -> IO ()
|
||||
testLargeMessage =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
connectUsers alice bob
|
||||
|
||||
img <- genProfileImg
|
||||
let profileImage = "data:image/png;base64," <> B.unpack img
|
||||
alice `send` ("/_profile 1 {\"displayName\": \"alice2\", \"fullName\": \"\", \"image\": \"" <> profileImage <> "\"}")
|
||||
_trimmedCmd1 <- getTermLine alice
|
||||
alice <## "user profile is changed to alice2 (your 1 contacts are notified)"
|
||||
bob <## "contact alice changed to alice2"
|
||||
bob <## "use @alice2 <message> to send messages"
|
||||
|
||||
testGetSetSMPServers :: HasCallStack => FilePath -> IO ()
|
||||
testGetSetSMPServers =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
@@ -1649,17 +1660,17 @@ testUsersDifferentCIExpirationTTL tmp = do
|
||||
-- set ttl for first user
|
||||
alice ##> "/user alice"
|
||||
showActiveUser alice "alice (Alice)"
|
||||
alice #$> ("/_ttl 1 1", id, "ok")
|
||||
alice #$> ("/_ttl 1 2", id, "ok")
|
||||
|
||||
-- set ttl for second user
|
||||
alice ##> "/user alisa"
|
||||
showActiveUser alice "alisa"
|
||||
alice #$> ("/_ttl 2 3", id, "ok")
|
||||
alice #$> ("/_ttl 2 4", id, "ok")
|
||||
|
||||
-- first user messages
|
||||
alice ##> "/user alice"
|
||||
showActiveUser alice "alice (Alice)"
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: 1 second(s)")
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: 2 second(s)")
|
||||
|
||||
alice #> "@bob alice 3"
|
||||
bob <# "alice> alice 3"
|
||||
@@ -1671,7 +1682,7 @@ testUsersDifferentCIExpirationTTL tmp = do
|
||||
-- second user messages
|
||||
alice ##> "/user alisa"
|
||||
showActiveUser alice "alisa"
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: 3 second(s)")
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: 4 second(s)")
|
||||
|
||||
alice #> "@bob alisa 3"
|
||||
bob <# "alisa> alisa 3"
|
||||
@@ -1680,7 +1691,7 @@ testUsersDifferentCIExpirationTTL tmp = do
|
||||
|
||||
alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")])
|
||||
|
||||
threadDelay 2000000
|
||||
threadDelay 3000000
|
||||
|
||||
-- messages both before and after setting chat item ttl are deleted
|
||||
-- first user messages
|
||||
@@ -1916,13 +1927,13 @@ testUsersTimedMessages tmp = do
|
||||
withNewTestChat tmp "bob" bobProfile $ \bob -> do
|
||||
withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
||||
connectUsers alice bob
|
||||
configureTimedMessages alice bob "2" "1"
|
||||
configureTimedMessages alice bob "2" "2"
|
||||
|
||||
-- create second user and configure timed messages for contact
|
||||
alice ##> "/create user alisa"
|
||||
showActiveUser alice "alisa"
|
||||
connectUsers alice bob
|
||||
configureTimedMessages alice bob "4" "2"
|
||||
configureTimedMessages alice bob "4" "3"
|
||||
|
||||
-- first user messages
|
||||
alice ##> "/user alice"
|
||||
@@ -1943,7 +1954,7 @@ testUsersTimedMessages tmp = do
|
||||
alice <# "bob> alisa 2"
|
||||
|
||||
-- messages are deleted after ttl
|
||||
threadDelay 500000
|
||||
threadDelay 1500000
|
||||
|
||||
alice ##> "/user alice"
|
||||
showActiveUser alice "alice (Alice)"
|
||||
@@ -2094,7 +2105,7 @@ testUserPrivacy =
|
||||
alice <##? chatHistory
|
||||
alice ##> "/_get items before=13 count=10"
|
||||
alice
|
||||
<##? [ ConsoleString ("bob> " <> e2eeInfoNoPQStr),
|
||||
<##? [ ConsoleString ("bob> " <> e2eeInfoPQStr),
|
||||
"bob> Disappearing messages: allowed",
|
||||
"bob> Full deletion: off",
|
||||
"bob> Message reactions: enabled",
|
||||
@@ -2165,7 +2176,7 @@ testUserPrivacy =
|
||||
alice <## "messages are shown"
|
||||
alice <## "profile is visible"
|
||||
chatHistory =
|
||||
[ ConsoleString ("bob> " <> e2eeInfoNoPQStr),
|
||||
[ ConsoleString ("bob> " <> e2eeInfoPQStr),
|
||||
"bob> Disappearing messages: allowed",
|
||||
"bob> Full deletion: off",
|
||||
"bob> Message reactions: enabled",
|
||||
@@ -2347,6 +2358,7 @@ testMarkContactVerified =
|
||||
alice <## "sending messages via: localhost"
|
||||
alice <## "you've shared main profile with this contact"
|
||||
alice <## connVerified
|
||||
alice <## "quantum resistant end-to-end encryption"
|
||||
alice <## currentChatVRangeInfo
|
||||
where
|
||||
connVerified
|
||||
@@ -2517,6 +2529,7 @@ testSyncRatchetCodeReset tmp =
|
||||
bob <## "sending messages via: localhost"
|
||||
bob <## "you've shared main profile with this contact"
|
||||
bob <## connVerified
|
||||
bob <## "quantum resistant end-to-end encryption"
|
||||
bob <## currentChatVRangeInfo
|
||||
where
|
||||
connVerified
|
||||
@@ -2667,8 +2680,8 @@ testConfigureDeliveryReceipts tmp =
|
||||
|
||||
testConnInvChatVRange :: HasCallStack => VersionRangeChat -> VersionRangeChat -> FilePath -> IO ()
|
||||
testConnInvChatVRange ct1VRange ct2VRange tmp =
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const ct1VRange} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const ct2VRange} "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = ct1VRange} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = ct2VRange} "bob" bobProfile $ \bob -> do
|
||||
connectUsers alice bob
|
||||
|
||||
alice ##> "/i bob"
|
||||
@@ -2679,8 +2692,8 @@ testConnInvChatVRange ct1VRange ct2VRange tmp =
|
||||
|
||||
testConnReqChatVRange :: HasCallStack => VersionRangeChat -> VersionRangeChat -> FilePath -> IO ()
|
||||
testConnReqChatVRange ct1VRange ct2VRange tmp =
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const ct1VRange} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const ct2VRange} "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = ct1VRange} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = ct2VRange} "bob" bobProfile $ \bob -> do
|
||||
alice ##> "/ad"
|
||||
cLink <- getContactLink alice True
|
||||
bob ##> ("/c " <> cLink)
|
||||
@@ -2707,7 +2720,7 @@ testUpdatePeerChatVRange tmp =
|
||||
contactInfoChatVRange alice vr11
|
||||
|
||||
bob ##> "/i alice"
|
||||
contactInfoChatVRange bob (supportedChatVRange PQSupportOff)
|
||||
contactInfoChatVRange bob supportedChatVRange
|
||||
|
||||
withTestChat tmp "bob" $ \bob -> do
|
||||
bob <## "1 contacts connected (use /cs for the list)"
|
||||
@@ -2716,10 +2729,10 @@ testUpdatePeerChatVRange tmp =
|
||||
alice <# "bob> hello 1"
|
||||
|
||||
alice ##> "/i bob"
|
||||
contactInfoChatVRange alice (supportedChatVRange PQSupportOff)
|
||||
contactInfoChatVRange alice supportedChatVRange
|
||||
|
||||
bob ##> "/i alice"
|
||||
contactInfoChatVRange bob (supportedChatVRange PQSupportOff)
|
||||
contactInfoChatVRange bob supportedChatVRange
|
||||
|
||||
withTestChatCfg tmp cfg11 "bob" $ \bob -> do
|
||||
bob <## "1 contacts connected (use /cs for the list)"
|
||||
@@ -2731,9 +2744,9 @@ testUpdatePeerChatVRange tmp =
|
||||
contactInfoChatVRange alice vr11
|
||||
|
||||
bob ##> "/i alice"
|
||||
contactInfoChatVRange bob (supportedChatVRange PQSupportOff)
|
||||
contactInfoChatVRange bob supportedChatVRange
|
||||
where
|
||||
cfg11 = testCfg {chatVRange = const vr11} :: ChatConfig
|
||||
cfg11 = testCfg {chatVRange = vr11} :: ChatConfig
|
||||
|
||||
testGetNetworkStatuses :: HasCallStack => FilePath -> IO ()
|
||||
testGetNetworkStatuses tmp = do
|
||||
@@ -2759,259 +2772,5 @@ contactInfoChatVRange cc (VersionRange minVer maxVer) = do
|
||||
cc <## "sending messages via: localhost"
|
||||
cc <## "you've shared main profile with this contact"
|
||||
cc <## "connection not verified, use /code command to see security code"
|
||||
cc <## "quantum resistant end-to-end encryption"
|
||||
cc <## ("peer chat protocol version range: (" <> show minVer <> ", " <> show maxVer <> ")")
|
||||
|
||||
runTestPQConnectViaLink :: HasCallStack => (TestCC, PQEnabled) -> (TestCC, PQEnabled) -> IO ()
|
||||
runTestPQConnectViaLink (alice, aPQ) (bob, bPQ) = do
|
||||
when aPQ $ pqOn alice
|
||||
when bPQ $ pqOn bob
|
||||
|
||||
connectUsers alice bob
|
||||
|
||||
(alice, "hi") `pqSend` bob
|
||||
(bob, "hey") `pqSend` alice
|
||||
|
||||
alice ##> "/_get chat @2 count=100"
|
||||
ra <- chat <$> getTermLine alice
|
||||
ra `shouldContain` [(0, e2eeInfo)]
|
||||
alice `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled
|
||||
|
||||
bob ##> "/_get chat @2 count=100"
|
||||
rb <- chat <$> getTermLine bob
|
||||
rb `shouldContain` [(0, e2eeInfo)]
|
||||
bob `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled
|
||||
where
|
||||
pqEnabled = aPQ && bPQ
|
||||
pqSend = if pqEnabled then (+#>) else (\#>)
|
||||
e2eeInfo = if pqEnabled then e2eeInfoPQStr else e2eeInfoNoPQStr
|
||||
|
||||
pqOn :: TestCC -> IO ()
|
||||
pqOn cc = do
|
||||
cc ##> "/pq on"
|
||||
cc <## "ok"
|
||||
|
||||
runTestPQConnectViaAddress :: HasCallStack => (TestCC, PQEnabled) -> (TestCC, PQEnabled) -> IO ()
|
||||
runTestPQConnectViaAddress (alice, aPQ) (bob, bPQ) = do
|
||||
when aPQ $ pqOn alice
|
||||
when bPQ $ pqOn bob
|
||||
|
||||
alice ##> "/ad"
|
||||
cLink <- getContactLink alice True
|
||||
bob ##> ("/c " <> cLink)
|
||||
alice <#? bob
|
||||
alice @@@ [("<@bob", "")]
|
||||
alice ##> "/ac bob"
|
||||
alice <## "bob (Bob): accepting contact request..."
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
|
||||
(alice, "hi") `pqSend` bob
|
||||
(bob, "hey") `pqSend` alice
|
||||
|
||||
alice ##> "/_get chat @2 count=100"
|
||||
ra <- chat <$> getTermLine alice
|
||||
ra `shouldContain` [(0, e2eeInfo)]
|
||||
alice `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled
|
||||
|
||||
bob ##> "/_get chat @2 count=100"
|
||||
rb <- chat <$> getTermLine bob
|
||||
rb `shouldContain` [(0, e2eeInfo)]
|
||||
bob `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled
|
||||
where
|
||||
pqEnabled = aPQ && bPQ
|
||||
pqSend = if pqEnabled then (+#>) else (\#>)
|
||||
e2eeInfo = if pqEnabled then e2eeInfoPQStr else e2eeInfoNoPQStr
|
||||
|
||||
runTestPQVersionsViaLink :: HasCallStack => TestCC -> TestCC -> Bool -> VersionChat -> IO ()
|
||||
runTestPQVersionsViaLink alice bob pqExpected vExpected = do
|
||||
img <- genProfileImg
|
||||
let profileImage = "data:image/png;base64," <> B.unpack img
|
||||
alice `send` ("/set profile image " <> profileImage)
|
||||
_trimmedCmd1 <- getTermLine alice
|
||||
alice <## "profile image updated"
|
||||
bob `send` ("/set profile image " <> profileImage)
|
||||
_trimmedCmd2 <- getTermLine bob
|
||||
bob <## "profile image updated"
|
||||
|
||||
pqOn alice
|
||||
pqOn bob
|
||||
|
||||
connectUsers alice bob
|
||||
|
||||
(alice, "hi", vExpected) `pqSend` (bob, vExpected)
|
||||
(bob, "hey", vExpected) `pqSend` (alice, vExpected)
|
||||
|
||||
alice ##> "/_get chat @2 count=100"
|
||||
ra <- chat <$> getTermLine alice
|
||||
ra `shouldContain` [(0, e2eeInfo)]
|
||||
alice `pqForContact` 2 `shouldReturn` PQEncryption pqExpected
|
||||
|
||||
bob ##> "/_get chat @2 count=100"
|
||||
rb <- chat <$> getTermLine bob
|
||||
rb `shouldContain` [(0, e2eeInfo)]
|
||||
bob `pqForContact` 2 `shouldReturn` PQEncryption pqExpected
|
||||
where
|
||||
pqSend = if pqExpected then (+:#>) else (\:#>)
|
||||
e2eeInfo = if pqExpected then e2eeInfoPQStr else e2eeInfoNoPQStr
|
||||
|
||||
runTestPQVersionsViaAddress :: HasCallStack => TestCC -> TestCC -> Bool -> VersionChat -> IO ()
|
||||
runTestPQVersionsViaAddress alice bob pqExpected vExpected = do
|
||||
img <- genProfileImg
|
||||
let profileImage = "data:image/png;base64," <> B.unpack img
|
||||
alice `send` ("/set profile image " <> profileImage)
|
||||
_trimmedCmd1 <- getTermLine alice
|
||||
alice <## "profile image updated"
|
||||
bob `send` ("/set profile image " <> profileImage)
|
||||
_trimmedCmd2 <- getTermLine bob
|
||||
bob <## "profile image updated"
|
||||
|
||||
pqOn alice
|
||||
pqOn bob
|
||||
|
||||
alice ##> "/ad"
|
||||
cLink <- getContactLink alice True
|
||||
bob ##> ("/c " <> cLink)
|
||||
alice <#? bob
|
||||
alice @@@ [("<@bob", "")]
|
||||
alice ##> "/ac bob"
|
||||
alice <## "bob (Bob): accepting contact request..."
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
|
||||
(alice, "hi", vExpected) `pqSend` (bob, vExpected)
|
||||
(bob, "hey", vExpected) `pqSend` (alice, vExpected)
|
||||
|
||||
alice ##> "/_get chat @2 count=100"
|
||||
ra <- chat <$> getTermLine alice
|
||||
ra `shouldContain` [(0, e2eeInfo)]
|
||||
alice `pqForContact` 2 `shouldReturn` PQEncryption pqExpected
|
||||
|
||||
bob ##> "/_get chat @2 count=100"
|
||||
rb <- chat <$> getTermLine bob
|
||||
rb `shouldContain` [(0, e2eeInfo)]
|
||||
bob `pqForContact` 2 `shouldReturn` PQEncryption pqExpected
|
||||
where
|
||||
pqSend = if pqExpected then (+:#>) else (\:#>)
|
||||
e2eeInfo = if pqExpected then e2eeInfoPQStr else e2eeInfoNoPQStr
|
||||
|
||||
testPQEnableContact :: HasCallStack => FilePath -> IO ()
|
||||
testPQEnableContact =
|
||||
testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||
connectUsers alice bob
|
||||
(alice, "hi") \#> bob
|
||||
(bob, "hey") \#> alice
|
||||
|
||||
alice ##> "/_get chat @2 count=100"
|
||||
ra <- chat <$> getTermLine alice
|
||||
ra `shouldContain` [(0, e2eeInfoNoPQStr)]
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
|
||||
bob ##> "/_get chat @2 count=100"
|
||||
rb <- chat <$> getTermLine bob
|
||||
rb `shouldContain` [(0, e2eeInfoNoPQStr)]
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
|
||||
sendMany PQEncOff alice bob
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
|
||||
-- enabling experimental flags doesn't enable PQ in previously created connection
|
||||
pqOn alice
|
||||
sendMany PQEncOff alice bob
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
|
||||
pqOn bob
|
||||
sendMany PQEncOff alice bob
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
|
||||
-- if only one contact allows PQ, it's not enabled
|
||||
alice ##> "/pq @bob on"
|
||||
alice <## "bob: enable quantum resistant end-to-end encryption"
|
||||
sendMany PQEncOff alice bob
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
|
||||
-- both contacts have to allow PQ to enable it
|
||||
bob ##> "/pq @alice on"
|
||||
bob <## "alice: enable quantum resistant end-to-end encryption"
|
||||
|
||||
(alice, "1") \#> bob
|
||||
(bob, "2") \#> alice
|
||||
(alice, "3") \#> bob
|
||||
(bob, "4") \#> alice
|
||||
(alice, "5") +#> bob
|
||||
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
|
||||
(bob, "6") ++#> alice
|
||||
-- equivalent to:
|
||||
-- bob `send` "@alice 6"
|
||||
-- bob <## "alice: quantum resistant end-to-end encryption enabled"
|
||||
-- bob <# "@alice 6"
|
||||
-- alice <## "bob: quantum resistant end-to-end encryption enabled"
|
||||
-- alice <# "bob> 6"
|
||||
|
||||
PQEncOn <- alice `pqForContact` 2
|
||||
alice #$> ("/_get chat @2 count=2", chat, [(0, e2eeInfoPQStr), (0, "6")])
|
||||
|
||||
PQEncOn <- bob `pqForContact` 2
|
||||
bob #$> ("/_get chat @2 count=2", chat, [(1, e2eeInfoPQStr), (1, "6")])
|
||||
|
||||
(alice, "6") +#> bob
|
||||
(bob, "7") +#> alice
|
||||
|
||||
sendMany PQEncOn alice bob
|
||||
|
||||
PQEncOn <- alice `pqForContact` 2
|
||||
PQEncOn <- bob `pqForContact` 2
|
||||
pure ()
|
||||
|
||||
sendMany :: PQEncryption -> TestCC -> TestCC -> IO ()
|
||||
sendMany pqEnc alice bob =
|
||||
forM_ [(1 :: Int) .. 10] $ \i -> do
|
||||
sndRcv pqEnc False (alice, show i) bob
|
||||
sndRcv pqEnc False (bob, show i) alice
|
||||
|
||||
testPQEnableContactCompression :: HasCallStack => FilePath -> IO ()
|
||||
testPQEnableContactCompression =
|
||||
testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||
connectUsers alice bob
|
||||
(alice, "hi") \#> bob
|
||||
(bob, "hey") \#> alice
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
(alice, "lrg 1", v) \:#> (bob, v)
|
||||
(bob, "lrg 2", v) \:#> (alice, v)
|
||||
PQSupportOff <- alice `pqSupportForCt` 2
|
||||
alice ##> "/pq @bob on"
|
||||
alice <## "bob: enable quantum resistant end-to-end encryption"
|
||||
PQSupportOn <- alice `pqSupportForCt` 2
|
||||
(alice, "lrg 3", v) \:#> (bob, v)
|
||||
(bob, "lrg 4", v) \:#> (alice, v)
|
||||
PQSupportOff <- bob `pqSupportForCt` 2
|
||||
bob ##> "/pq @alice on"
|
||||
bob <## "alice: enable quantum resistant end-to-end encryption"
|
||||
PQSupportOn <- bob `pqSupportForCt` 2
|
||||
threadDelay 300000
|
||||
(alice, "lrg 1", v) \:#> (bob, v')
|
||||
threadDelay 300000
|
||||
(bob, "lrg 2", v') \:#> (alice, v')
|
||||
threadDelay 300000
|
||||
(alice, "lrg 3", v') \:#> (bob, v')
|
||||
threadDelay 300000
|
||||
(bob, "lrg 4", v') \:#> (alice, v')
|
||||
threadDelay 300000
|
||||
(alice, "lrg 5", v') +:#> (bob, v')
|
||||
PQEncOff <- alice `pqForContact` 2
|
||||
PQEncOff <- bob `pqForContact` 2
|
||||
(bob, "lrg 6", v') ++:#> (alice, v')
|
||||
(alice, "lrg 7", v') +:#> (bob, v')
|
||||
(bob, "lrg 8", v') +:#> (alice, v')
|
||||
where
|
||||
v = currentChatVersion
|
||||
v' = pqEncryptionCompressionVersion
|
||||
|
||||
@@ -9,7 +9,7 @@ import ChatTests.Utils
|
||||
import Control.Concurrent (threadDelay)
|
||||
import Control.Concurrent.Async (concurrently_)
|
||||
import Control.Monad (void, when)
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.List (isInfixOf)
|
||||
import qualified Data.Text as T
|
||||
import Simplex.Chat.Controller (ChatConfig (..))
|
||||
@@ -18,7 +18,6 @@ import Simplex.Chat.Store (agentStoreFile, chatStoreFile)
|
||||
import Simplex.Chat.Types (VersionRangeChat)
|
||||
import Simplex.Chat.Types.Shared (GroupMemberRole (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Crypto.Ratchet (pattern PQSupportOff)
|
||||
import System.Directory (copyFile)
|
||||
import System.FilePath ((</>))
|
||||
import Test.Hspec hiding (it)
|
||||
@@ -29,6 +28,7 @@ chatGroupTests = do
|
||||
describe "add contacts, create group and send/receive messages" testGroupMatrix
|
||||
it "v1: add contacts, create group and send/receive messages" testGroup
|
||||
it "v1: add contacts, create group and send/receive messages, check messages" testGroupCheckMessages
|
||||
it "send large message" testGroupLargeMessage
|
||||
it "create group with incognito membership" testNewGroupIncognito
|
||||
it "create and join group with 4 members" testGroup2
|
||||
it "create and delete group" testGroupDelete
|
||||
@@ -150,19 +150,19 @@ chatGroupTests = do
|
||||
it "member was blocked before joining group" testBlockForAllBeforeJoining
|
||||
it "can't repeat block, unblock" testBlockForAllCantRepeat
|
||||
where
|
||||
_0 = supportedChatVRange PQSupportOff -- don't create direct connections
|
||||
_0 = supportedChatVRange -- don't create direct connections
|
||||
_1 = groupCreateDirectVRange
|
||||
-- having host configured with older version doesn't have effect in tests
|
||||
-- because host uses current code and sends version in MemberInfo
|
||||
testNoDirect vrMem2 vrMem3 noConns =
|
||||
it
|
||||
( "host "
|
||||
<> vRangeStr (supportedChatVRange PQSupportOff)
|
||||
<> vRangeStr supportedChatVRange
|
||||
<> (", 2nd mem " <> vRangeStr vrMem2)
|
||||
<> (", 3rd mem " <> vRangeStr vrMem3)
|
||||
<> (if noConns then " : 2 <!!> 3" else " : 2 <##> 3")
|
||||
)
|
||||
$ testNoGroupDirectConns (supportedChatVRange PQSupportOff) vrMem2 vrMem3 noConns
|
||||
$ testNoGroupDirectConns supportedChatVRange vrMem2 vrMem3 noConns
|
||||
|
||||
testGroup :: HasCallStack => FilePath -> IO ()
|
||||
testGroup =
|
||||
@@ -357,6 +357,20 @@ testGroupShared alice bob cath checkMessages directConnections = do
|
||||
alice #$> ("/_unread chat #1 on", id, "ok")
|
||||
alice #$> ("/_unread chat #1 off", id, "ok")
|
||||
|
||||
testGroupLargeMessage :: HasCallStack => FilePath -> IO ()
|
||||
testGroupLargeMessage =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
createGroup2 "team" alice bob
|
||||
|
||||
img <- genProfileImg
|
||||
let profileImage = "data:image/png;base64," <> B.unpack img
|
||||
alice `send` ("/_group_profile #1 {\"displayName\": \"team\", \"fullName\": \"\", \"image\": \"" <> profileImage <> "\", \"groupPreferences\": {\"directMessages\": {\"enable\": \"on\"}, \"history\": {\"enable\": \"on\"}}}")
|
||||
_trimmedCmd1 <- getTermLine alice
|
||||
alice <## "profile image updated"
|
||||
bob <## "alice updated group #team:"
|
||||
bob <## "profile image updated"
|
||||
|
||||
testNewGroupIncognito :: HasCallStack => FilePath -> IO ()
|
||||
testNewGroupIncognito =
|
||||
testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $
|
||||
@@ -3594,9 +3608,9 @@ testConfigureGroupDeliveryReceipts tmp =
|
||||
|
||||
testNoGroupDirectConns :: HasCallStack => VersionRangeChat -> VersionRangeChat -> VersionRangeChat -> Bool -> FilePath -> IO ()
|
||||
testNoGroupDirectConns hostVRange mem2VRange mem3VRange noDirectConns tmp =
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const hostVRange} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const mem2VRange} "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = const mem3VRange} "cath" cathProfile $ \cath -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = hostVRange} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = mem2VRange} "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatCfg tmp testCfg {chatVRange = mem3VRange} "cath" cathProfile $ \cath -> do
|
||||
createGroup3 "team" alice bob cath
|
||||
if noDirectConns
|
||||
then contactsDontExist bob cath
|
||||
@@ -3885,6 +3899,20 @@ testMemberContactMessage =
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
|
||||
bob #$> ("/_get chat #1 count=1", chat, [(0, "started direct connection with you")])
|
||||
|
||||
-- exchanging messages will enable PQ (see Chat "TODO PQ" - perhaps connection should be negotiated with PQ on)
|
||||
alice <##> bob
|
||||
alice <##> bob
|
||||
|
||||
alice `send` "@bob hi"
|
||||
alice <## "bob: quantum resistant end-to-end encryption enabled"
|
||||
alice <# "@bob hi"
|
||||
bob <## "alice: quantum resistant end-to-end encryption enabled"
|
||||
bob <# "alice> hi"
|
||||
|
||||
bob #> "@alice hey"
|
||||
alice <# "bob> hey"
|
||||
|
||||
alice <##> bob
|
||||
|
||||
-- bob and cath connect
|
||||
|
||||
@@ -307,6 +307,7 @@ testProfileLink =
|
||||
cc <## ("contact address: " <> cLink)
|
||||
cc <## "you've shared main profile with this contact"
|
||||
cc <## "connection not verified, use /code command to see security code"
|
||||
cc <## "quantum resistant end-to-end encryption"
|
||||
cc <## currentChatVRangeInfo
|
||||
checkAliceNoProfileLink cc = do
|
||||
cc ##> "/info alice"
|
||||
@@ -315,6 +316,7 @@ testProfileLink =
|
||||
cc <##. "sending messages via"
|
||||
cc <## "you've shared main profile with this contact"
|
||||
cc <## "connection not verified, use /code command to see security code"
|
||||
cc <## "quantum resistant end-to-end encryption"
|
||||
cc <## currentChatVRangeInfo
|
||||
|
||||
testUserContactLinkAutoAccept :: HasCallStack => FilePath -> IO ()
|
||||
@@ -1516,7 +1518,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $
|
||||
alice ##> "/_set prefs @2 {}"
|
||||
alice <## "your preferences for bob did not change"
|
||||
(bob </)
|
||||
let startFeatures = [(0, e2eeInfoNoPQStr), (0, "Disappearing messages: allowed"), (0, "Full deletion: off"), (0, "Message reactions: enabled"), (0, "Voice messages: off"), (0, "Audio/video calls: enabled")]
|
||||
let startFeatures = [(0, e2eeInfoPQStr), (0, "Disappearing messages: allowed"), (0, "Full deletion: off"), (0, "Message reactions: enabled"), (0, "Voice messages: off"), (0, "Audio/video calls: enabled")]
|
||||
alice #$> ("/_get chat @2 count=100", chat, startFeatures)
|
||||
bob #$> ("/_get chat @2 count=100", chat, startFeatures)
|
||||
let sendVoice = "/_send @2 json {\"filePath\": \"test.txt\", \"msgContent\": {\"type\": \"voice\", \"text\": \"\", \"duration\": 10}}"
|
||||
|
||||
+11
-38
@@ -87,15 +87,16 @@ ifCI xrun run d t = do
|
||||
skip :: String -> SpecWith a -> SpecWith a
|
||||
skip = before_ . pendingWith
|
||||
|
||||
versionTestMatrix2 :: (HasCallStack => TestCC -> TestCC -> IO ()) -> SpecWith FilePath
|
||||
-- Bool is pqExpected - see testAddContact
|
||||
versionTestMatrix2 :: (HasCallStack => Bool -> TestCC -> TestCC -> IO ()) -> SpecWith FilePath
|
||||
versionTestMatrix2 runTest = do
|
||||
it "current" $ testChat2 aliceProfile bobProfile runTest
|
||||
it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile runTest
|
||||
it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev runTest
|
||||
it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg runTest
|
||||
it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile runTest
|
||||
it "old to curr" $ runTestCfg2 testCfg testCfgV1 runTest
|
||||
it "curr to old" $ runTestCfg2 testCfgV1 testCfg runTest
|
||||
it "current" $ testChat2 aliceProfile bobProfile (runTest True)
|
||||
it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile (runTest False)
|
||||
it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev (runTest False)
|
||||
it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg (runTest False)
|
||||
it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile (runTest False)
|
||||
it "old to curr" $ runTestCfg2 testCfg testCfgV1 (runTest False)
|
||||
it "curr to old" $ runTestCfg2 testCfgV1 testCfg (runTest False)
|
||||
|
||||
versionTestMatrix3 :: (HasCallStack => TestCC -> TestCC -> TestCC -> IO ()) -> SpecWith FilePath
|
||||
versionTestMatrix3 runTest = do
|
||||
@@ -119,34 +120,6 @@ runTestCfg3 aliceCfg bobCfg cathCfg runTest tmp =
|
||||
withNewTestChatCfg tmp cathCfg "cath" cathProfile $ \cath ->
|
||||
runTest alice bob cath
|
||||
|
||||
type PQEnabled = Bool
|
||||
|
||||
pqMatrix2 :: (HasCallStack => (TestCC, PQEnabled) -> (TestCC, PQEnabled) -> IO ()) -> SpecWith FilePath
|
||||
pqMatrix2 runTest = do
|
||||
it "PQ: off, off" $ test False False
|
||||
it "PQ: on, off" $ test False True
|
||||
it "PQ: off, on" $ test True False
|
||||
it "PQ: on, on" $ test True True
|
||||
where
|
||||
test aPQ bPQ = testChat2 aliceProfile bobProfile $ \a b -> runTest (a, aPQ) (b, bPQ)
|
||||
|
||||
pqVersionTestMatrix2 :: (HasCallStack => TestCC -> TestCC -> Bool -> VersionChat -> IO ()) -> SpecWith FilePath
|
||||
pqVersionTestMatrix2 runTest = do
|
||||
it "current" $ testChat2 aliceProfile bobProfile (runTest' True pqEncryptionCompressionVersion)
|
||||
it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile (runTest' False (VersionChat 6))
|
||||
it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev (runTest' False (VersionChat 6))
|
||||
it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg (runTest' False (VersionChat 6))
|
||||
it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile (runTest' False (VersionChat 1))
|
||||
it "old to curr" $ runTestCfg2 testCfg testCfgV1 (runTest' False (VersionChat 1))
|
||||
it "curr to old" $ runTestCfg2 testCfgV1 testCfg (runTest' False (VersionChat 1))
|
||||
it "next" $ testChatCfg2 testCfgVNext aliceProfile bobProfile (runTest' True pqEncryptionCompressionVersion)
|
||||
it "next to curr" $ runTestCfg2 testCfg testCfgVNext (runTest' True pqEncryptionCompressionVersion)
|
||||
it "curr to next" $ runTestCfg2 testCfgVNext testCfg (runTest' True pqEncryptionCompressionVersion)
|
||||
it "next to prev" $ runTestCfg2 testCfgVPrev testCfgVNext (runTest' False (VersionChat 6))
|
||||
it "prev to next" $ runTestCfg2 testCfgVNext testCfgVPrev (runTest' False (VersionChat 6))
|
||||
where
|
||||
runTest' pqExpected v a b = runTest a b pqExpected v
|
||||
|
||||
withTestChatGroup3Connected :: HasCallStack => FilePath -> String -> (HasCallStack => TestCC -> IO a) -> IO a
|
||||
withTestChatGroup3Connected tmp dbPrefix action = do
|
||||
withTestChat tmp dbPrefix $ \cc -> do
|
||||
@@ -287,7 +260,7 @@ chatFeaturesF = map (\(a, _, c) -> (a, c)) chatFeatures''
|
||||
|
||||
chatFeatures'' :: [((Int, String), Maybe (Int, String), Maybe String)]
|
||||
chatFeatures'' =
|
||||
[ ((0, e2eeInfoNoPQStr), Nothing, Nothing),
|
||||
[ ((0, e2eeInfoPQStr), Nothing, Nothing),
|
||||
((0, "Disappearing messages: allowed"), Nothing, Nothing),
|
||||
((0, "Full deletion: off"), Nothing, Nothing),
|
||||
((0, "Message reactions: enabled"), Nothing, Nothing),
|
||||
@@ -708,7 +681,7 @@ checkActionDeletesFile file action = do
|
||||
|
||||
currentChatVRangeInfo :: String
|
||||
currentChatVRangeInfo =
|
||||
"peer chat protocol version range: " <> vRangeStr (supportedChatVRange PQSupportOff)
|
||||
"peer chat protocol version range: " <> vRangeStr supportedChatVRange
|
||||
|
||||
vRangeStr :: VersionRange v -> String
|
||||
vRangeStr (VersionRange minVer maxVer) = "(" <> show minVer <> ", " <> show maxVer <> ")"
|
||||
|
||||
@@ -50,7 +50,7 @@ testDhPubKey :: C.PublicKeyX448
|
||||
testDhPubKey = "MEIwBQYDK2VvAzkAmKuSYeQ/m0SixPDS8Wq8VBaTS1cW+Lp0n0h4Diu+kUpR+qXx4SDJ32YGEFoGFGSbGPry5Ychr6U="
|
||||
|
||||
testE2ERatchetParams :: RcvE2ERatchetParamsUri 'C.X448
|
||||
testE2ERatchetParams = E2ERatchetParamsUri (supportedE2EEncryptVRange PQSupportOn) testDhPubKey testDhPubKey Nothing
|
||||
testE2ERatchetParams = E2ERatchetParamsUri supportedE2EEncryptVRange testDhPubKey testDhPubKey Nothing
|
||||
|
||||
testConnReq :: ConnectionRequestUri 'CMInvitation
|
||||
testConnReq = CRInvitationUri connReqData testE2ERatchetParams
|
||||
@@ -132,8 +132,8 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}"
|
||||
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing)))
|
||||
it "x.msg.new chat message with chat version range" $
|
||||
"{\"v\":\"1-7\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}"
|
||||
##==## ChatMessage (supportedChatVRange PQSupportOff) (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing)))
|
||||
"{\"v\":\"1-8\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}"
|
||||
##==## ChatMessage supportedChatVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing)))
|
||||
it "x.msg.new quote" $
|
||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}}}}"
|
||||
##==## ChatMessage
|
||||
@@ -242,14 +242,14 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile}
|
||||
it "x.grp.mem.new with member chat version range" $
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-7\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange $ supportedChatVRange PQSupportOff, profile = testProfile}
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile}
|
||||
it "x.grp.mem.intro" $
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} Nothing
|
||||
it "x.grp.mem.intro with member chat version range" $
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-7\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange $ supportedChatVRange PQSupportOff, profile = testProfile} Nothing
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} Nothing
|
||||
it "x.grp.mem.intro with member restrictions" $
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberRestrictions\":{\"restriction\":\"blocked\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} (Just MemberRestrictions {restriction = MRSBlocked})
|
||||
@@ -263,8 +263,8 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"directConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq}
|
||||
it "x.grp.mem.fwd with member chat version range and w/t directConnReq" $
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-7\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange $ supportedChatVRange PQSupportOff, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing}
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing}
|
||||
it "x.grp.mem.info" $
|
||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.info\",\"params\":{\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"
|
||||
#==# XGrpMemInfo (MemberId "\1\2\3\4") testProfile
|
||||
|
||||
Reference in New Issue
Block a user