Merge branch 'master' into remote-desktop
This commit is contained in:
commit
92eae012b3
@ -257,6 +257,12 @@ func setXFTPConfig(_ cfg: XFTPFileConfig?) throws {
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
|
||||||
|
let r = chatSendCmdSync(.apiSetEncryptLocalFiles(enable: enable))
|
||||||
|
if case .cmdOk = r { return }
|
||||||
|
throw r
|
||||||
|
}
|
||||||
|
|
||||||
func apiExportArchive(config: ArchiveConfig) async throws {
|
func apiExportArchive(config: ArchiveConfig) async throws {
|
||||||
try await sendCommandOkResp(.apiExportArchive(config: config))
|
try await sendCommandOkResp(.apiExportArchive(config: config))
|
||||||
}
|
}
|
||||||
@ -1184,6 +1190,7 @@ func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool
|
|||||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||||
try setXFTPConfig(getXFTPCfg())
|
try setXFTPConfig(getXFTPCfg())
|
||||||
|
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||||
m.chatInitialized = true
|
m.chatInitialized = true
|
||||||
m.currentUser = try apiGetActiveUser()
|
m.currentUser = try apiGetActiveUser()
|
||||||
if m.currentUser == nil {
|
if m.currentUser == nil {
|
||||||
|
@ -111,14 +111,17 @@ struct ChatPreviewView: View {
|
|||||||
|
|
||||||
private func chatPreviewLayout(_ text: Text, draft: Bool = false) -> some View {
|
private func chatPreviewLayout(_ text: Text, draft: Bool = false) -> some View {
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topTrailing) {
|
||||||
text
|
let t = text
|
||||||
.lineLimit(2)
|
.lineLimit(2)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||||
.padding(.leading, 8)
|
.padding(.leading, 8)
|
||||||
.padding(.trailing, 36)
|
.padding(.trailing, 36)
|
||||||
.privacySensitive(!showChatPreviews && !draft)
|
if !showChatPreviews && !draft {
|
||||||
.redacted(reason: .privacy)
|
t.privacySensitive(true).redacted(reason: .privacy)
|
||||||
|
} else {
|
||||||
|
t
|
||||||
|
}
|
||||||
let s = chat.chatStats
|
let s = chat.chatStats
|
||||||
if s.unreadCount > 0 || s.unreadChat {
|
if s.unreadCount > 0 || s.unreadChat {
|
||||||
unreadCountText(s.unreadCount)
|
unreadCountText(s.unreadCount)
|
||||||
|
@ -66,6 +66,9 @@ struct PrivacySettings: View {
|
|||||||
Section {
|
Section {
|
||||||
settingsRow("lock.doc") {
|
settingsRow("lock.doc") {
|
||||||
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
|
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
|
||||||
|
.onChange(of: encryptLocalFiles) {
|
||||||
|
setEncryptLocalFiles($0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
settingsRow("photo") {
|
settingsRow("photo") {
|
||||||
Toggle("Auto-accept images", isOn: $autoAcceptImages)
|
Toggle("Auto-accept images", isOn: $autoAcceptImages)
|
||||||
@ -183,6 +186,16 @@ struct PrivacySettings: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setEncryptLocalFiles(_ enable: Bool) {
|
||||||
|
do {
|
||||||
|
try apiSetEncryptLocalFiles(enable)
|
||||||
|
} catch let error {
|
||||||
|
let err = responseError(error)
|
||||||
|
logger.error("apiSetEncryptLocalFiles \(err)")
|
||||||
|
alert = .error(title: "Error", error: "\(err)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func setOrAskSendReceiptsContacts(_ enable: Bool) {
|
private func setOrAskSendReceiptsContacts(_ enable: Bool) {
|
||||||
contactReceiptsOverrides = m.chats.reduce(0) { count, chat in
|
contactReceiptsOverrides = m.chats.reduce(0) { count, chat in
|
||||||
let sendRcpts = chat.chatInfo.contact?.chatSettings.sendRcpts
|
let sendRcpts = chat.chatInfo.contact?.chatSettings.sendRcpts
|
||||||
|
@ -216,6 +216,7 @@ func startChat() -> DBMigrationResult? {
|
|||||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||||
try setXFTPConfig(xftpConfig)
|
try setXFTPConfig(xftpConfig)
|
||||||
|
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||||
let justStarted = try apiStartChat()
|
let justStarted = try apiStartChat()
|
||||||
chatStarted = true
|
chatStarted = true
|
||||||
if justStarted {
|
if justStarted {
|
||||||
@ -351,6 +352,12 @@ func setXFTPConfig(_ cfg: XFTPFileConfig?) throws {
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
|
||||||
|
let r = sendSimpleXCmd(.apiSetEncryptLocalFiles(enable: enable))
|
||||||
|
if case .cmdOk = r { return }
|
||||||
|
throw r
|
||||||
|
}
|
||||||
|
|
||||||
func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
||||||
guard apiGetActiveUser() != nil else {
|
guard apiGetActiveUser() != nil else {
|
||||||
logger.debug("no active user")
|
logger.debug("no active user")
|
||||||
|
@ -32,6 +32,7 @@ public enum ChatCommand {
|
|||||||
case setTempFolder(tempFolder: String)
|
case setTempFolder(tempFolder: String)
|
||||||
case setFilesFolder(filesFolder: String)
|
case setFilesFolder(filesFolder: String)
|
||||||
case apiSetXFTPConfig(config: XFTPFileConfig?)
|
case apiSetXFTPConfig(config: XFTPFileConfig?)
|
||||||
|
case apiSetEncryptLocalFiles(enable: Bool)
|
||||||
case apiExportArchive(config: ArchiveConfig)
|
case apiExportArchive(config: ArchiveConfig)
|
||||||
case apiImportArchive(config: ArchiveConfig)
|
case apiImportArchive(config: ArchiveConfig)
|
||||||
case apiDeleteStorage
|
case apiDeleteStorage
|
||||||
@ -114,8 +115,8 @@ public enum ChatCommand {
|
|||||||
case apiGetNetworkStatuses
|
case apiGetNetworkStatuses
|
||||||
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
||||||
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
||||||
case receiveFile(fileId: Int64, encrypted: Bool, inline: Bool?)
|
case receiveFile(fileId: Int64, encrypted: Bool?, inline: Bool?)
|
||||||
case setFileToReceive(fileId: Int64, encrypted: Bool)
|
case setFileToReceive(fileId: Int64, encrypted: Bool?)
|
||||||
case cancelFile(fileId: Int64)
|
case cancelFile(fileId: Int64)
|
||||||
case setLocalDeviceName(displayName: String)
|
case setLocalDeviceName(displayName: String)
|
||||||
case startRemoteCtrl
|
case startRemoteCtrl
|
||||||
@ -160,6 +161,7 @@ public enum ChatCommand {
|
|||||||
} else {
|
} else {
|
||||||
return "/_xftp off"
|
return "/_xftp off"
|
||||||
}
|
}
|
||||||
|
case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))"
|
||||||
case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))"
|
case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))"
|
||||||
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
|
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
|
||||||
case .apiDeleteStorage: return "/_db delete"
|
case .apiDeleteStorage: return "/_db delete"
|
||||||
@ -255,13 +257,8 @@ public enum ChatCommand {
|
|||||||
case .apiGetNetworkStatuses: return "/_network_statuses"
|
case .apiGetNetworkStatuses: return "/_network_statuses"
|
||||||
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
||||||
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
||||||
case let .receiveFile(fileId, encrypted, inline):
|
case let .receiveFile(fileId, encrypt, inline): return "/freceive \(fileId)\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
|
||||||
let s = "/freceive \(fileId) encrypt=\(onOff(encrypted))"
|
case let .setFileToReceive(fileId, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("encrypt", encrypt))"
|
||||||
if let inline = inline {
|
|
||||||
return s + " inline=\(onOff(inline))"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
case let .setFileToReceive(fileId, encrypted): return "/_set_file_to_receive \(fileId) encrypt=\(onOff(encrypted))"
|
|
||||||
case let .cancelFile(fileId): return "/fcancel \(fileId)"
|
case let .cancelFile(fileId): return "/fcancel \(fileId)"
|
||||||
case let .setLocalDeviceName(displayName): return "/set device name \(displayName)"
|
case let .setLocalDeviceName(displayName): return "/set device name \(displayName)"
|
||||||
case .startRemoteCtrl: return "/start remote ctrl"
|
case .startRemoteCtrl: return "/start remote ctrl"
|
||||||
@ -299,6 +296,7 @@ public enum ChatCommand {
|
|||||||
case .setTempFolder: return "setTempFolder"
|
case .setTempFolder: return "setTempFolder"
|
||||||
case .setFilesFolder: return "setFilesFolder"
|
case .setFilesFolder: return "setFilesFolder"
|
||||||
case .apiSetXFTPConfig: return "apiSetXFTPConfig"
|
case .apiSetXFTPConfig: return "apiSetXFTPConfig"
|
||||||
|
case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles"
|
||||||
case .apiExportArchive: return "apiExportArchive"
|
case .apiExportArchive: return "apiExportArchive"
|
||||||
case .apiImportArchive: return "apiImportArchive"
|
case .apiImportArchive: return "apiImportArchive"
|
||||||
case .apiDeleteStorage: return "apiDeleteStorage"
|
case .apiDeleteStorage: return "apiDeleteStorage"
|
||||||
@ -444,6 +442,13 @@ public enum ChatCommand {
|
|||||||
b ? "on" : "off"
|
b ? "on" : "off"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func onOffParam(_ param: String, _ b: Bool?) -> String {
|
||||||
|
if let b = b {
|
||||||
|
return " \(param)=\(onOff(b))"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
private func maybePwd(_ pwd: String?) -> String {
|
private func maybePwd(_ pwd: String?) -> String {
|
||||||
pwd == "" || pwd == nil ? "" : " " + encodeJSON(pwd)
|
pwd == "" || pwd == nil ? "" : " " + encodeJSON(pwd)
|
||||||
}
|
}
|
||||||
|
@ -340,6 +340,7 @@ object ChatController {
|
|||||||
apiSetTempFolder(coreTmpDir.absolutePath)
|
apiSetTempFolder(coreTmpDir.absolutePath)
|
||||||
apiSetFilesFolder(appFilesDir.absolutePath)
|
apiSetFilesFolder(appFilesDir.absolutePath)
|
||||||
apiSetXFTPConfig(getXFTPCfg())
|
apiSetXFTPConfig(getXFTPCfg())
|
||||||
|
apiSetEncryptLocalFiles(appPrefs.privacyEncryptLocalFiles.get())
|
||||||
val justStarted = apiStartChat()
|
val justStarted = apiStartChat()
|
||||||
val users = listUsers()
|
val users = listUsers()
|
||||||
chatModel.users.clear()
|
chatModel.users.clear()
|
||||||
@ -567,6 +568,8 @@ object ChatController {
|
|||||||
throw Error("apiSetXFTPConfig bad response: ${r.responseType} ${r.details}")
|
throw Error("apiSetXFTPConfig bad response: ${r.responseType} ${r.details}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun apiSetEncryptLocalFiles(enable: Boolean) = sendCommandOkResp(CC.ApiSetEncryptLocalFiles(enable))
|
||||||
|
|
||||||
suspend fun apiExportArchive(config: ArchiveConfig) {
|
suspend fun apiExportArchive(config: ArchiveConfig) {
|
||||||
val r = sendCmd(CC.ApiExportArchive(config))
|
val r = sendCmd(CC.ApiExportArchive(config))
|
||||||
if (r is CR.CmdOk) return
|
if (r is CR.CmdOk) return
|
||||||
@ -1384,7 +1387,7 @@ object ChatController {
|
|||||||
private suspend fun sendCommandOkResp(cmd: CC): Boolean {
|
private suspend fun sendCommandOkResp(cmd: CC): Boolean {
|
||||||
val r = sendCmd(cmd)
|
val r = sendCmd(cmd)
|
||||||
val ok = r is CR.CmdOk
|
val ok = r is CR.CmdOk
|
||||||
if (!ok) apiErrorAlert(cmd.cmdType, generalGetString(MR.strings.error), r)
|
if (!ok) apiErrorAlert(cmd.cmdType, generalGetString(MR.strings.error_alert_title), r)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1927,6 +1930,7 @@ sealed class CC {
|
|||||||
class SetTempFolder(val tempFolder: String): CC()
|
class SetTempFolder(val tempFolder: String): CC()
|
||||||
class SetFilesFolder(val filesFolder: String): CC()
|
class SetFilesFolder(val filesFolder: String): CC()
|
||||||
class ApiSetXFTPConfig(val config: XFTPFileConfig?): CC()
|
class ApiSetXFTPConfig(val config: XFTPFileConfig?): CC()
|
||||||
|
class ApiSetEncryptLocalFiles(val enable: Boolean): CC()
|
||||||
class ApiExportArchive(val config: ArchiveConfig): CC()
|
class ApiExportArchive(val config: ArchiveConfig): CC()
|
||||||
class ApiImportArchive(val config: ArchiveConfig): CC()
|
class ApiImportArchive(val config: ArchiveConfig): CC()
|
||||||
class ApiDeleteStorage: CC()
|
class ApiDeleteStorage: CC()
|
||||||
@ -2000,7 +2004,7 @@ sealed class CC {
|
|||||||
class ApiRejectContact(val contactReqId: Long): CC()
|
class ApiRejectContact(val contactReqId: Long): CC()
|
||||||
class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC()
|
class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC()
|
||||||
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
|
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
|
||||||
class ReceiveFile(val fileId: Long, val encrypted: Boolean, val inline: Boolean?): CC()
|
class ReceiveFile(val fileId: Long, val encrypt: Boolean, val inline: Boolean?): CC()
|
||||||
class CancelFile(val fileId: Long): CC()
|
class CancelFile(val fileId: Long): CC()
|
||||||
class SetLocalDeviceName(val displayName: String): CC()
|
class SetLocalDeviceName(val displayName: String): CC()
|
||||||
class CreateRemoteHost(): CC()
|
class CreateRemoteHost(): CC()
|
||||||
@ -2045,6 +2049,7 @@ sealed class CC {
|
|||||||
is SetTempFolder -> "/_temp_folder $tempFolder"
|
is SetTempFolder -> "/_temp_folder $tempFolder"
|
||||||
is SetFilesFolder -> "/_files_folder $filesFolder"
|
is SetFilesFolder -> "/_files_folder $filesFolder"
|
||||||
is ApiSetXFTPConfig -> if (config != null) "/_xftp on ${json.encodeToString(config)}" else "/_xftp off"
|
is ApiSetXFTPConfig -> if (config != null) "/_xftp on ${json.encodeToString(config)}" else "/_xftp off"
|
||||||
|
is ApiSetEncryptLocalFiles -> "/_files_encrypt ${onOff(enable)}"
|
||||||
is ApiExportArchive -> "/_db export ${json.encodeToString(config)}"
|
is ApiExportArchive -> "/_db export ${json.encodeToString(config)}"
|
||||||
is ApiImportArchive -> "/_db import ${json.encodeToString(config)}"
|
is ApiImportArchive -> "/_db import ${json.encodeToString(config)}"
|
||||||
is ApiDeleteStorage -> "/_db delete"
|
is ApiDeleteStorage -> "/_db delete"
|
||||||
@ -2121,7 +2126,10 @@ sealed class CC {
|
|||||||
is ApiGetNetworkStatuses -> "/_network_statuses"
|
is ApiGetNetworkStatuses -> "/_network_statuses"
|
||||||
is ApiChatRead -> "/_read chat ${chatRef(type, id)} from=${range.from} to=${range.to}"
|
is ApiChatRead -> "/_read chat ${chatRef(type, id)} from=${range.from} to=${range.to}"
|
||||||
is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}"
|
is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}"
|
||||||
is ReceiveFile -> "/freceive $fileId encrypt=${onOff(encrypted)}" + (if (inline == null) "" else " inline=${onOff(inline)}")
|
is ReceiveFile ->
|
||||||
|
"/freceive $fileId" +
|
||||||
|
(if (encrypt == null) "" else " encrypt=${onOff(encrypt)}") +
|
||||||
|
(if (inline == null) "" else " inline=${onOff(inline)}")
|
||||||
is CancelFile -> "/fcancel $fileId"
|
is CancelFile -> "/fcancel $fileId"
|
||||||
is SetLocalDeviceName -> "/set device name $displayName"
|
is SetLocalDeviceName -> "/set device name $displayName"
|
||||||
is CreateRemoteHost -> "/create remote host"
|
is CreateRemoteHost -> "/create remote host"
|
||||||
@ -2158,6 +2166,7 @@ sealed class CC {
|
|||||||
is SetTempFolder -> "setTempFolder"
|
is SetTempFolder -> "setTempFolder"
|
||||||
is SetFilesFolder -> "setFilesFolder"
|
is SetFilesFolder -> "setFilesFolder"
|
||||||
is ApiSetXFTPConfig -> "apiSetXFTPConfig"
|
is ApiSetXFTPConfig -> "apiSetXFTPConfig"
|
||||||
|
is ApiSetEncryptLocalFiles -> "apiSetEncryptLocalFiles"
|
||||||
is ApiExportArchive -> "apiExportArchive"
|
is ApiExportArchive -> "apiExportArchive"
|
||||||
is ApiImportArchive -> "apiImportArchive"
|
is ApiImportArchive -> "apiImportArchive"
|
||||||
is ApiDeleteStorage -> "apiDeleteStorage"
|
is ApiDeleteStorage -> "apiDeleteStorage"
|
||||||
|
@ -64,7 +64,9 @@ fun PrivacySettingsView(
|
|||||||
SectionDividerSpaced()
|
SectionDividerSpaced()
|
||||||
|
|
||||||
SectionView(stringResource(MR.strings.settings_section_title_chats)) {
|
SectionView(stringResource(MR.strings.settings_section_title_chats)) {
|
||||||
SettingsPreferenceItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.encrypt_local_files), chatModel.controller.appPrefs.privacyEncryptLocalFiles)
|
SettingsPreferenceItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.encrypt_local_files), chatModel.controller.appPrefs.privacyEncryptLocalFiles, onChange = { enable ->
|
||||||
|
withBGApi { chatModel.controller.apiSetEncryptLocalFiles(enable) }
|
||||||
|
})
|
||||||
SettingsPreferenceItem(painterResource(MR.images.ic_image), stringResource(MR.strings.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
|
SettingsPreferenceItem(painterResource(MR.images.ic_image), stringResource(MR.strings.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
|
||||||
SettingsPreferenceItem(painterResource(MR.images.ic_travel_explore), stringResource(MR.strings.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
|
SettingsPreferenceItem(painterResource(MR.images.ic_travel_explore), stringResource(MR.strings.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
|
||||||
SettingsPreferenceItem(
|
SettingsPreferenceItem(
|
||||||
|
@ -113,6 +113,7 @@
|
|||||||
<string name="error_xftp_test_server_auth">Server requires authorization to upload, check password</string>
|
<string name="error_xftp_test_server_auth">Server requires authorization to upload, check password</string>
|
||||||
<string name="error_smp_test_certificate">Possibly, certificate fingerprint in server address is incorrect</string>
|
<string name="error_smp_test_certificate">Possibly, certificate fingerprint in server address is incorrect</string>
|
||||||
<string name="error_setting_address">Error setting address</string>
|
<string name="error_setting_address">Error setting address</string>
|
||||||
|
<string name="error_alert_title">Error</string>
|
||||||
<string name="smp_server_test_connect">Connect</string>
|
<string name="smp_server_test_connect">Connect</string>
|
||||||
<string name="smp_server_test_disconnect">Disconnect</string>
|
<string name="smp_server_test_disconnect">Disconnect</string>
|
||||||
<string name="smp_server_test_create_queue">Create queue</string>
|
<string name="smp_server_test_create_queue">Create queue</string>
|
||||||
|
@ -9,7 +9,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
|||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/simplex-chat/simplexmq.git
|
location: https://github.com/simplex-chat/simplexmq.git
|
||||||
tag: 6b0da8ac50b1582c9f5187c316b93fc8f12c9365
|
tag: 1ad69cf74f18f25713ce564e1629d2538313b9e0
|
||||||
|
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"https://github.com/simplex-chat/simplexmq.git"."6b0da8ac50b1582c9f5187c316b93fc8f12c9365" = "18n0b1l1adraw5rq118a6iz9pqg43yf41vrzm193q1si06iwk24b";
|
"https://github.com/simplex-chat/simplexmq.git"."1ad69cf74f18f25713ce564e1629d2538313b9e0" = "1kil0962pn3ksnxh7dcwcbnkidz95yl31rm4m585ps7wnh6fp0l9";
|
||||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||||
"https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb";
|
"https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb";
|
||||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "0kiwhvml42g9anw4d2v0zd1fpc790pj9syg5x3ik4l97fnkbbwpp";
|
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "0kiwhvml42g9anw4d2v0zd1fpc790pj9syg5x3ik4l97fnkbbwpp";
|
||||||
|
@ -216,6 +216,7 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen
|
|||||||
cleanupManagerAsync <- newTVarIO Nothing
|
cleanupManagerAsync <- newTVarIO Nothing
|
||||||
timedItemThreads <- atomically TM.empty
|
timedItemThreads <- atomically TM.empty
|
||||||
showLiveItems <- newTVarIO False
|
showLiveItems <- newTVarIO False
|
||||||
|
encryptLocalFiles <- newTVarIO False
|
||||||
userXFTPFileConfig <- newTVarIO $ xftpFileConfig cfg
|
userXFTPFileConfig <- newTVarIO $ xftpFileConfig cfg
|
||||||
tempDirectory <- newTVarIO tempDir
|
tempDirectory <- newTVarIO tempDir
|
||||||
contactMergeEnabled <- newTVarIO True
|
contactMergeEnabled <- newTVarIO True
|
||||||
@ -248,6 +249,7 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen
|
|||||||
cleanupManagerAsync,
|
cleanupManagerAsync,
|
||||||
timedItemThreads,
|
timedItemThreads,
|
||||||
showLiveItems,
|
showLiveItems,
|
||||||
|
encryptLocalFiles,
|
||||||
userXFTPFileConfig,
|
userXFTPFileConfig,
|
||||||
tempDirectory,
|
tempDirectory,
|
||||||
logFilePath = logFile,
|
logFilePath = logFile,
|
||||||
@ -535,6 +537,7 @@ processChatCommand = \case
|
|||||||
APISetXFTPConfig cfg -> do
|
APISetXFTPConfig cfg -> do
|
||||||
asks userXFTPFileConfig >>= atomically . (`writeTVar` cfg)
|
asks userXFTPFileConfig >>= atomically . (`writeTVar` cfg)
|
||||||
ok_
|
ok_
|
||||||
|
APISetEncryptLocalFiles on -> chatWriteVar encryptLocalFiles on >> ok_
|
||||||
SetContactMergeEnabled onOff -> do
|
SetContactMergeEnabled onOff -> do
|
||||||
asks contactMergeEnabled >>= atomically . (`writeTVar` onOff)
|
asks contactMergeEnabled >>= atomically . (`writeTVar` onOff)
|
||||||
ok_
|
ok_
|
||||||
@ -1381,13 +1384,13 @@ processChatCommand = \case
|
|||||||
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
||||||
Connect incognito aCReqUri@(Just cReqUri) -> withUser $ \user@User {userId} -> do
|
Connect incognito aCReqUri@(Just cReqUri) -> withUser $ \user@User {userId} -> do
|
||||||
plan <- connectPlan user cReqUri `catchChatError` const (pure $ CPInvitationLink ILPOk)
|
plan <- connectPlan user cReqUri `catchChatError` const (pure $ CPInvitationLink ILPOk)
|
||||||
unless (connectionPlanOk plan) $ throwChatError (CEConnectionPlan plan)
|
unless (connectionPlanProceed plan) $ throwChatError (CEConnectionPlan plan)
|
||||||
processChatCommand $ APIConnect userId incognito aCReqUri
|
processChatCommand $ APIConnect userId incognito aCReqUri
|
||||||
Connect _ Nothing -> throwChatError CEInvalidConnReq
|
Connect _ Nothing -> throwChatError CEInvalidConnReq
|
||||||
ConnectSimplex incognito -> withUser $ \user@User {userId} -> do
|
ConnectSimplex incognito -> withUser $ \user@User {userId} -> do
|
||||||
let cReqUri = ACR SCMContact adminContactReq
|
let cReqUri = ACR SCMContact adminContactReq
|
||||||
plan <- connectPlan user cReqUri `catchChatError` const (pure $ CPInvitationLink ILPOk)
|
plan <- connectPlan user cReqUri `catchChatError` const (pure $ CPInvitationLink ILPOk)
|
||||||
unless (connectionPlanOk plan) $ throwChatError (CEConnectionPlan plan)
|
unless (connectionPlanProceed plan) $ throwChatError (CEConnectionPlan plan)
|
||||||
processChatCommand $ APIConnect userId incognito (Just cReqUri)
|
processChatCommand $ APIConnect userId incognito (Just cReqUri)
|
||||||
DeleteContact cName -> withContactName cName $ \ctId -> APIDeleteChat (ChatRef CTDirect ctId) True
|
DeleteContact cName -> withContactName cName $ \ctId -> APIDeleteChat (ChatRef CTDirect ctId) True
|
||||||
ClearContact cName -> withContactName cName $ APIClearChat . ChatRef CTDirect
|
ClearContact cName -> withContactName cName $ APIClearChat . ChatRef CTDirect
|
||||||
@ -1793,19 +1796,16 @@ processChatCommand = \case
|
|||||||
ForwardFile chatName fileId -> forwardFile chatName fileId SendFile
|
ForwardFile chatName fileId -> forwardFile chatName fileId SendFile
|
||||||
ForwardImage chatName fileId -> forwardFile chatName fileId SendImage
|
ForwardImage chatName fileId -> forwardFile chatName fileId SendImage
|
||||||
SendFileDescription _chatName _f -> pure $ chatCmdError Nothing "TODO"
|
SendFileDescription _chatName _f -> pure $ chatCmdError Nothing "TODO"
|
||||||
ReceiveFile fileId encrypted rcvInline_ filePath_ -> withUser $ \_ ->
|
ReceiveFile fileId encrypted_ rcvInline_ filePath_ -> withUser $ \_ ->
|
||||||
withChatLock "receiveFile" . procCmd $ do
|
withChatLock "receiveFile" . procCmd $ do
|
||||||
(user, ft) <- withStore (`getRcvFileTransferById` fileId)
|
(user, ft) <- withStore (`getRcvFileTransferById` fileId)
|
||||||
ft' <- if encrypted then encryptLocalFile ft else pure ft
|
encrypt <- (`fromMaybe` encrypted_) <$> chatReadVar encryptLocalFiles
|
||||||
|
ft' <- (if encrypt then setFileToEncrypt else pure) ft
|
||||||
receiveFile' user ft' rcvInline_ filePath_
|
receiveFile' user ft' rcvInline_ filePath_
|
||||||
where
|
SetFileToReceive fileId encrypted_ -> withUser $ \_ -> do
|
||||||
encryptLocalFile ft = do
|
|
||||||
cfArgs <- liftIO $ CF.randomArgs
|
|
||||||
withStore' $ \db -> setFileCryptoArgs db fileId cfArgs
|
|
||||||
pure (ft :: RcvFileTransfer) {cryptoArgs = Just cfArgs}
|
|
||||||
SetFileToReceive fileId encrypted -> withUser $ \_ -> do
|
|
||||||
withChatLock "setFileToReceive" . procCmd $ do
|
withChatLock "setFileToReceive" . procCmd $ do
|
||||||
cfArgs <- if encrypted then Just <$> liftIO CF.randomArgs else pure Nothing
|
encrypt <- (`fromMaybe` encrypted_) <$> chatReadVar encryptLocalFiles
|
||||||
|
cfArgs <- if encrypt then Just <$> liftIO CF.randomArgs else pure Nothing
|
||||||
withStore' $ \db -> setRcvFileToReceive db fileId cfArgs
|
withStore' $ \db -> setRcvFileToReceive db fileId cfArgs
|
||||||
ok_
|
ok_
|
||||||
CancelFile fileId -> withUser $ \user@User {userId} ->
|
CancelFile fileId -> withUser $ \user@User {userId} ->
|
||||||
@ -2255,7 +2255,7 @@ processChatCommand = \case
|
|||||||
processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ updateSettings chatSettings
|
processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ updateSettings chatSettings
|
||||||
connectPlan :: User -> AConnectionRequestUri -> m ConnectionPlan
|
connectPlan :: User -> AConnectionRequestUri -> m ConnectionPlan
|
||||||
connectPlan user (ACR SCMInvitation cReq) = do
|
connectPlan user (ACR SCMInvitation cReq) = do
|
||||||
withStore' (\db -> getConnectionEntityByConnReq db user cReq) >>= \case
|
withStore' (\db -> getConnectionEntityByConnReq db user cReqSchemas) >>= \case
|
||||||
Nothing -> pure $ CPInvitationLink ILPOk
|
Nothing -> pure $ CPInvitationLink ILPOk
|
||||||
Just (RcvDirectMsgConnection conn ct_) -> do
|
Just (RcvDirectMsgConnection conn ct_) -> do
|
||||||
let Connection {connStatus, contactConnInitiated} = conn
|
let Connection {connStatus, contactConnInitiated} = conn
|
||||||
@ -2268,39 +2268,59 @@ processChatCommand = \case
|
|||||||
Just ct -> pure $ CPInvitationLink (ILPKnown ct)
|
Just ct -> pure $ CPInvitationLink (ILPKnown ct)
|
||||||
Nothing -> throwChatError $ CEInternalError "ready RcvDirectMsgConnection connection should have associated contact"
|
Nothing -> throwChatError $ CEInternalError "ready RcvDirectMsgConnection connection should have associated contact"
|
||||||
Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
||||||
|
where
|
||||||
|
cReqSchemas :: (ConnReqInvitation, ConnReqInvitation)
|
||||||
|
cReqSchemas = case cReq of
|
||||||
|
(CRInvitationUri crData e2e) ->
|
||||||
|
( CRInvitationUri crData {crScheme = CRSSimplex} e2e,
|
||||||
|
CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||||
|
)
|
||||||
connectPlan user (ACR SCMContact cReq) = do
|
connectPlan user (ACR SCMContact cReq) = do
|
||||||
let CRContactUri ConnReqUriData {crClientData} = cReq
|
let CRContactUri ConnReqUriData {crClientData} = cReq
|
||||||
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||||
case groupLinkId of
|
case groupLinkId of
|
||||||
-- contact address
|
-- contact address
|
||||||
Nothing ->
|
Nothing ->
|
||||||
withStore' (`getUserContactLinkByConnReq` cReq) >>= \case
|
withStore' (`getUserContactLinkByConnReq` cReqSchemas) >>= \case
|
||||||
Just _ -> pure $ CPContactAddress CAPOwnLink
|
Just _ -> pure $ CPContactAddress CAPOwnLink
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
|
withStore' (\db -> getContactConnEntityByConnReqHash db user cReqHashes) >>= \case
|
||||||
withStore' (\db -> getContactByConnReqHash db user cReqHash) >>= \case
|
|
||||||
Nothing -> pure $ CPContactAddress CAPOk
|
Nothing -> pure $ CPContactAddress CAPOk
|
||||||
Just ct
|
Just (RcvDirectMsgConnection _conn Nothing) -> pure $ CPContactAddress CAPConnectingConfirmReconnect
|
||||||
| not (contactReady ct) && contactActive ct -> pure $ CPContactAddress (CAPConnecting ct)
|
Just (RcvDirectMsgConnection _ (Just ct))
|
||||||
|
| not (contactReady ct) && contactActive ct -> pure $ CPContactAddress (CAPConnectingProhibit ct)
|
||||||
|
| contactDeleted ct -> pure $ CPContactAddress CAPOk
|
||||||
| otherwise -> pure $ CPContactAddress (CAPKnown ct)
|
| otherwise -> pure $ CPContactAddress (CAPKnown ct)
|
||||||
|
Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
||||||
-- group link
|
-- group link
|
||||||
Just _ ->
|
Just _ ->
|
||||||
withStore' (\db -> getGroupInfoByUserContactLinkConnReq db user cReq) >>= \case
|
withStore' (\db -> getGroupInfoByUserContactLinkConnReq db user cReqSchemas) >>= \case
|
||||||
Just g -> pure $ CPGroupLink (GLPOwnLink g)
|
Just g -> pure $ CPGroupLink (GLPOwnLink g)
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
|
connEnt_ <- withStore' $ \db -> getContactConnEntityByConnReqHash db user cReqHashes
|
||||||
ct_ <- withStore' $ \db -> getContactByConnReqHash db user cReqHash
|
gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db user cReqHashes
|
||||||
gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db user cReqHash
|
case (gInfo_, connEnt_) of
|
||||||
case (gInfo_, ct_) of
|
|
||||||
(Nothing, Nothing) -> pure $ CPGroupLink GLPOk
|
(Nothing, Nothing) -> pure $ CPGroupLink GLPOk
|
||||||
(Nothing, Just ct)
|
(Nothing, Just (RcvDirectMsgConnection _conn Nothing)) -> pure $ CPGroupLink GLPConnectingConfirmReconnect
|
||||||
| not (contactReady ct) && contactActive ct -> pure $ CPGroupLink (GLPConnecting gInfo_)
|
(Nothing, Just (RcvDirectMsgConnection _ (Just ct)))
|
||||||
|
| not (contactReady ct) && contactActive ct -> pure $ CPGroupLink (GLPConnectingProhibit gInfo_)
|
||||||
| otherwise -> pure $ CPGroupLink GLPOk
|
| otherwise -> pure $ CPGroupLink GLPOk
|
||||||
|
(Nothing, Just _) -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
||||||
(Just gInfo@GroupInfo {membership}, _)
|
(Just gInfo@GroupInfo {membership}, _)
|
||||||
| not (memberActive membership) && not (memberRemoved membership) ->
|
| not (memberActive membership) && not (memberRemoved membership) ->
|
||||||
pure $ CPGroupLink (GLPConnecting gInfo_)
|
pure $ CPGroupLink (GLPConnectingProhibit gInfo_)
|
||||||
| memberActive membership -> pure $ CPGroupLink (GLPKnown gInfo)
|
| memberActive membership -> pure $ CPGroupLink (GLPKnown gInfo)
|
||||||
| otherwise -> pure $ CPGroupLink GLPOk
|
| otherwise -> pure $ CPGroupLink GLPOk
|
||||||
|
where
|
||||||
|
cReqSchemas :: (ConnReqContact, ConnReqContact)
|
||||||
|
cReqSchemas = case cReq of
|
||||||
|
(CRContactUri crData) ->
|
||||||
|
( CRContactUri crData {crScheme = CRSSimplex},
|
||||||
|
CRContactUri crData {crScheme = simplexChat}
|
||||||
|
)
|
||||||
|
cReqHashes :: (ConnReqUriHash, ConnReqUriHash)
|
||||||
|
cReqHashes = bimap hash hash cReqSchemas
|
||||||
|
hash = ConnReqUriHash . C.sha256Hash . strEncode
|
||||||
|
|
||||||
assertDirectAllowed :: ChatMonad m => User -> MsgDirection -> Contact -> CMEventTag e -> m ()
|
assertDirectAllowed :: ChatMonad m => User -> MsgDirection -> Contact -> CMEventTag e -> m ()
|
||||||
assertDirectAllowed user dir ct event =
|
assertDirectAllowed user dir ct event =
|
||||||
@ -2443,6 +2463,12 @@ toFSFilePath :: ChatMonad' m => FilePath -> m FilePath
|
|||||||
toFSFilePath f =
|
toFSFilePath f =
|
||||||
maybe f (</> f) <$> (readTVarIO =<< asks filesFolder)
|
maybe f (</> f) <$> (readTVarIO =<< asks filesFolder)
|
||||||
|
|
||||||
|
setFileToEncrypt :: ChatMonad m => RcvFileTransfer -> m RcvFileTransfer
|
||||||
|
setFileToEncrypt ft@RcvFileTransfer {fileId} = do
|
||||||
|
cfArgs <- liftIO CF.randomArgs
|
||||||
|
withStore' $ \db -> setFileCryptoArgs db fileId cfArgs
|
||||||
|
pure (ft :: RcvFileTransfer) {cryptoArgs = Just cfArgs}
|
||||||
|
|
||||||
receiveFile' :: ChatMonad m => User -> RcvFileTransfer -> Maybe Bool -> Maybe FilePath -> m ChatResponse
|
receiveFile' :: ChatMonad m => User -> RcvFileTransfer -> Maybe Bool -> Maybe FilePath -> m ChatResponse
|
||||||
receiveFile' user ft rcvInline_ filePath_ = do
|
receiveFile' user ft rcvInline_ filePath_ = do
|
||||||
(CRRcvFileAccepted user <$> acceptFileReceive user ft rcvInline_ filePath_) `catchChatError` processError
|
(CRRcvFileAccepted user <$> acceptFileReceive user ft rcvInline_ filePath_) `catchChatError` processError
|
||||||
@ -3964,14 +3990,17 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
|
|||||||
inline <- receiveInlineMode fInv (Just mc) fileChunkSize
|
inline <- receiveInlineMode fInv (Just mc) fileChunkSize
|
||||||
ft@RcvFileTransfer {fileId, xftpRcvFile} <- withStore $ \db -> createRcvFT db fInv inline fileChunkSize
|
ft@RcvFileTransfer {fileId, xftpRcvFile} <- withStore $ \db -> createRcvFT db fInv inline fileChunkSize
|
||||||
let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP
|
let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP
|
||||||
(filePath, fileStatus) <- case inline of
|
(filePath, fileStatus, ft') <- case inline of
|
||||||
Just IFMSent -> do
|
Just IFMSent -> do
|
||||||
|
encrypt <- chatReadVar encryptLocalFiles
|
||||||
|
ft' <- (if encrypt then setFileToEncrypt else pure) ft
|
||||||
fPath <- getRcvFilePath fileId Nothing fileName True
|
fPath <- getRcvFilePath fileId Nothing fileName True
|
||||||
withStore' $ \db -> startRcvInlineFT db user ft fPath inline
|
withStore' $ \db -> startRcvInlineFT db user ft' fPath inline
|
||||||
pure (Just fPath, CIFSRcvAccepted)
|
pure (Just fPath, CIFSRcvAccepted, ft')
|
||||||
_ -> pure (Nothing, CIFSRcvInvitation)
|
_ -> pure (Nothing, CIFSRcvInvitation, ft)
|
||||||
let fileSource = CF.plain <$> filePath
|
let RcvFileTransfer {cryptoArgs} = ft'
|
||||||
pure (ft, CIFile {fileId, fileName, fileSize, fileSource, fileStatus, fileProtocol})
|
fileSource = (`CryptoFile` cryptoArgs) <$> filePath
|
||||||
|
pure (ft', CIFile {fileId, fileName, fileSize, fileSource, fileStatus, fileProtocol})
|
||||||
|
|
||||||
messageUpdate :: Contact -> SharedMsgId -> MsgContent -> RcvMessage -> MsgMeta -> Maybe Int -> Maybe Bool -> m ()
|
messageUpdate :: Contact -> SharedMsgId -> MsgContent -> RcvMessage -> MsgMeta -> Maybe Int -> Maybe Bool -> m ()
|
||||||
messageUpdate ct@Contact {contactId} sharedMsgId mc msg@RcvMessage {msgId} msgMeta ttl live_ = do
|
messageUpdate ct@Contact {contactId} sharedMsgId mc msg@RcvMessage {msgId} msgMeta ttl live_ = do
|
||||||
@ -5597,6 +5626,7 @@ chatCommandP =
|
|||||||
("/_files_folder " <|> "/files_folder ") *> (SetFilesFolder <$> filePath),
|
("/_files_folder " <|> "/files_folder ") *> (SetFilesFolder <$> filePath),
|
||||||
"/_xftp " *> (APISetXFTPConfig <$> ("on " *> (Just <$> jsonP) <|> ("off" $> Nothing))),
|
"/_xftp " *> (APISetXFTPConfig <$> ("on " *> (Just <$> jsonP) <|> ("off" $> Nothing))),
|
||||||
"/xftp " *> (APISetXFTPConfig <$> ("on" *> (Just <$> xftpCfgP) <|> ("off" $> Nothing))),
|
"/xftp " *> (APISetXFTPConfig <$> ("on" *> (Just <$> xftpCfgP) <|> ("off" $> Nothing))),
|
||||||
|
"/_files_encrypt " *> (APISetEncryptLocalFiles <$> onOffP),
|
||||||
"/contact_merge " *> (SetContactMergeEnabled <$> onOffP),
|
"/contact_merge " *> (SetContactMergeEnabled <$> onOffP),
|
||||||
"/_db export " *> (APIExportArchive <$> jsonP),
|
"/_db export " *> (APIExportArchive <$> jsonP),
|
||||||
"/db export" $> ExportArchive,
|
"/db export" $> ExportArchive,
|
||||||
@ -5773,8 +5803,8 @@ chatCommandP =
|
|||||||
("/fforward " <|> "/ff ") *> (ForwardFile <$> chatNameP' <* A.space <*> A.decimal),
|
("/fforward " <|> "/ff ") *> (ForwardFile <$> chatNameP' <* A.space <*> A.decimal),
|
||||||
("/image_forward " <|> "/imgf ") *> (ForwardImage <$> chatNameP' <* A.space <*> A.decimal),
|
("/image_forward " <|> "/imgf ") *> (ForwardImage <$> chatNameP' <* A.space <*> A.decimal),
|
||||||
("/fdescription " <|> "/fd") *> (SendFileDescription <$> chatNameP' <* A.space <*> filePath),
|
("/fdescription " <|> "/fd") *> (SendFileDescription <$> chatNameP' <* A.space <*> filePath),
|
||||||
("/freceive " <|> "/fr ") *> (ReceiveFile <$> A.decimal <*> (" encrypt=" *> onOffP <|> pure False) <*> optional (" inline=" *> onOffP) <*> optional (A.space *> filePath)),
|
("/freceive " <|> "/fr ") *> (ReceiveFile <$> A.decimal <*> optional (" encrypt=" *> onOffP) <*> optional (" inline=" *> onOffP) <*> optional (A.space *> filePath)),
|
||||||
"/_set_file_to_receive " *> (SetFileToReceive <$> A.decimal <*> (" encrypt=" *> onOffP <|> pure False)),
|
"/_set_file_to_receive " *> (SetFileToReceive <$> A.decimal <*> optional (" encrypt=" *> onOffP)),
|
||||||
("/fcancel " <|> "/fc ") *> (CancelFile <$> A.decimal),
|
("/fcancel " <|> "/fc ") *> (CancelFile <$> A.decimal),
|
||||||
("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal),
|
("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal),
|
||||||
"/simplex" *> (ConnectSimplex <$> incognitoP),
|
"/simplex" *> (ConnectSimplex <$> incognitoP),
|
||||||
@ -5950,7 +5980,7 @@ chatCommandP =
|
|||||||
|
|
||||||
adminContactReq :: ConnReqContact
|
adminContactReq :: ConnReqContact
|
||||||
adminContactReq =
|
adminContactReq =
|
||||||
either error id $ strDecode "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D"
|
either error id $ strDecode "simplex:/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D"
|
||||||
|
|
||||||
timeItToView :: ChatMonad' m => String -> m a -> m a
|
timeItToView :: ChatMonad' m => String -> m a -> m a
|
||||||
timeItToView s action = do
|
timeItToView s action = do
|
||||||
|
@ -189,6 +189,7 @@ data ChatController = ChatController
|
|||||||
cleanupManagerAsync :: TVar (Maybe (Async ())),
|
cleanupManagerAsync :: TVar (Maybe (Async ())),
|
||||||
timedItemThreads :: TMap (ChatRef, ChatItemId) (TVar (Maybe (Weak ThreadId))),
|
timedItemThreads :: TMap (ChatRef, ChatItemId) (TVar (Maybe (Weak ThreadId))),
|
||||||
showLiveItems :: TVar Bool,
|
showLiveItems :: TVar Bool,
|
||||||
|
encryptLocalFiles :: TVar Bool,
|
||||||
userXFTPFileConfig :: TVar (Maybe XFTPFileConfig),
|
userXFTPFileConfig :: TVar (Maybe XFTPFileConfig),
|
||||||
tempDirectory :: TVar (Maybe FilePath),
|
tempDirectory :: TVar (Maybe FilePath),
|
||||||
logFilePath :: Maybe FilePath,
|
logFilePath :: Maybe FilePath,
|
||||||
@ -234,6 +235,7 @@ data ChatCommand
|
|||||||
| SetTempFolder FilePath
|
| SetTempFolder FilePath
|
||||||
| SetFilesFolder FilePath
|
| SetFilesFolder FilePath
|
||||||
| APISetXFTPConfig (Maybe XFTPFileConfig)
|
| APISetXFTPConfig (Maybe XFTPFileConfig)
|
||||||
|
| APISetEncryptLocalFiles Bool
|
||||||
| SetContactMergeEnabled Bool
|
| SetContactMergeEnabled Bool
|
||||||
| APIExportArchive ArchiveConfig
|
| APIExportArchive ArchiveConfig
|
||||||
| ExportArchive
|
| ExportArchive
|
||||||
@ -406,8 +408,8 @@ data ChatCommand
|
|||||||
| ForwardFile ChatName FileTransferId
|
| ForwardFile ChatName FileTransferId
|
||||||
| ForwardImage ChatName FileTransferId
|
| ForwardImage ChatName FileTransferId
|
||||||
| SendFileDescription ChatName FilePath
|
| SendFileDescription ChatName FilePath
|
||||||
| ReceiveFile {fileId :: FileTransferId, storeEncrypted :: Bool, fileInline :: Maybe Bool, filePath :: Maybe FilePath}
|
| ReceiveFile {fileId :: FileTransferId, storeEncrypted :: Maybe Bool, fileInline :: Maybe Bool, filePath :: Maybe FilePath}
|
||||||
| SetFileToReceive {fileId :: FileTransferId, storeEncrypted :: Bool}
|
| SetFileToReceive {fileId :: FileTransferId, storeEncrypted :: Maybe Bool}
|
||||||
| CancelFile FileTransferId
|
| CancelFile FileTransferId
|
||||||
| FileStatus FileTransferId
|
| FileStatus FileTransferId
|
||||||
| ShowProfile -- UserId (not used in UI)
|
| ShowProfile -- UserId (not used in UI)
|
||||||
@ -723,7 +725,8 @@ instance ToJSON InvitationLinkPlan where
|
|||||||
data ContactAddressPlan
|
data ContactAddressPlan
|
||||||
= CAPOk
|
= CAPOk
|
||||||
| CAPOwnLink
|
| CAPOwnLink
|
||||||
| CAPConnecting {contact :: Contact}
|
| CAPConnectingConfirmReconnect
|
||||||
|
| CAPConnectingProhibit {contact :: Contact}
|
||||||
| CAPKnown {contact :: Contact}
|
| CAPKnown {contact :: Contact}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
|
|
||||||
@ -737,7 +740,8 @@ instance ToJSON ContactAddressPlan where
|
|||||||
data GroupLinkPlan
|
data GroupLinkPlan
|
||||||
= GLPOk
|
= GLPOk
|
||||||
| GLPOwnLink {groupInfo :: GroupInfo}
|
| GLPOwnLink {groupInfo :: GroupInfo}
|
||||||
| GLPConnecting {groupInfo_ :: Maybe GroupInfo}
|
| GLPConnectingConfirmReconnect
|
||||||
|
| GLPConnectingProhibit {groupInfo_ :: Maybe GroupInfo}
|
||||||
| GLPKnown {groupInfo :: GroupInfo}
|
| GLPKnown {groupInfo :: GroupInfo}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
|
|
||||||
@ -748,8 +752,8 @@ instance ToJSON GroupLinkPlan where
|
|||||||
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "GLP"
|
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "GLP"
|
||||||
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "GLP"
|
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "GLP"
|
||||||
|
|
||||||
connectionPlanOk :: ConnectionPlan -> Bool
|
connectionPlanProceed :: ConnectionPlan -> Bool
|
||||||
connectionPlanOk = \case
|
connectionPlanProceed = \case
|
||||||
CPInvitationLink ilp -> case ilp of
|
CPInvitationLink ilp -> case ilp of
|
||||||
ILPOk -> True
|
ILPOk -> True
|
||||||
ILPOwnLink -> True
|
ILPOwnLink -> True
|
||||||
@ -757,10 +761,12 @@ connectionPlanOk = \case
|
|||||||
CPContactAddress cap -> case cap of
|
CPContactAddress cap -> case cap of
|
||||||
CAPOk -> True
|
CAPOk -> True
|
||||||
CAPOwnLink -> True
|
CAPOwnLink -> True
|
||||||
|
CAPConnectingConfirmReconnect -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
CPGroupLink glp -> case glp of
|
CPGroupLink glp -> case glp of
|
||||||
GLPOk -> True
|
GLPOk -> True
|
||||||
GLPOwnLink _ -> True
|
GLPOwnLink _ -> True
|
||||||
|
GLPConnectingConfirmReconnect -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
newtype UserPwd = UserPwd {unUserPwd :: Text}
|
newtype UserPwd = UserPwd {unUserPwd :: Text}
|
||||||
|
@ -33,7 +33,7 @@ import Simplex.Chat.Types.Util
|
|||||||
import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), ConnReqScheme (..), ConnReqUriData (..), ConnectionRequestUri (..), SMPQueue (..))
|
import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), ConnReqScheme (..), ConnReqUriData (..), ConnectionRequestUri (..), SMPQueue (..))
|
||||||
import Simplex.Messaging.Encoding.String
|
import Simplex.Messaging.Encoding.String
|
||||||
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, fstToLower, sumTypeJSON)
|
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, fstToLower, sumTypeJSON)
|
||||||
import Simplex.Messaging.Protocol (ProtocolServer (..), SrvLoc (..))
|
import Simplex.Messaging.Protocol (ProtocolServer (..))
|
||||||
import Simplex.Messaging.Util (safeDecodeUtf8)
|
import Simplex.Messaging.Util (safeDecodeUtf8)
|
||||||
import System.Console.ANSI.Types
|
import System.Console.ANSI.Types
|
||||||
import qualified Text.Email.Validate as Email
|
import qualified Text.Email.Validate as Email
|
||||||
@ -49,7 +49,7 @@ data Format
|
|||||||
| Secret
|
| Secret
|
||||||
| Colored {color :: FormatColor}
|
| Colored {color :: FormatColor}
|
||||||
| Uri
|
| Uri
|
||||||
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: Text, trustedUri :: Bool, smpHosts :: NonEmpty Text}
|
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: Text, smpHosts :: NonEmpty Text}
|
||||||
| Email
|
| Email
|
||||||
| Phone
|
| Phone
|
||||||
deriving (Eq, Show, Generic)
|
deriving (Eq, Show, Generic)
|
||||||
@ -248,15 +248,12 @@ markdownP = mconcat <$> A.many' fragmentP
|
|||||||
simplexUriFormat = \case
|
simplexUriFormat = \case
|
||||||
ACR _ (CRContactUri crData) ->
|
ACR _ (CRContactUri crData) ->
|
||||||
let uri = safeDecodeUtf8 . strEncode $ CRContactUri crData {crScheme = CRSSimplex}
|
let uri = safeDecodeUtf8 . strEncode $ CRContactUri crData {crScheme = CRSSimplex}
|
||||||
in SimplexLink (linkType' crData) uri (trustedUri' crData) $ uriHosts crData
|
in SimplexLink (linkType' crData) uri $ uriHosts crData
|
||||||
ACR _ (CRInvitationUri crData e2e) ->
|
ACR _ (CRInvitationUri crData e2e) ->
|
||||||
let uri = safeDecodeUtf8 . strEncode $ CRInvitationUri crData {crScheme = CRSSimplex} e2e
|
let uri = safeDecodeUtf8 . strEncode $ CRInvitationUri crData {crScheme = CRSSimplex} e2e
|
||||||
in SimplexLink XLInvitation uri (trustedUri' crData) $ uriHosts crData
|
in SimplexLink XLInvitation uri $ uriHosts crData
|
||||||
where
|
where
|
||||||
uriHosts ConnReqUriData {crSmpQueues} = L.map (safeDecodeUtf8 . strEncode) $ sconcat $ L.map (host . qServer) crSmpQueues
|
uriHosts ConnReqUriData {crSmpQueues} = L.map (safeDecodeUtf8 . strEncode) $ sconcat $ L.map (host . qServer) crSmpQueues
|
||||||
trustedUri' ConnReqUriData {crScheme} = case crScheme of
|
|
||||||
CRSSimplex -> True
|
|
||||||
CRSAppServer (SrvLoc host _) -> host == "simplex.chat"
|
|
||||||
linkType' ConnReqUriData {crClientData} = case crClientData >>= decodeJSON of
|
linkType' ConnReqUriData {crClientData} = case crClientData >>= decodeJSON of
|
||||||
Just (CRDataGroup _) -> XLGroup
|
Just (CRDataGroup _) -> XLGroup
|
||||||
Nothing -> XLContact
|
Nothing -> XLContact
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
module Simplex.Chat.Store.Connections
|
module Simplex.Chat.Store.Connections
|
||||||
( getConnectionEntity,
|
( getConnectionEntity,
|
||||||
getConnectionEntityByConnReq,
|
getConnectionEntityByConnReq,
|
||||||
|
getContactConnEntityByConnReqHash,
|
||||||
getConnectionsToSubscribe,
|
getConnectionsToSubscribe,
|
||||||
unsetConnectionToSubscribe,
|
unsetConnectionToSubscribe,
|
||||||
)
|
)
|
||||||
@ -153,10 +154,33 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do
|
|||||||
userContact_ [(cReq, groupId)] = Right UserContact {userContactLinkId, connReqContact = cReq, groupId}
|
userContact_ [(cReq, groupId)] = Right UserContact {userContactLinkId, connReqContact = cReq, groupId}
|
||||||
userContact_ _ = Left SEUserContactLinkNotFound
|
userContact_ _ = Left SEUserContactLinkNotFound
|
||||||
|
|
||||||
getConnectionEntityByConnReq :: DB.Connection -> User -> ConnReqInvitation -> IO (Maybe ConnectionEntity)
|
getConnectionEntityByConnReq :: DB.Connection -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity)
|
||||||
getConnectionEntityByConnReq db user cReq = do
|
getConnectionEntityByConnReq db user (cReqSchema1, cReqSchema2) = do
|
||||||
connId_ <- maybeFirstRow fromOnly $
|
connId_ <- maybeFirstRow fromOnly $
|
||||||
DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv = ? LIMIT 1" (Only cReq)
|
DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv IN (?,?) LIMIT 1" (cReqSchema1, cReqSchema2)
|
||||||
|
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_
|
||||||
|
|
||||||
|
-- search connection for connection plan:
|
||||||
|
-- multiple connections can have same via_contact_uri_hash if request was repeated;
|
||||||
|
-- this function searches for latest connection with contact so that "known contact" plan would be chosen;
|
||||||
|
-- deleted connections are filtered out to allow re-connecting via same contact address
|
||||||
|
getContactConnEntityByConnReqHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity)
|
||||||
|
getContactConnEntityByConnReqHash db user (cReqHash1, cReqHash2) = do
|
||||||
|
connId_ <- maybeFirstRow fromOnly $
|
||||||
|
DB.query
|
||||||
|
db
|
||||||
|
[sql|
|
||||||
|
SELECT agent_conn_id FROM (
|
||||||
|
SELECT
|
||||||
|
agent_conn_id,
|
||||||
|
(CASE WHEN contact_id IS NOT NULL THEN 1 ELSE 0 END) AS conn_ord
|
||||||
|
FROM connections
|
||||||
|
WHERE via_contact_uri_hash IN (?,?) AND conn_status != ?
|
||||||
|
ORDER BY conn_ord DESC, created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
|]
|
||||||
|
(cReqHash1, cReqHash2, ConnDeleted)
|
||||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_
|
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_
|
||||||
|
|
||||||
getConnectionsToSubscribe :: DB.Connection -> IO ([ConnId], [ConnectionEntity])
|
getConnectionsToSubscribe :: DB.Connection -> IO ([ConnId], [ConnectionEntity])
|
||||||
|
@ -1121,21 +1121,21 @@ getGroupInfo db User {userId, userContactId} groupId =
|
|||||||
|]
|
|]
|
||||||
(groupId, userId, userContactId)
|
(groupId, userId, userContactId)
|
||||||
|
|
||||||
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> ConnReqContact -> IO (Maybe GroupInfo)
|
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
||||||
getGroupInfoByUserContactLinkConnReq db user cReq = do
|
getGroupInfoByUserContactLinkConnReq db user (cReqSchema1, cReqSchema2) = do
|
||||||
groupId_ <- maybeFirstRow fromOnly $
|
groupId_ <- maybeFirstRow fromOnly $
|
||||||
DB.query
|
DB.query
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT group_id
|
SELECT group_id
|
||||||
FROM user_contact_links
|
FROM user_contact_links
|
||||||
WHERE conn_req_contact = ?
|
WHERE conn_req_contact IN (?,?)
|
||||||
|]
|
|]
|
||||||
(Only cReq)
|
(cReqSchema1, cReqSchema2)
|
||||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_
|
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_
|
||||||
|
|
||||||
getGroupInfoByGroupLinkHash :: DB.Connection -> User -> ConnReqUriHash -> IO (Maybe GroupInfo)
|
getGroupInfoByGroupLinkHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo)
|
||||||
getGroupInfoByGroupLinkHash db user@User {userId, userContactId} groupLinkHash = do
|
getGroupInfoByGroupLinkHash db user@User {userId, userContactId} (groupLinkHash1, groupLinkHash2) = do
|
||||||
groupId_ <- maybeFirstRow fromOnly $
|
groupId_ <- maybeFirstRow fromOnly $
|
||||||
DB.query
|
DB.query
|
||||||
db
|
db
|
||||||
@ -1143,11 +1143,11 @@ getGroupInfoByGroupLinkHash db user@User {userId, userContactId} groupLinkHash =
|
|||||||
SELECT g.group_id
|
SELECT g.group_id
|
||||||
FROM groups g
|
FROM groups g
|
||||||
JOIN group_members mu ON mu.group_id = g.group_id
|
JOIN group_members mu ON mu.group_id = g.group_id
|
||||||
WHERE g.user_id = ? AND g.via_group_link_uri_hash = ?
|
WHERE g.user_id = ? AND g.via_group_link_uri_hash IN (?,?)
|
||||||
AND mu.contact_id = ? AND mu.member_status NOT IN (?,?,?)
|
AND mu.contact_id = ? AND mu.member_status NOT IN (?,?,?)
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|]
|
|]
|
||||||
(userId, groupLinkHash, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
|
(userId, groupLinkHash1, groupLinkHash2, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
|
||||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_
|
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_
|
||||||
|
|
||||||
getGroupIdByName :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO GroupId
|
getGroupIdByName :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO GroupId
|
||||||
|
@ -442,17 +442,17 @@ getUserContactLinkById db userId userContactLinkId =
|
|||||||
|]
|
|]
|
||||||
(userId, userContactLinkId)
|
(userId, userContactLinkId)
|
||||||
|
|
||||||
getUserContactLinkByConnReq :: DB.Connection -> ConnReqContact -> IO (Maybe UserContactLink)
|
getUserContactLinkByConnReq :: DB.Connection -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink)
|
||||||
getUserContactLinkByConnReq db cReq =
|
getUserContactLinkByConnReq db (cReqSchema1, cReqSchema2) =
|
||||||
maybeFirstRow toUserContactLink $
|
maybeFirstRow toUserContactLink $
|
||||||
DB.query
|
DB.query
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT conn_req_contact, auto_accept, auto_accept_incognito, auto_reply_msg_content
|
SELECT conn_req_contact, auto_accept, auto_accept_incognito, auto_reply_msg_content
|
||||||
FROM user_contact_links
|
FROM user_contact_links
|
||||||
WHERE conn_req_contact = ?
|
WHERE conn_req_contact IN (?,?)
|
||||||
|]
|
|]
|
||||||
(Only cReq)
|
(cReqSchema1, cReqSchema2)
|
||||||
|
|
||||||
updateUserAddressAutoAccept :: DB.Connection -> User -> Maybe AutoAccept -> ExceptT StoreError IO UserContactLink
|
updateUserAddressAutoAccept :: DB.Connection -> User -> Maybe AutoAccept -> ExceptT StoreError IO UserContactLink
|
||||||
updateUserAddressAutoAccept db user@User {userId} autoAccept = do
|
updateUserAddressAutoAccept db user@User {userId} autoAccept = do
|
||||||
|
@ -207,6 +207,9 @@ contactReady Contact {activeConn} = connReady activeConn
|
|||||||
contactActive :: Contact -> Bool
|
contactActive :: Contact -> Bool
|
||||||
contactActive Contact {contactStatus} = contactStatus == CSActive
|
contactActive Contact {contactStatus} = contactStatus == CSActive
|
||||||
|
|
||||||
|
contactDeleted :: Contact -> Bool
|
||||||
|
contactDeleted Contact {contactStatus} = contactStatus == CSDeleted
|
||||||
|
|
||||||
contactSecurityCode :: Contact -> Maybe SecurityCode
|
contactSecurityCode :: Contact -> Maybe SecurityCode
|
||||||
contactSecurityCode Contact {activeConn} = connectionCode activeConn
|
contactSecurityCode Contact {activeConn} = connectionCode activeConn
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ responseToView (currentRH, user_) ChatConfig {logLevel, showReactions, showRecei
|
|||||||
CRRcvFileDescrReady _ _ -> []
|
CRRcvFileDescrReady _ _ -> []
|
||||||
CRRcvFileDescrNotReady _ _ -> []
|
CRRcvFileDescrNotReady _ _ -> []
|
||||||
CRRcvFileProgressXFTP {} -> []
|
CRRcvFileProgressXFTP {} -> []
|
||||||
CRRcvFileAccepted u ci -> ttyUser u $ savingFile' testView ci
|
CRRcvFileAccepted u ci -> ttyUser u $ savingFile' ci
|
||||||
CRRcvFileAcceptedSndCancelled u ft -> ttyUser u $ viewRcvFileSndCancelled ft
|
CRRcvFileAcceptedSndCancelled u ft -> ttyUser u $ viewRcvFileSndCancelled ft
|
||||||
CRSndFileCancelled u _ ftm fts -> ttyUser u $ viewSndFileCancelled ftm fts
|
CRSndFileCancelled u _ ftm fts -> ttyUser u $ viewSndFileCancelled ftm fts
|
||||||
CRRcvFileCancelled u _ ft -> ttyUser u $ receivingFile_ "cancelled" ft
|
CRRcvFileCancelled u _ ft -> ttyUser u $ receivingFile_ "cancelled" ft
|
||||||
@ -179,10 +179,10 @@ responseToView (currentRH, user_) ChatConfig {logLevel, showReactions, showRecei
|
|||||||
CRContactUpdated {user = u, fromContact = c, toContact = c'} -> ttyUser u $ viewContactUpdated c c' <> viewContactPrefsUpdated u c c'
|
CRContactUpdated {user = u, fromContact = c, toContact = c'} -> ttyUser u $ viewContactUpdated c c' <> viewContactPrefsUpdated u c c'
|
||||||
CRContactsMerged u intoCt mergedCt ct' -> ttyUser u $ viewContactsMerged intoCt mergedCt ct'
|
CRContactsMerged u intoCt mergedCt ct' -> ttyUser u $ viewContactsMerged intoCt mergedCt ct'
|
||||||
CRReceivedContactRequest u UserContactRequest {localDisplayName = c, profile} -> ttyUser u $ viewReceivedContactRequest c profile
|
CRReceivedContactRequest u UserContactRequest {localDisplayName = c, profile} -> ttyUser u $ viewReceivedContactRequest c profile
|
||||||
CRRcvFileStart u ci -> ttyUser u $ receivingFile_' "started" ci
|
CRRcvFileStart u ci -> ttyUser u $ receivingFile_' testView "started" ci
|
||||||
CRRcvFileComplete u ci -> ttyUser u $ receivingFile_' "completed" ci
|
CRRcvFileComplete u ci -> ttyUser u $ receivingFile_' testView "completed" ci
|
||||||
CRRcvFileSndCancelled u _ ft -> ttyUser u $ viewRcvFileSndCancelled ft
|
CRRcvFileSndCancelled u _ ft -> ttyUser u $ viewRcvFileSndCancelled ft
|
||||||
CRRcvFileError u ci e -> ttyUser u $ receivingFile_' "error" ci <> [sShow e]
|
CRRcvFileError u ci e -> ttyUser u $ receivingFile_' testView "error" ci <> [sShow e]
|
||||||
CRSndFileStart u _ ft -> ttyUser u $ sendingFile_ "started" ft
|
CRSndFileStart u _ ft -> ttyUser u $ sendingFile_ "started" ft
|
||||||
CRSndFileComplete u _ ft -> ttyUser u $ sendingFile_ "completed" ft
|
CRSndFileComplete u _ ft -> ttyUser u $ sendingFile_ "completed" ft
|
||||||
CRSndFileStartXFTP {} -> []
|
CRSndFileStartXFTP {} -> []
|
||||||
@ -707,11 +707,14 @@ viewConnReqInvitation :: ConnReqInvitation -> [StyledString]
|
|||||||
viewConnReqInvitation cReq =
|
viewConnReqInvitation cReq =
|
||||||
[ "pass this invitation link to your contact (via another channel): ",
|
[ "pass this invitation link to your contact (via another channel): ",
|
||||||
"",
|
"",
|
||||||
(plain . strEncode) cReq,
|
(plain . strEncode) (simplexChatInvitation cReq),
|
||||||
"",
|
"",
|
||||||
"and ask them to connect: " <> highlight' "/c <invitation_link_above>"
|
"and ask them to connect: " <> highlight' "/c <invitation_link_above>"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
simplexChatInvitation :: ConnReqInvitation -> ConnReqInvitation
|
||||||
|
simplexChatInvitation (CRInvitationUri crData e2e) = CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||||
|
|
||||||
viewContactNotFound :: ContactName -> Maybe (GroupInfo, GroupMember) -> [StyledString]
|
viewContactNotFound :: ContactName -> Maybe (GroupInfo, GroupMember) -> [StyledString]
|
||||||
viewContactNotFound cName suspectedMember =
|
viewContactNotFound cName suspectedMember =
|
||||||
["no contact " <> ttyContact cName <> useMessageMember]
|
["no contact " <> ttyContact cName <> useMessageMember]
|
||||||
@ -750,7 +753,7 @@ connReqContact_ :: StyledString -> ConnReqContact -> [StyledString]
|
|||||||
connReqContact_ intro cReq =
|
connReqContact_ intro cReq =
|
||||||
[ intro,
|
[ intro,
|
||||||
"",
|
"",
|
||||||
(plain . strEncode) cReq,
|
(plain . strEncode) (simplexChatContact cReq),
|
||||||
"",
|
"",
|
||||||
"Anybody can send you contact requests with: " <> highlight' "/c <contact_link_above>",
|
"Anybody can send you contact requests with: " <> highlight' "/c <contact_link_above>",
|
||||||
"to show it again: " <> highlight' "/sa",
|
"to show it again: " <> highlight' "/sa",
|
||||||
@ -758,6 +761,9 @@ connReqContact_ intro cReq =
|
|||||||
"to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)"
|
"to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
simplexChatContact :: ConnReqContact -> ConnReqContact
|
||||||
|
simplexChatContact (CRContactUri crData) = CRContactUri crData {crScheme = simplexChat}
|
||||||
|
|
||||||
autoAcceptStatus_ :: Maybe AutoAccept -> [StyledString]
|
autoAcceptStatus_ :: Maybe AutoAccept -> [StyledString]
|
||||||
autoAcceptStatus_ = \case
|
autoAcceptStatus_ = \case
|
||||||
Just AutoAccept {acceptIncognito, autoReply} ->
|
Just AutoAccept {acceptIncognito, autoReply} ->
|
||||||
@ -769,7 +775,7 @@ groupLink_ :: StyledString -> GroupInfo -> ConnReqContact -> GroupMemberRole ->
|
|||||||
groupLink_ intro g cReq mRole =
|
groupLink_ intro g cReq mRole =
|
||||||
[ intro,
|
[ intro,
|
||||||
"",
|
"",
|
||||||
(plain . strEncode) cReq,
|
(plain . strEncode) (simplexChatContact cReq),
|
||||||
"",
|
"",
|
||||||
"Anybody can connect to you and join group as " <> showRole mRole <> " with: " <> highlight' "/c <group_link_above>",
|
"Anybody can connect to you and join group as " <> showRole mRole <> " with: " <> highlight' "/c <group_link_above>",
|
||||||
"to show it again: " <> highlight ("/show link #" <> viewGroupName g),
|
"to show it again: " <> highlight ("/show link #" <> viewGroupName g),
|
||||||
@ -1036,7 +1042,7 @@ viewContactInfo :: Contact -> ConnectionStats -> Maybe Profile -> [StyledString]
|
|||||||
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink}, activeConn} stats incognitoProfile =
|
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink}, activeConn} stats incognitoProfile =
|
||||||
["contact ID: " <> sShow contactId]
|
["contact ID: " <> sShow contactId]
|
||||||
<> viewConnectionStats stats
|
<> viewConnectionStats stats
|
||||||
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) l]) contactLink
|
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) (simplexChatContact l)]) contactLink
|
||||||
<> maybe
|
<> maybe
|
||||||
["you've shared main profile with this contact"]
|
["you've shared main profile with this contact"]
|
||||||
(\p -> ["you've shared incognito profile with this contact: " <> incognitoProfile' p])
|
(\p -> ["you've shared incognito profile with this contact: " <> incognitoProfile' p])
|
||||||
@ -1295,7 +1301,8 @@ viewConnectionPlan = \case
|
|||||||
CPContactAddress cap -> case cap of
|
CPContactAddress cap -> case cap of
|
||||||
CAPOk -> [ctAddr "ok to connect"]
|
CAPOk -> [ctAddr "ok to connect"]
|
||||||
CAPOwnLink -> [ctAddr "own address"]
|
CAPOwnLink -> [ctAddr "own address"]
|
||||||
CAPConnecting ct -> [ctAddr ("connecting to contact " <> ttyContact' ct)]
|
CAPConnectingConfirmReconnect -> [ctAddr "connecting, allowed to reconnect"]
|
||||||
|
CAPConnectingProhibit ct -> [ctAddr ("connecting to contact " <> ttyContact' ct)]
|
||||||
CAPKnown ct ->
|
CAPKnown ct ->
|
||||||
[ ctAddr ("known contact " <> ttyContact' ct),
|
[ ctAddr ("known contact " <> ttyContact' ct),
|
||||||
"use " <> ttyToContact' ct <> highlight' "<message>" <> " to send messages"
|
"use " <> ttyToContact' ct <> highlight' "<message>" <> " to send messages"
|
||||||
@ -1305,8 +1312,9 @@ viewConnectionPlan = \case
|
|||||||
CPGroupLink glp -> case glp of
|
CPGroupLink glp -> case glp of
|
||||||
GLPOk -> [grpLink "ok to connect"]
|
GLPOk -> [grpLink "ok to connect"]
|
||||||
GLPOwnLink g -> [grpLink "own link for group " <> ttyGroup' g]
|
GLPOwnLink g -> [grpLink "own link for group " <> ttyGroup' g]
|
||||||
GLPConnecting Nothing -> [grpLink "connecting"]
|
GLPConnectingConfirmReconnect -> [grpLink "connecting, allowed to reconnect"]
|
||||||
GLPConnecting (Just g) -> [grpLink ("connecting to group " <> ttyGroup' g)]
|
GLPConnectingProhibit Nothing -> [grpLink "connecting"]
|
||||||
|
GLPConnectingProhibit (Just g) -> [grpLink ("connecting to group " <> ttyGroup' g)]
|
||||||
GLPKnown g ->
|
GLPKnown g ->
|
||||||
[ grpLink ("known group " <> ttyGroup' g),
|
[ grpLink ("known group " <> ttyGroup' g),
|
||||||
"use " <> ttyToGroup g <> highlight' "<message>" <> " to send messages"
|
"use " <> ttyToGroup g <> highlight' "<message>" <> " to send messages"
|
||||||
@ -1463,27 +1471,28 @@ humanReadableSize size
|
|||||||
mB = kB * 1024
|
mB = kB * 1024
|
||||||
gB = mB * 1024
|
gB = mB * 1024
|
||||||
|
|
||||||
savingFile' :: Bool -> AChatItem -> [StyledString]
|
savingFile' :: AChatItem -> [StyledString]
|
||||||
savingFile' testView (AChatItem _ _ chat ChatItem {file = Just CIFile {fileId, fileSource = Just (CryptoFile filePath cfArgs_)}, chatDir}) =
|
savingFile' (AChatItem _ _ chat ChatItem {file = Just CIFile {fileId, fileSource = Just (CryptoFile filePath _)}, chatDir}) =
|
||||||
let from = case (chat, chatDir) of
|
["saving file " <> sShow fileId <> fileFrom chat chatDir <> " to " <> plain filePath]
|
||||||
(DirectChat Contact {localDisplayName = c}, CIDirectRcv) -> " from " <> ttyContact c
|
savingFile' _ = ["saving file"] -- shouldn't happen
|
||||||
(_, CIGroupRcv GroupMember {localDisplayName = m}) -> " from " <> ttyContact m
|
|
||||||
_ -> ""
|
|
||||||
in ["saving file " <> sShow fileId <> from <> " to " <> plain filePath] <> cfArgsStr
|
|
||||||
where
|
|
||||||
cfArgsStr = case cfArgs_ of
|
|
||||||
Just cfArgs@(CFArgs key nonce)
|
|
||||||
| testView -> [plain $ LB.unpack $ J.encode cfArgs]
|
|
||||||
| otherwise -> [plain $ "encryption key: " <> strEncode key <> ", nonce: " <> strEncode nonce]
|
|
||||||
_ -> []
|
|
||||||
savingFile' _ _ = ["saving file"] -- shouldn't happen
|
|
||||||
|
|
||||||
receivingFile_' :: StyledString -> AChatItem -> [StyledString]
|
receivingFile_' :: Bool -> String -> AChatItem -> [StyledString]
|
||||||
receivingFile_' status (AChatItem _ _ (DirectChat c) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIDirectRcv}) =
|
receivingFile_' testView status (AChatItem _ _ chat ChatItem {file = Just CIFile {fileId, fileName, fileSource = Just (CryptoFile _ cfArgs_)}, chatDir}) =
|
||||||
[status <> " receiving " <> fileTransferStr fileId fileName <> " from " <> ttyContact' c]
|
[plain status <> " receiving " <> fileTransferStr fileId fileName <> fileFrom chat chatDir] <> cfArgsStr cfArgs_
|
||||||
receivingFile_' status (AChatItem _ _ _ ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIGroupRcv m}) =
|
where
|
||||||
[status <> " receiving " <> fileTransferStr fileId fileName <> " from " <> ttyMember m]
|
cfArgsStr (Just cfArgs@(CFArgs key nonce)) = [plain s | status == "completed"]
|
||||||
receivingFile_' status _ = [status <> " receiving file"] -- shouldn't happen
|
where
|
||||||
|
s =
|
||||||
|
if testView
|
||||||
|
then LB.toStrict $ J.encode cfArgs
|
||||||
|
else "encryption key: " <> strEncode key <> ", nonce: " <> strEncode nonce
|
||||||
|
cfArgsStr _ = []
|
||||||
|
receivingFile_' _ status _ = [plain status <> " receiving file"] -- shouldn't happen
|
||||||
|
|
||||||
|
fileFrom :: ChatInfo c -> CIDirection c d -> StyledString
|
||||||
|
fileFrom (DirectChat ct) CIDirectRcv = " from " <> ttyContact' ct
|
||||||
|
fileFrom _ (CIGroupRcv m) = " from " <> ttyMember m
|
||||||
|
fileFrom _ _ = ""
|
||||||
|
|
||||||
receivingFile_ :: StyledString -> RcvFileTransfer -> [StyledString]
|
receivingFile_ :: StyledString -> RcvFileTransfer -> [StyledString]
|
||||||
receivingFile_ status ft@RcvFileTransfer {senderDisplayName = c} =
|
receivingFile_ status ft@RcvFileTransfer {senderDisplayName = c} =
|
||||||
|
@ -49,7 +49,7 @@ extra-deps:
|
|||||||
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
|
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
|
||||||
# - ../simplexmq
|
# - ../simplexmq
|
||||||
- github: simplex-chat/simplexmq
|
- github: simplex-chat/simplexmq
|
||||||
commit: 6b0da8ac50b1582c9f5187c316b93fc8f12c9365
|
commit: 1ad69cf74f18f25713ce564e1629d2538313b9e0
|
||||||
- github: kazu-yamamoto/http2
|
- github: kazu-yamamoto/http2
|
||||||
commit: b5a1b7200cf5bc7044af34ba325284271f6dff25
|
commit: b5a1b7200cf5bc7044af34ba325284271f6dff25
|
||||||
# - ../direct-sqlcipher
|
# - ../direct-sqlcipher
|
||||||
|
@ -270,6 +270,10 @@ testPlanInvitationLinkOwn tmp =
|
|||||||
alice ##> ("/_connect plan 1 " <> inv)
|
alice ##> ("/_connect plan 1 " <> inv)
|
||||||
alice <## "invitation link: own link"
|
alice <## "invitation link: own link"
|
||||||
|
|
||||||
|
let invSchema2 = linkAnotherSchema inv
|
||||||
|
alice ##> ("/_connect plan 1 " <> invSchema2)
|
||||||
|
alice <## "invitation link: own link"
|
||||||
|
|
||||||
alice ##> ("/c " <> inv)
|
alice ##> ("/c " <> inv)
|
||||||
alice <## "confirmation sent!"
|
alice <## "confirmation sent!"
|
||||||
alice
|
alice
|
||||||
@ -305,6 +309,10 @@ testPlanInvitationLinkConnecting tmp = do
|
|||||||
bob ##> ("/_connect plan 1 " <> inv)
|
bob ##> ("/_connect plan 1 " <> inv)
|
||||||
bob <## "invitation link: connecting"
|
bob <## "invitation link: connecting"
|
||||||
|
|
||||||
|
let invSchema2 = linkAnotherSchema inv
|
||||||
|
bob ##> ("/_connect plan 1 " <> invSchema2)
|
||||||
|
bob <## "invitation link: connecting"
|
||||||
|
|
||||||
testContactClear :: HasCallStack => FilePath -> IO ()
|
testContactClear :: HasCallStack => FilePath -> IO ()
|
||||||
testContactClear =
|
testContactClear =
|
||||||
testChat2 aliceProfile bobProfile $
|
testChat2 aliceProfile bobProfile $
|
||||||
|
@ -33,6 +33,7 @@ chatFileTests = do
|
|||||||
describe "send and receive file" $ fileTestMatrix2 runTestFileTransfer
|
describe "send and receive file" $ fileTestMatrix2 runTestFileTransfer
|
||||||
describe "send file, receive and locally encrypt file" $ fileTestMatrix2 runTestFileTransferEncrypted
|
describe "send file, receive and locally encrypt file" $ fileTestMatrix2 runTestFileTransferEncrypted
|
||||||
it "send and receive file inline (without accepting)" testInlineFileTransfer
|
it "send and receive file inline (without accepting)" testInlineFileTransfer
|
||||||
|
it "send inline file, receive (without accepting) and locally encrypt" testInlineFileTransferEncrypted
|
||||||
xit'' "accept inline file transfer, sender cancels during transfer" testAcceptInlineFileSndCancelDuringTransfer
|
xit'' "accept inline file transfer, sender cancels during transfer" testAcceptInlineFileSndCancelDuringTransfer
|
||||||
it "send and receive small file inline (default config)" testSmallInlineFileTransfer
|
it "send and receive small file inline (default config)" testSmallInlineFileTransfer
|
||||||
it "small file sent without acceptance is ignored in terminal by default" testSmallInlineFileIgnored
|
it "small file sent without acceptance is ignored in terminal by default" testSmallInlineFileIgnored
|
||||||
@ -107,7 +108,6 @@ runTestFileTransferEncrypted alice bob = do
|
|||||||
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
||||||
bob ##> "/fr 1 encrypt=on ./tests/tmp"
|
bob ##> "/fr 1 encrypt=on ./tests/tmp"
|
||||||
bob <## "saving file 1 from alice to ./tests/tmp/test.pdf"
|
bob <## "saving file 1 from alice to ./tests/tmp/test.pdf"
|
||||||
Just (CFArgs key nonce) <- J.decode . LB.pack <$> getTermLine bob
|
|
||||||
concurrently_
|
concurrently_
|
||||||
(bob <## "started receiving file 1 (test.pdf) from alice")
|
(bob <## "started receiving file 1 (test.pdf) from alice")
|
||||||
(alice <## "started sending file 1 (test.pdf) to bob")
|
(alice <## "started sending file 1 (test.pdf) to bob")
|
||||||
@ -123,6 +123,7 @@ runTestFileTransferEncrypted alice bob = do
|
|||||||
"completed sending file 1 (test.pdf) to bob"
|
"completed sending file 1 (test.pdf) to bob"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
Just (CFArgs key nonce) <- J.decode . LB.pack <$> getTermLine bob
|
||||||
src <- B.readFile "./tests/fixtures/test.pdf"
|
src <- B.readFile "./tests/fixtures/test.pdf"
|
||||||
-- dest <- B.readFile "./tests/tmp/test.pdf"
|
-- dest <- B.readFile "./tests/tmp/test.pdf"
|
||||||
-- dest `shouldBe` src
|
-- dest `shouldBe` src
|
||||||
@ -154,6 +155,34 @@ testInlineFileTransfer =
|
|||||||
where
|
where
|
||||||
cfg = testCfg {inlineFiles = defaultInlineFilesConfig {offerChunks = 100, sendChunks = 100, receiveChunks = 100}}
|
cfg = testCfg {inlineFiles = defaultInlineFilesConfig {offerChunks = 100, sendChunks = 100, receiveChunks = 100}}
|
||||||
|
|
||||||
|
testInlineFileTransferEncrypted :: HasCallStack => FilePath -> IO ()
|
||||||
|
testInlineFileTransferEncrypted =
|
||||||
|
testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do
|
||||||
|
connectUsers alice bob
|
||||||
|
bob ##> "/_files_folder ./tests/tmp/"
|
||||||
|
bob <## "ok"
|
||||||
|
bob ##> "/_files_encrypt on"
|
||||||
|
bob <## "ok"
|
||||||
|
alice ##> "/_send @2 json {\"msgContent\":{\"type\":\"voice\", \"duration\":10, \"text\":\"\"}, \"filePath\":\"./tests/fixtures/test.jpg\"}"
|
||||||
|
alice <# "@bob voice message (00:10)"
|
||||||
|
alice <# "/f @bob ./tests/fixtures/test.jpg"
|
||||||
|
-- below is not shown in "sent" mode
|
||||||
|
-- alice <## "use /fc 1 to cancel sending"
|
||||||
|
bob <# "alice> voice message (00:10)"
|
||||||
|
bob <# "alice> sends file test.jpg (136.5 KiB / 139737 bytes)"
|
||||||
|
-- below is not shown in "sent" mode
|
||||||
|
-- bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
||||||
|
bob <## "started receiving file 1 (test.jpg) from alice"
|
||||||
|
concurrently_
|
||||||
|
(alice <## "completed sending file 1 (test.jpg) to bob")
|
||||||
|
(bob <## "completed receiving file 1 (test.jpg) from alice")
|
||||||
|
Just (CFArgs key nonce) <- J.decode . LB.pack <$> getTermLine bob
|
||||||
|
src <- B.readFile "./tests/fixtures/test.jpg"
|
||||||
|
Right dest <- chatReadFile "./tests/tmp/test.jpg" (strEncode key) (strEncode nonce)
|
||||||
|
LB.toStrict dest `shouldBe` src
|
||||||
|
where
|
||||||
|
cfg = testCfg {inlineFiles = defaultInlineFilesConfig {offerChunks = 100, sendChunks = 100, receiveChunks = 100}}
|
||||||
|
|
||||||
testAcceptInlineFileSndCancelDuringTransfer :: HasCallStack => FilePath -> IO ()
|
testAcceptInlineFileSndCancelDuringTransfer :: HasCallStack => FilePath -> IO ()
|
||||||
testAcceptInlineFileSndCancelDuringTransfer =
|
testAcceptInlineFileSndCancelDuringTransfer =
|
||||||
testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do
|
testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do
|
||||||
@ -1077,10 +1106,10 @@ testXFTPFileTransferEncrypted =
|
|||||||
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
||||||
bob ##> "/fr 1 encrypt=on ./tests/tmp/bob/"
|
bob ##> "/fr 1 encrypt=on ./tests/tmp/bob/"
|
||||||
bob <## "saving file 1 from alice to ./tests/tmp/bob/test.pdf"
|
bob <## "saving file 1 from alice to ./tests/tmp/bob/test.pdf"
|
||||||
Just (CFArgs key nonce) <- J.decode . LB.pack <$> getTermLine bob
|
|
||||||
alice <## "completed uploading file 1 (test.pdf) for bob"
|
alice <## "completed uploading file 1 (test.pdf) for bob"
|
||||||
bob <## "started receiving file 1 (test.pdf) from alice"
|
bob <## "started receiving file 1 (test.pdf) from alice"
|
||||||
bob <## "completed receiving file 1 (test.pdf) from alice"
|
bob <## "completed receiving file 1 (test.pdf) from alice"
|
||||||
|
Just (CFArgs key nonce) <- J.decode . LB.pack <$> getTermLine bob
|
||||||
Right dest <- chatReadFile "./tests/tmp/bob/test.pdf" (strEncode key) (strEncode nonce)
|
Right dest <- chatReadFile "./tests/tmp/bob/test.pdf" (strEncode key) (strEncode nonce)
|
||||||
LB.length dest `shouldBe` fromIntegral srcLen
|
LB.length dest `shouldBe` fromIntegral srcLen
|
||||||
LB.toStrict dest `shouldBe` src
|
LB.toStrict dest `shouldBe` src
|
||||||
|
@ -2290,6 +2290,11 @@ testPlanGroupLinkOkKnown =
|
|||||||
bob <## "group link: known group #team"
|
bob <## "group link: known group #team"
|
||||||
bob <## "use #team <message> to send messages"
|
bob <## "use #team <message> to send messages"
|
||||||
|
|
||||||
|
let gLinkSchema2 = linkAnotherSchema gLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
bob <## "group link: known group #team"
|
||||||
|
bob <## "use #team <message> to send messages"
|
||||||
|
|
||||||
bob ##> ("/c " <> gLink)
|
bob ##> ("/c " <> gLink)
|
||||||
bob <## "group link: known group #team"
|
bob <## "group link: known group #team"
|
||||||
bob <## "use #team <message> to send messages"
|
bob <## "use #team <message> to send messages"
|
||||||
@ -2331,6 +2336,11 @@ testPlanHostContactDeletedGroupLinkKnown =
|
|||||||
bob <## "group link: known group #team"
|
bob <## "group link: known group #team"
|
||||||
bob <## "use #team <message> to send messages"
|
bob <## "use #team <message> to send messages"
|
||||||
|
|
||||||
|
let gLinkSchema2 = linkAnotherSchema gLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
bob <## "group link: known group #team"
|
||||||
|
bob <## "use #team <message> to send messages"
|
||||||
|
|
||||||
bob ##> ("/c " <> gLink)
|
bob ##> ("/c " <> gLink)
|
||||||
bob <## "group link: known group #team"
|
bob <## "group link: known group #team"
|
||||||
bob <## "use #team <message> to send messages"
|
bob <## "use #team <message> to send messages"
|
||||||
@ -2347,6 +2357,10 @@ testPlanGroupLinkOwn tmp =
|
|||||||
alice ##> ("/_connect plan 1 " <> gLink)
|
alice ##> ("/_connect plan 1 " <> gLink)
|
||||||
alice <## "group link: own link for group #team"
|
alice <## "group link: own link for group #team"
|
||||||
|
|
||||||
|
let gLinkSchema2 = linkAnotherSchema gLink
|
||||||
|
alice ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
alice <## "group link: own link for group #team"
|
||||||
|
|
||||||
alice ##> ("/c " <> gLink)
|
alice ##> ("/c " <> gLink)
|
||||||
alice <## "connection request sent!"
|
alice <## "connection request sent!"
|
||||||
alice <## "alice_1 (Alice): accepting request to join group #team..."
|
alice <## "alice_1 (Alice): accepting request to join group #team..."
|
||||||
@ -2373,6 +2387,9 @@ testPlanGroupLinkOwn tmp =
|
|||||||
alice ##> ("/_connect plan 1 " <> gLink)
|
alice ##> ("/_connect plan 1 " <> gLink)
|
||||||
alice <## "group link: own link for group #team"
|
alice <## "group link: own link for group #team"
|
||||||
|
|
||||||
|
alice ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
alice <## "group link: own link for group #team"
|
||||||
|
|
||||||
-- group works if merged contact is deleted
|
-- group works if merged contact is deleted
|
||||||
alice ##> "/d alice_1"
|
alice ##> "/d alice_1"
|
||||||
alice <## "alice_1: contact is deleted"
|
alice <## "alice_1: contact is deleted"
|
||||||
@ -2397,8 +2414,19 @@ testPlanGroupLinkConnecting tmp = do
|
|||||||
alice ##> "/create link #team"
|
alice ##> "/create link #team"
|
||||||
getGroupLink alice "team" GRMember True
|
getGroupLink alice "team" GRMember True
|
||||||
withNewTestChat tmp "bob" bobProfile $ \bob -> do
|
withNewTestChat tmp "bob" bobProfile $ \bob -> do
|
||||||
|
threadDelay 100000
|
||||||
|
|
||||||
bob ##> ("/c " <> gLink)
|
bob ##> ("/c " <> gLink)
|
||||||
bob <## "connection request sent!"
|
bob <## "connection request sent!"
|
||||||
|
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLink)
|
||||||
|
bob <## "group link: connecting, allowed to reconnect"
|
||||||
|
|
||||||
|
let gLinkSchema2 = linkAnotherSchema gLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
bob <## "group link: connecting, allowed to reconnect"
|
||||||
|
|
||||||
|
threadDelay 100000
|
||||||
withTestChat tmp "alice" $ \alice -> do
|
withTestChat tmp "alice" $ \alice -> do
|
||||||
alice
|
alice
|
||||||
<### [ "1 group links active",
|
<### [ "1 group links active",
|
||||||
@ -2410,6 +2438,10 @@ testPlanGroupLinkConnecting tmp = do
|
|||||||
bob ##> ("/_connect plan 1 " <> gLink)
|
bob ##> ("/_connect plan 1 " <> gLink)
|
||||||
bob <## "group link: connecting"
|
bob <## "group link: connecting"
|
||||||
|
|
||||||
|
let gLinkSchema2 = linkAnotherSchema gLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
bob <## "group link: connecting"
|
||||||
|
|
||||||
bob ##> ("/c " <> gLink)
|
bob ##> ("/c " <> gLink)
|
||||||
bob <## "group link: connecting"
|
bob <## "group link: connecting"
|
||||||
|
|
||||||
@ -2455,6 +2487,10 @@ testPlanGroupLinkLeaveRejoin =
|
|||||||
bob ##> ("/_connect plan 1 " <> gLink)
|
bob ##> ("/_connect plan 1 " <> gLink)
|
||||||
bob <## "group link: ok to connect"
|
bob <## "group link: ok to connect"
|
||||||
|
|
||||||
|
let gLinkSchema2 = linkAnotherSchema gLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
bob <## "group link: ok to connect"
|
||||||
|
|
||||||
bob ##> ("/c " <> gLink)
|
bob ##> ("/c " <> gLink)
|
||||||
bob <## "connection request sent!"
|
bob <## "connection request sent!"
|
||||||
alice <## "bob_1 (Bob): accepting request to join group #team..."
|
alice <## "bob_1 (Bob): accepting request to join group #team..."
|
||||||
@ -2483,6 +2519,10 @@ testPlanGroupLinkLeaveRejoin =
|
|||||||
bob <## "group link: known group #team_1"
|
bob <## "group link: known group #team_1"
|
||||||
bob <## "use #team_1 <message> to send messages"
|
bob <## "use #team_1 <message> to send messages"
|
||||||
|
|
||||||
|
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
|
||||||
|
bob <## "group link: known group #team_1"
|
||||||
|
bob <## "use #team_1 <message> to send messages"
|
||||||
|
|
||||||
bob ##> ("/c " <> gLink)
|
bob ##> ("/c " <> gLink)
|
||||||
bob <## "group link: known group #team_1"
|
bob <## "group link: known group #team_1"
|
||||||
bob <## "use #team_1 <message> to send messages"
|
bob <## "use #team_1 <message> to send messages"
|
||||||
|
@ -599,6 +599,11 @@ testPlanAddressOkKnown =
|
|||||||
bob <## "contact address: known contact alice"
|
bob <## "contact address: known contact alice"
|
||||||
bob <## "use @alice <message> to send messages"
|
bob <## "use @alice <message> to send messages"
|
||||||
|
|
||||||
|
let cLinkSchema2 = linkAnotherSchema cLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
|
||||||
|
bob <## "contact address: known contact alice"
|
||||||
|
bob <## "use @alice <message> to send messages"
|
||||||
|
|
||||||
bob ##> ("/c " <> cLink)
|
bob ##> ("/c " <> cLink)
|
||||||
bob <## "contact address: known contact alice"
|
bob <## "contact address: known contact alice"
|
||||||
bob <## "use @alice <message> to send messages"
|
bob <## "use @alice <message> to send messages"
|
||||||
@ -612,11 +617,15 @@ testPlanAddressOwn tmp =
|
|||||||
alice ##> ("/_connect plan 1 " <> cLink)
|
alice ##> ("/_connect plan 1 " <> cLink)
|
||||||
alice <## "contact address: own address"
|
alice <## "contact address: own address"
|
||||||
|
|
||||||
|
let cLinkSchema2 = linkAnotherSchema cLink
|
||||||
|
alice ##> ("/_connect plan 1 " <> cLinkSchema2)
|
||||||
|
alice <## "contact address: own address"
|
||||||
|
|
||||||
alice ##> ("/c " <> cLink)
|
alice ##> ("/c " <> cLink)
|
||||||
alice <## "connection request sent!"
|
alice <## "connection request sent!"
|
||||||
alice <## "alice_1 (Alice) wants to connect to you!"
|
alice <## "alice_1 (Alice) wants to connect to you!"
|
||||||
alice <## "to accept: /ac alice_1"
|
alice <## "to accept: /ac alice_1"
|
||||||
alice <## ("to reject: /rc alice_1 (the sender will NOT be notified)")
|
alice <## "to reject: /rc alice_1 (the sender will NOT be notified)"
|
||||||
alice @@@ [("<@alice_1", ""), (":2","")]
|
alice @@@ [("<@alice_1", ""), (":2","")]
|
||||||
alice ##> "/ac alice_1"
|
alice ##> "/ac alice_1"
|
||||||
alice <## "alice_1 (Alice): accepting contact request..."
|
alice <## "alice_1 (Alice): accepting contact request..."
|
||||||
@ -651,8 +660,17 @@ testPlanAddressConnecting tmp = do
|
|||||||
getContactLink alice True
|
getContactLink alice True
|
||||||
withNewTestChat tmp "bob" bobProfile $ \bob -> do
|
withNewTestChat tmp "bob" bobProfile $ \bob -> do
|
||||||
threadDelay 100000
|
threadDelay 100000
|
||||||
|
|
||||||
bob ##> ("/c " <> cLink)
|
bob ##> ("/c " <> cLink)
|
||||||
bob <## "connection request sent!"
|
bob <## "connection request sent!"
|
||||||
|
|
||||||
|
bob ##> ("/_connect plan 1 " <> cLink)
|
||||||
|
bob <## "contact address: connecting, allowed to reconnect"
|
||||||
|
|
||||||
|
let cLinkSchema2 = linkAnotherSchema cLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
|
||||||
|
bob <## "contact address: connecting, allowed to reconnect"
|
||||||
|
|
||||||
threadDelay 100000
|
threadDelay 100000
|
||||||
withTestChat tmp "alice" $ \alice -> do
|
withTestChat tmp "alice" $ \alice -> do
|
||||||
alice <## "Your address is active! To show: /sa"
|
alice <## "Your address is active! To show: /sa"
|
||||||
@ -667,6 +685,10 @@ testPlanAddressConnecting tmp = do
|
|||||||
bob ##> ("/_connect plan 1 " <> cLink)
|
bob ##> ("/_connect plan 1 " <> cLink)
|
||||||
bob <## "contact address: connecting to contact alice"
|
bob <## "contact address: connecting to contact alice"
|
||||||
|
|
||||||
|
let cLinkSchema2 = linkAnotherSchema cLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
|
||||||
|
bob <## "contact address: connecting to contact alice"
|
||||||
|
|
||||||
bob ##> ("/c " <> cLink)
|
bob ##> ("/c " <> cLink)
|
||||||
bob <## "contact address: connecting to contact alice"
|
bob <## "contact address: connecting to contact alice"
|
||||||
|
|
||||||
@ -701,6 +723,10 @@ testPlanAddressContactDeletedReconnected =
|
|||||||
bob ##> ("/_connect plan 1 " <> cLink)
|
bob ##> ("/_connect plan 1 " <> cLink)
|
||||||
bob <## "contact address: ok to connect"
|
bob <## "contact address: ok to connect"
|
||||||
|
|
||||||
|
let cLinkSchema2 = linkAnotherSchema cLink
|
||||||
|
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
|
||||||
|
bob <## "contact address: ok to connect"
|
||||||
|
|
||||||
bob ##> ("/c " <> cLink)
|
bob ##> ("/c " <> cLink)
|
||||||
bob <## "connection request sent!"
|
bob <## "connection request sent!"
|
||||||
alice <## "bob (Bob) wants to connect to you!"
|
alice <## "bob (Bob) wants to connect to you!"
|
||||||
@ -721,6 +747,10 @@ testPlanAddressContactDeletedReconnected =
|
|||||||
bob <## "contact address: known contact alice_1"
|
bob <## "contact address: known contact alice_1"
|
||||||
bob <## "use @alice_1 <message> to send messages"
|
bob <## "use @alice_1 <message> to send messages"
|
||||||
|
|
||||||
|
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
|
||||||
|
bob <## "contact address: known contact alice_1"
|
||||||
|
bob <## "use @alice_1 <message> to send messages"
|
||||||
|
|
||||||
bob ##> ("/c " <> cLink)
|
bob ##> ("/c " <> cLink)
|
||||||
bob <## "contact address: known contact alice_1"
|
bob <## "contact address: known contact alice_1"
|
||||||
bob <## "use @alice_1 <message> to send messages"
|
bob <## "use @alice_1 <message> to send messages"
|
||||||
|
@ -562,3 +562,11 @@ currentChatVRangeInfo =
|
|||||||
|
|
||||||
vRangeStr :: VersionRange -> String
|
vRangeStr :: VersionRange -> String
|
||||||
vRangeStr (VersionRange minVer maxVer) = "(" <> show minVer <> ", " <> show maxVer <> ")"
|
vRangeStr (VersionRange minVer maxVer) = "(" <> show minVer <> ", " <> show maxVer <> ")"
|
||||||
|
|
||||||
|
linkAnotherSchema :: String -> String
|
||||||
|
linkAnotherSchema link
|
||||||
|
| "https://simplex.chat/" `isPrefixOf` link =
|
||||||
|
T.unpack $ T.replace "https://simplex.chat/" "simplex:/" $ T.pack link
|
||||||
|
| "simplex:/" `isPrefixOf` link =
|
||||||
|
T.unpack $ T.replace "simplex:/" "https://simplex.chat/" $ T.pack link
|
||||||
|
| otherwise = error "link starts with neither https://simplex.chat/ nor simplex:/"
|
||||||
|
@ -137,8 +137,8 @@ textColor = describe "text color (red)" do
|
|||||||
uri :: Text -> Markdown
|
uri :: Text -> Markdown
|
||||||
uri = Markdown $ Just Uri
|
uri = Markdown $ Just Uri
|
||||||
|
|
||||||
simplexLink :: SimplexLinkType -> Text -> Bool -> NonEmpty Text -> Text -> Markdown
|
simplexLink :: SimplexLinkType -> Text -> NonEmpty Text -> Text -> Markdown
|
||||||
simplexLink linkType simplexUri trustedUri smpHosts = Markdown $ Just SimplexLink {linkType, simplexUri, trustedUri, smpHosts}
|
simplexLink linkType simplexUri smpHosts = Markdown $ Just SimplexLink {linkType, simplexUri, smpHosts}
|
||||||
|
|
||||||
textWithUri :: Spec
|
textWithUri :: Spec
|
||||||
textWithUri = describe "text with Uri" do
|
textWithUri = describe "text with Uri" do
|
||||||
@ -152,13 +152,13 @@ textWithUri = describe "text with Uri" do
|
|||||||
parseMarkdown "this is _https://simplex.chat" `shouldBe` "this is _https://simplex.chat"
|
parseMarkdown "this is _https://simplex.chat" `shouldBe` "this is _https://simplex.chat"
|
||||||
it "SimpleX links" do
|
it "SimpleX links" do
|
||||||
let inv = "/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
|
let inv = "/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
|
||||||
parseMarkdown ("https://simplex.chat" <> inv) `shouldBe` simplexLink XLInvitation ("simplex:" <> inv) True ["smp.simplex.im"] ("https://simplex.chat" <> inv)
|
parseMarkdown ("https://simplex.chat" <> inv) `shouldBe` simplexLink XLInvitation ("simplex:" <> inv) ["smp.simplex.im"] ("https://simplex.chat" <> inv)
|
||||||
parseMarkdown ("simplex:" <> inv) `shouldBe` simplexLink XLInvitation ("simplex:" <> inv) True ["smp.simplex.im"] ("simplex:" <> inv)
|
parseMarkdown ("simplex:" <> inv) `shouldBe` simplexLink XLInvitation ("simplex:" <> inv) ["smp.simplex.im"] ("simplex:" <> inv)
|
||||||
parseMarkdown ("https://example.com" <> inv) `shouldBe` simplexLink XLInvitation ("simplex:" <> inv) False ["smp.simplex.im"] ("https://example.com" <> inv)
|
parseMarkdown ("https://example.com" <> inv) `shouldBe` simplexLink XLInvitation ("simplex:" <> inv) ["smp.simplex.im"] ("https://example.com" <> inv)
|
||||||
let ct = "/contact#/?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"
|
let ct = "/contact#/?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"
|
||||||
parseMarkdown ("https://simplex.chat" <> ct) `shouldBe` simplexLink XLContact ("simplex:" <> ct) True ["smp.simplex.im"] ("https://simplex.chat" <> ct)
|
parseMarkdown ("https://simplex.chat" <> ct) `shouldBe` simplexLink XLContact ("simplex:" <> ct) ["smp.simplex.im"] ("https://simplex.chat" <> ct)
|
||||||
let gr = "/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FWHV0YU1sYlU7NqiEHkHDB6gxO1ofTync%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAWbebOqVYuBXaiqHcXYjEHCpYi6VzDlu6CVaijDTmsQU%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22mL-7Divb94GGmGmRBef5Dg%3D%3D%22%7D"
|
let gr = "/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FWHV0YU1sYlU7NqiEHkHDB6gxO1ofTync%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAWbebOqVYuBXaiqHcXYjEHCpYi6VzDlu6CVaijDTmsQU%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22mL-7Divb94GGmGmRBef5Dg%3D%3D%22%7D"
|
||||||
parseMarkdown ("https://simplex.chat" <> gr) `shouldBe` simplexLink XLGroup ("simplex:" <> gr) True ["smp4.simplex.im", "o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion"] ("https://simplex.chat" <> gr)
|
parseMarkdown ("https://simplex.chat" <> gr) `shouldBe` simplexLink XLGroup ("simplex:" <> gr) ["smp4.simplex.im", "o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion"] ("https://simplex.chat" <> gr)
|
||||||
|
|
||||||
email :: Text -> Markdown
|
email :: Text -> Markdown
|
||||||
email = Markdown $ Just Email
|
email = Markdown $ Just Email
|
||||||
|
@ -39,7 +39,7 @@ queue =
|
|||||||
connReqData :: ConnReqUriData
|
connReqData :: ConnReqUriData
|
||||||
connReqData =
|
connReqData =
|
||||||
ConnReqUriData
|
ConnReqUriData
|
||||||
{ crScheme = simplexChat,
|
{ crScheme = CRSSimplex,
|
||||||
crAgentVRange = mkVersionRange 1 1,
|
crAgentVRange = mkVersionRange 1 1,
|
||||||
crSmpQueues = [queue],
|
crSmpQueues = [queue],
|
||||||
crClientData = Nothing
|
crClientData = Nothing
|
||||||
@ -184,7 +184,7 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
"{\"v\":\"1\",\"event\":\"x.msg.deleted\",\"params\":{}}"
|
"{\"v\":\"1\",\"event\":\"x.msg.deleted\",\"params\":{}}"
|
||||||
#==# XMsgDeleted
|
#==# XMsgDeleted
|
||||||
it "x.file" $
|
it "x.file" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.file\",\"params\":{\"file\":{\"fileConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.file\",\"params\":{\"file\":{\"fileConnReq\":\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}"
|
||||||
#==# XFile FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileDigest = Nothing, fileConnReq = Just testConnReq, fileInline = Nothing, fileDescr = Nothing}
|
#==# XFile FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileDigest = Nothing, fileConnReq = Just testConnReq, fileInline = Nothing, fileDescr = Nothing}
|
||||||
it "x.file without file invitation" $
|
it "x.file without file invitation" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.file\",\"params\":{\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.file\",\"params\":{\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}"
|
||||||
@ -193,7 +193,7 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
"{\"v\":\"1\",\"event\":\"x.file.acpt\",\"params\":{\"fileName\":\"photo.jpg\"}}"
|
"{\"v\":\"1\",\"event\":\"x.file.acpt\",\"params\":{\"fileName\":\"photo.jpg\"}}"
|
||||||
#==# XFileAcpt "photo.jpg"
|
#==# XFileAcpt "photo.jpg"
|
||||||
it "x.file.acpt.inv" $
|
it "x.file.acpt.inv" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.file.acpt.inv\",\"params\":{\"msgId\":\"AQIDBA==\",\"fileName\":\"photo.jpg\",\"fileConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}"
|
"{\"v\":\"1\",\"event\":\"x.file.acpt.inv\",\"params\":{\"msgId\":\"AQIDBA==\",\"fileName\":\"photo.jpg\",\"fileConnReq\":\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}"
|
||||||
#==# XFileAcptInv (SharedMsgId "\1\2\3\4") (Just testConnReq) "photo.jpg"
|
#==# XFileAcptInv (SharedMsgId "\1\2\3\4") (Just testConnReq) "photo.jpg"
|
||||||
it "x.file.acpt.inv" $
|
it "x.file.acpt.inv" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.file.acpt.inv\",\"params\":{\"msgId\":\"AQIDBA==\",\"fileName\":\"photo.jpg\"}}"
|
"{\"v\":\"1\",\"event\":\"x.file.acpt.inv\",\"params\":{\"msgId\":\"AQIDBA==\",\"fileName\":\"photo.jpg\"}}"
|
||||||
@ -220,10 +220,10 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
"{\"v\":\"1\",\"event\":\"x.contact\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"
|
"{\"v\":\"1\",\"event\":\"x.contact\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"
|
||||||
==# XContact testProfile Nothing
|
==# XContact testProfile Nothing
|
||||||
it "x.grp.inv" $
|
it "x.grp.inv" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.inv\",\"params\":{\"groupInvitation\":{\"connRequest\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"invitedMember\":{\"memberRole\":\"member\",\"memberId\":\"BQYHCA==\"},\"groupProfile\":{\"fullName\":\"Team\",\"displayName\":\"team\",\"groupPreferences\":{\"reactions\":{\"enable\":\"on\"},\"voice\":{\"enable\":\"on\"}}},\"fromMember\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\"}}}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.inv\",\"params\":{\"groupInvitation\":{\"connRequest\":\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"invitedMember\":{\"memberRole\":\"member\",\"memberId\":\"BQYHCA==\"},\"groupProfile\":{\"fullName\":\"Team\",\"displayName\":\"team\",\"groupPreferences\":{\"reactions\":{\"enable\":\"on\"},\"voice\":{\"enable\":\"on\"}}},\"fromMember\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\"}}}}"
|
||||||
#==# XGrpInv GroupInvitation {fromMember = MemberIdRole (MemberId "\1\2\3\4") GRAdmin, invitedMember = MemberIdRole (MemberId "\5\6\7\8") GRMember, connRequest = testConnReq, groupProfile = testGroupProfile, groupLinkId = Nothing}
|
#==# XGrpInv GroupInvitation {fromMember = MemberIdRole (MemberId "\1\2\3\4") GRAdmin, invitedMember = MemberIdRole (MemberId "\5\6\7\8") GRMember, connRequest = testConnReq, groupProfile = testGroupProfile, groupLinkId = Nothing}
|
||||||
it "x.grp.inv with group link id" $
|
it "x.grp.inv with group link id" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.inv\",\"params\":{\"groupInvitation\":{\"connRequest\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"invitedMember\":{\"memberRole\":\"member\",\"memberId\":\"BQYHCA==\"},\"groupProfile\":{\"fullName\":\"Team\",\"displayName\":\"team\",\"groupPreferences\":{\"reactions\":{\"enable\":\"on\"},\"voice\":{\"enable\":\"on\"}}},\"fromMember\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\"}, \"groupLinkId\":\"AQIDBA==\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.inv\",\"params\":{\"groupInvitation\":{\"connRequest\":\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"invitedMember\":{\"memberRole\":\"member\",\"memberId\":\"BQYHCA==\"},\"groupProfile\":{\"fullName\":\"Team\",\"displayName\":\"team\",\"groupPreferences\":{\"reactions\":{\"enable\":\"on\"},\"voice\":{\"enable\":\"on\"}}},\"fromMember\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\"}, \"groupLinkId\":\"AQIDBA==\"}}}"
|
||||||
#==# XGrpInv GroupInvitation {fromMember = MemberIdRole (MemberId "\1\2\3\4") GRAdmin, invitedMember = MemberIdRole (MemberId "\5\6\7\8") GRMember, connRequest = testConnReq, groupProfile = testGroupProfile, groupLinkId = Just $ GroupLinkId "\1\2\3\4"}
|
#==# XGrpInv GroupInvitation {fromMember = MemberIdRole (MemberId "\1\2\3\4") GRAdmin, invitedMember = MemberIdRole (MemberId "\5\6\7\8") GRMember, connRequest = testConnReq, groupProfile = testGroupProfile, groupLinkId = Just $ GroupLinkId "\1\2\3\4"}
|
||||||
it "x.grp.acpt without incognito profile" $
|
it "x.grp.acpt without incognito profile" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.acpt\",\"params\":{\"memberId\":\"AQIDBA==\"}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.acpt\",\"params\":{\"memberId\":\"AQIDBA==\"}}"
|
||||||
@ -241,16 +241,16 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-2\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-2\",\"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}
|
#==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile}
|
||||||
it "x.grp.mem.inv" $
|
it "x.grp.mem.inv" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.inv\",\"params\":{\"memberId\":\"AQIDBA==\",\"memberIntro\":{\"directConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.mem.inv\",\"params\":{\"memberId\":\"AQIDBA==\",\"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%3D1-2%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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}}"
|
||||||
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq}
|
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq}
|
||||||
it "x.grp.mem.inv w/t directConnReq" $
|
it "x.grp.mem.inv w/t directConnReq" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.inv\",\"params\":{\"memberId\":\"AQIDBA==\",\"memberIntro\":{\"groupConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.mem.inv\",\"params\":{\"memberId\":\"AQIDBA==\",\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}}"
|
||||||
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing}
|
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing}
|
||||||
it "x.grp.mem.fwd" $
|
it "x.grp.mem.fwd" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"directConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"https://simplex.chat/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%3D1-2%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\"}}}}}}"
|
"{\"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%3D1-2%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%3D1-2%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}
|
#==# 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" $
|
it "x.grp.mem.fwd with member chat version range and w/t directConnReq" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-2\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
"{\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-2\",\"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}
|
#==# 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" $
|
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\"}}}}}"
|
"{\"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\"}}}}}"
|
||||||
@ -271,10 +271,10 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
"{\"v\":\"1\",\"event\":\"x.grp.del\",\"params\":{}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.del\",\"params\":{}}"
|
||||||
==# XGrpDel
|
==# XGrpDel
|
||||||
it "x.grp.direct.inv" $
|
it "x.grp.direct.inv" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.direct.inv\",\"params\":{\"connReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\", \"content\":{\"text\":\"hello\",\"type\":\"text\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.direct.inv\",\"params\":{\"connReq\":\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\", \"content\":{\"text\":\"hello\",\"type\":\"text\"}}}"
|
||||||
#==# XGrpDirectInv testConnReq (Just $ MCText "hello")
|
#==# XGrpDirectInv testConnReq (Just $ MCText "hello")
|
||||||
it "x.grp.direct.inv without content" $
|
it "x.grp.direct.inv without content" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.grp.direct.inv\",\"params\":{\"connReq\":\"https://simplex.chat/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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}"
|
"{\"v\":\"1\",\"event\":\"x.grp.direct.inv\",\"params\":{\"connReq\":\"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%3D1-2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}"
|
||||||
#==# XGrpDirectInv testConnReq Nothing
|
#==# XGrpDirectInv testConnReq Nothing
|
||||||
it "x.info.probe" $
|
it "x.info.probe" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.info.probe\",\"params\":{\"probe\":\"AQIDBA==\"}}"
|
"{\"v\":\"1\",\"event\":\"x.info.probe\",\"params\":{\"probe\":\"AQIDBA==\"}}"
|
||||||
|
@ -152,7 +152,7 @@
|
|||||||
v1.0.0+, {{ "copy-the-command-below-text" | i18n({}, lang ) | safe }}
|
v1.0.0+, {{ "copy-the-command-below-text" | i18n({}, lang ) | safe }}
|
||||||
</p>
|
</p>
|
||||||
<p class="bg-white flex items-center justify-between rounded p-3 shadow-[inset_0px_2px_2px_rgba(0,0,0,0.15)] mb-[36px]">
|
<p class="bg-white flex items-center justify-between rounded p-3 shadow-[inset_0px_2px_2px_rgba(0,0,0,0.15)] mb-[36px]">
|
||||||
<span id="conn_req_uri_text" class="text-grey-black font-light text-[14px] leading-6">/c https://simplex.chat/contact#/?v=1&smp=smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im/KBCmxJ3-lEjpWLPPkI6OWPk-YJneU5uY%23MCowBQYDK2VuAyEAtixHJWDXvYWcoe-77vIfjvI6XWEuzUsapMS9nVHP_Go=</span>
|
<span id="conn_req_uri_text" class="text-grey-black font-light text-[14px] leading-6"></span>
|
||||||
<!-- <img class="content_copy" src="/img/new/content-copy.svg" /> -->
|
<!-- <img class="content_copy" src="/img/new/content-copy.svg" /> -->
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user