core: update api (#3221)
This commit is contained in:
parent
f5e9bd4f8b
commit
41b86e07f1
@ -886,9 +886,9 @@ func startRemoteCtrl() async throws {
|
|||||||
try await sendCommandOkResp(.startRemoteCtrl)
|
try await sendCommandOkResp(.startRemoteCtrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerRemoteCtrl(_ remoteCtrlOOB: RemoteCtrlOOB) async throws -> Int64 {
|
func registerRemoteCtrl(_ remoteCtrlOOB: RemoteCtrlOOB) async throws -> RemoteCtrlInfo {
|
||||||
let r = await chatSendCmd(.registerRemoteCtrl(remoteCtrlOOB: remoteCtrlOOB))
|
let r = await chatSendCmd(.registerRemoteCtrl(remoteCtrlOOB: remoteCtrlOOB))
|
||||||
if case let .remoteCtrlRegistered(rcId) = r { return rcId }
|
if case let .remoteCtrlRegistered(rcInfo) = r { return rcInfo }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ public enum ChatCommand {
|
|||||||
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 startRemoteCtrl
|
case startRemoteCtrl
|
||||||
case registerRemoteCtrl(remoteCtrlOOB: RemoteCtrlOOB)
|
case registerRemoteCtrl(remoteCtrlOOB: RemoteCtrlOOB)
|
||||||
case listRemoteCtrls
|
case listRemoteCtrls
|
||||||
@ -262,6 +263,7 @@ public enum ChatCommand {
|
|||||||
return s
|
return s
|
||||||
case let .setFileToReceive(fileId, encrypted): return "/_set_file_to_receive \(fileId) encrypt=\(onOff(encrypted))"
|
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 .startRemoteCtrl: return "/start remote ctrl"
|
case .startRemoteCtrl: return "/start remote ctrl"
|
||||||
case let .registerRemoteCtrl(oob): return "/register remote ctrl \(oob.caFingerprint)"
|
case let .registerRemoteCtrl(oob): return "/register remote ctrl \(oob.caFingerprint)"
|
||||||
case let .acceptRemoteCtrl(rcId): return "/accept remote ctrl \(rcId)"
|
case let .acceptRemoteCtrl(rcId): return "/accept remote ctrl \(rcId)"
|
||||||
@ -381,6 +383,7 @@ public enum ChatCommand {
|
|||||||
case .receiveFile: return "receiveFile"
|
case .receiveFile: return "receiveFile"
|
||||||
case .setFileToReceive: return "setFileToReceive"
|
case .setFileToReceive: return "setFileToReceive"
|
||||||
case .cancelFile: return "cancelFile"
|
case .cancelFile: return "cancelFile"
|
||||||
|
case .setLocalDeviceName: return "setLocalDeviceName"
|
||||||
case .startRemoteCtrl: return "startRemoteCtrl"
|
case .startRemoteCtrl: return "startRemoteCtrl"
|
||||||
case .registerRemoteCtrl: return "registerRemoteCtrl"
|
case .registerRemoteCtrl: return "registerRemoteCtrl"
|
||||||
case .listRemoteCtrls: return "listRemoteCtrls"
|
case .listRemoteCtrls: return "listRemoteCtrls"
|
||||||
@ -585,11 +588,11 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case newContactConnection(user: UserRef, connection: PendingContactConnection)
|
case newContactConnection(user: UserRef, connection: PendingContactConnection)
|
||||||
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
||||||
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
|
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
|
||||||
case remoteCtrlRegistered(remoteCtrlId: Int64)
|
case remoteCtrlRegistered(remoteCtrl: RemoteCtrlInfo)
|
||||||
case remoteCtrlAnnounce(fingerprint: String)
|
case remoteCtrlAnnounce(fingerprint: String)
|
||||||
case remoteCtrlFound(remoteCtrl: RemoteCtrl)
|
case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo)
|
||||||
case remoteCtrlConnecting(remoteCtrlId: Int64, displayName: String)
|
case remoteCtrlConnecting(remoteCtrl: RemoteCtrlInfo)
|
||||||
case remoteCtrlConnected(remoteCtrlId: Int64, displayName: String)
|
case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo)
|
||||||
case remoteCtrlStopped
|
case remoteCtrlStopped
|
||||||
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
|
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
|
||||||
case cmdOk(user: UserRef?)
|
case cmdOk(user: UserRef?)
|
||||||
@ -874,11 +877,11 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case let .newContactConnection(u, connection): return withUser(u, String(describing: connection))
|
case let .newContactConnection(u, connection): return withUser(u, String(describing: connection))
|
||||||
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
||||||
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
|
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
|
||||||
case let .remoteCtrlRegistered(rcId): return "remote ctrl ID: \(rcId)"
|
case let .remoteCtrlRegistered(remoteCtrl): return String(describing: remoteCtrl)
|
||||||
case let .remoteCtrlAnnounce(fingerprint): return "fingerprint: \(fingerprint)"
|
case let .remoteCtrlAnnounce(fingerprint): return "fingerprint: \(fingerprint)"
|
||||||
case let .remoteCtrlFound(remoteCtrl): return "remote ctrl: \(String(describing: remoteCtrl))"
|
case let .remoteCtrlFound(remoteCtrl): return String(describing: remoteCtrl)
|
||||||
case let .remoteCtrlConnecting(rcId, displayName): return "remote ctrl ID: \(rcId)\nhost displayName: \(displayName)"
|
case let .remoteCtrlConnecting(remoteCtrl): return String(describing: remoteCtrl)
|
||||||
case let .remoteCtrlConnected(rcId, displayName): return "remote ctrl ID: \(rcId)\nhost displayName: \(displayName)"
|
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
|
||||||
case .remoteCtrlStopped: return noDetails
|
case .remoteCtrlStopped: return noDetails
|
||||||
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
|
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
|
||||||
case .cmdOk: return noDetails
|
case .cmdOk: return noDetails
|
||||||
|
@ -166,6 +166,7 @@ class AppPreferences {
|
|||||||
val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null)
|
val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null)
|
||||||
val lastMigratedVersionCode = mkIntPreference(SHARED_PREFS_LAST_MIGRATED_VERSION_CODE, 0)
|
val lastMigratedVersionCode = mkIntPreference(SHARED_PREFS_LAST_MIGRATED_VERSION_CODE, 0)
|
||||||
val customDisappearingMessageTime = mkIntPreference(SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME, 300)
|
val customDisappearingMessageTime = mkIntPreference(SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME, 300)
|
||||||
|
val deviceNameForRemoteAccess = mkStrPreference(SHARED_PREFS_DEVICE_NAME_FOR_REMOTE_ACCESS, "Desktop")
|
||||||
|
|
||||||
private fun mkIntPreference(prefName: String, default: Int) =
|
private fun mkIntPreference(prefName: String, default: Int) =
|
||||||
SharedPreference(
|
SharedPreference(
|
||||||
@ -306,6 +307,7 @@ class AppPreferences {
|
|||||||
private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion"
|
private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion"
|
||||||
private const val SHARED_PREFS_LAST_MIGRATED_VERSION_CODE = "LastMigratedVersionCode"
|
private const val SHARED_PREFS_LAST_MIGRATED_VERSION_CODE = "LastMigratedVersionCode"
|
||||||
private const val SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME = "CustomDisappearingMessageTime"
|
private const val SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME = "CustomDisappearingMessageTime"
|
||||||
|
private const val SHARED_PREFS_DEVICE_NAME_FOR_REMOTE_ACCESS = "DeviceNameForRemoteAccess"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +344,11 @@ object ChatController {
|
|||||||
val users = listUsers()
|
val users = listUsers()
|
||||||
chatModel.users.clear()
|
chatModel.users.clear()
|
||||||
chatModel.users.addAll(users)
|
chatModel.users.addAll(users)
|
||||||
|
val remoteHosts = listRemoteHosts()
|
||||||
|
if (remoteHosts != null) {
|
||||||
|
chatModel.remoteHosts.clear()
|
||||||
|
chatModel.remoteHosts.addAll(remoteHosts)
|
||||||
|
}
|
||||||
if (justStarted) {
|
if (justStarted) {
|
||||||
chatModel.currentUser.value = user
|
chatModel.currentUser.value = user
|
||||||
chatModel.userCreated.value = true
|
chatModel.userCreated.value = true
|
||||||
@ -432,15 +439,16 @@ object ChatController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recvMsg(ctrl: ChatCtrl): CR? {
|
private fun recvMsg(ctrl: ChatCtrl): APIResponse? {
|
||||||
val json = chatRecvMsgWait(ctrl, MESSAGE_TIMEOUT)
|
val json = chatRecvMsgWait(ctrl, MESSAGE_TIMEOUT)
|
||||||
return if (json == "") {
|
return if (json == "") {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
val r = APIResponse.decodeStr(json).resp
|
val apiResp = APIResponse.decodeStr(json)
|
||||||
|
val r = apiResp.resp
|
||||||
Log.d(TAG, "chatRecvMsg: ${r.responseType}")
|
Log.d(TAG, "chatRecvMsg: ${r.responseType}")
|
||||||
if (r is CR.Response || r is CR.Invalid) Log.d(TAG, "chatRecvMsg json: $json")
|
if (r is CR.Response || r is CR.Invalid) Log.d(TAG, "chatRecvMsg json: $json")
|
||||||
r
|
apiResp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1327,6 +1335,59 @@ object ChatController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setLocalDeviceName(displayName: String): Boolean = sendCommandOkResp(CC.SetLocalDeviceName(displayName))
|
||||||
|
|
||||||
|
suspend fun createRemoteHost(): RemoteHostInfo? {
|
||||||
|
val r = sendCmd(CC.CreateRemoteHost())
|
||||||
|
if (r is CR.RemoteHostCreated) return r.remoteHost
|
||||||
|
apiErrorAlert("createRemoteHost", generalGetString(MR.strings.error), r)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun listRemoteHosts(): List<RemoteHostInfo>? {
|
||||||
|
val r = sendCmd(CC.ListRemoteHosts())
|
||||||
|
if (r is CR.RemoteHostList) return r.remoteHosts
|
||||||
|
apiErrorAlert("listRemoteHosts", generalGetString(MR.strings.error), r)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun startRemoteHost(rhId: Long): Boolean = sendCommandOkResp(CC.StartRemoteHost(rhId))
|
||||||
|
|
||||||
|
suspend fun registerRemoteCtrl(oob: RemoteCtrlOOB): RemoteCtrlInfo? {
|
||||||
|
val r = sendCmd(CC.RegisterRemoteCtrl(oob))
|
||||||
|
if (r is CR.RemoteCtrlRegistered) return r.remoteCtrl
|
||||||
|
apiErrorAlert("registerRemoteCtrl", generalGetString(MR.strings.error), r)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun listRemoteCtrls(): List<RemoteCtrlInfo>? {
|
||||||
|
val r = sendCmd(CC.ListRemoteCtrls())
|
||||||
|
if (r is CR.RemoteCtrlList) return r.remoteCtrls
|
||||||
|
apiErrorAlert("listRemoteCtrls", generalGetString(MR.strings.error), r)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun stopRemoteHost(rhId: Long): Boolean = sendCommandOkResp(CC.StopRemoteHost(rhId))
|
||||||
|
|
||||||
|
suspend fun deleteRemoteHost(rhId: Long): Boolean = sendCommandOkResp(CC.DeleteRemoteHost(rhId))
|
||||||
|
|
||||||
|
suspend fun startRemoteCtrl(): Boolean = sendCommandOkResp(CC.StartRemoteCtrl())
|
||||||
|
|
||||||
|
suspend fun acceptRemoteCtrl(rcId: Long): Boolean = sendCommandOkResp(CC.AcceptRemoteCtrl(rcId))
|
||||||
|
|
||||||
|
suspend fun rejectRemoteCtrl(rcId: Long): Boolean = sendCommandOkResp(CC.RejectRemoteCtrl(rcId))
|
||||||
|
|
||||||
|
suspend fun stopRemoteCtrl(): Boolean = sendCommandOkResp(CC.StopRemoteCtrl())
|
||||||
|
|
||||||
|
suspend fun deleteRemoteCtrl(rcId: Long): Boolean = sendCommandOkResp(CC.DeleteRemoteCtrl(rcId))
|
||||||
|
|
||||||
|
private suspend fun sendCommandOkResp(cmd: CC): Boolean {
|
||||||
|
val r = sendCmd(cmd)
|
||||||
|
val ok = r is CR.CmdOk
|
||||||
|
if (!ok) apiErrorAlert(cmd.cmdType, generalGetString(MR.strings.error), r)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun apiGetVersion(): CoreVersionInfo? {
|
suspend fun apiGetVersion(): CoreVersionInfo? {
|
||||||
val r = sendCmd(CC.ShowVersion())
|
val r = sendCmd(CC.ShowVersion())
|
||||||
return if (r is CR.VersionInfo) {
|
return if (r is CR.VersionInfo) {
|
||||||
@ -1361,14 +1422,15 @@ object ChatController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun apiErrorAlert(method: String, title: String, r: CR) {
|
private fun apiErrorAlert(method: String, title: String, r: CR) {
|
||||||
val errMsg = "${r.responseType}: ${r.details}"
|
val errMsg = "${r.responseType}: ${r.details}"
|
||||||
Log.e(TAG, "$method bad response: $errMsg")
|
Log.e(TAG, "$method bad response: $errMsg")
|
||||||
AlertManager.shared.showAlertMsg(title, errMsg)
|
AlertManager.shared.showAlertMsg(title, errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun processReceivedMsg(r: CR) {
|
private suspend fun processReceivedMsg(apiResp: APIResponse) {
|
||||||
lastMsgReceivedTimestamp = System.currentTimeMillis()
|
lastMsgReceivedTimestamp = System.currentTimeMillis()
|
||||||
|
val r = apiResp.resp
|
||||||
chatModel.addTerminalItem(TerminalItem.resp(r))
|
chatModel.addTerminalItem(TerminalItem.resp(r))
|
||||||
when (r) {
|
when (r) {
|
||||||
is CR.NewContactConnection -> {
|
is CR.NewContactConnection -> {
|
||||||
@ -1674,6 +1736,13 @@ object ChatController {
|
|||||||
chatModel.updateContactConnectionStats(r.contact, r.ratchetSyncProgress.connectionStats)
|
chatModel.updateContactConnectionStats(r.contact, r.ratchetSyncProgress.connectionStats)
|
||||||
is CR.GroupMemberRatchetSync ->
|
is CR.GroupMemberRatchetSync ->
|
||||||
chatModel.updateGroupMemberConnectionStats(r.groupInfo, r.member, r.ratchetSyncProgress.connectionStats)
|
chatModel.updateGroupMemberConnectionStats(r.groupInfo, r.member, r.ratchetSyncProgress.connectionStats)
|
||||||
|
is CR.RemoteHostConnected -> {
|
||||||
|
// update
|
||||||
|
chatModel.connectingRemoteHost.value = r.remoteHost
|
||||||
|
}
|
||||||
|
is CR.RemoteHostStopped -> {
|
||||||
|
//
|
||||||
|
}
|
||||||
else ->
|
else ->
|
||||||
Log.d(TAG , "unsupported event: ${r.responseType}")
|
Log.d(TAG , "unsupported event: ${r.responseType}")
|
||||||
}
|
}
|
||||||
@ -1933,6 +2002,7 @@ sealed class 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 encrypted: Boolean, val inline: Boolean?): CC()
|
||||||
class CancelFile(val fileId: Long): CC()
|
class CancelFile(val fileId: Long): CC()
|
||||||
|
class SetLocalDeviceName(val displayName: String): CC()
|
||||||
class CreateRemoteHost(): CC()
|
class CreateRemoteHost(): CC()
|
||||||
class ListRemoteHosts(): CC()
|
class ListRemoteHosts(): CC()
|
||||||
class StartRemoteHost(val remoteHostId: Long): CC()
|
class StartRemoteHost(val remoteHostId: Long): CC()
|
||||||
@ -2053,13 +2123,14 @@ sealed class CC {
|
|||||||
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 encrypt=${onOff(encrypted)}" + (if (inline == null) "" else " inline=${onOff(inline)}")
|
||||||
is CancelFile -> "/fcancel $fileId"
|
is CancelFile -> "/fcancel $fileId"
|
||||||
|
is SetLocalDeviceName -> "/set device name $displayName"
|
||||||
is CreateRemoteHost -> "/create remote host"
|
is CreateRemoteHost -> "/create remote host"
|
||||||
is ListRemoteHosts -> "/list remote hosts"
|
is ListRemoteHosts -> "/list remote hosts"
|
||||||
is StartRemoteHost -> "/start remote host $remoteHostId"
|
is StartRemoteHost -> "/start remote host $remoteHostId"
|
||||||
is StopRemoteHost -> "/stop remote host $remoteHostId"
|
is StopRemoteHost -> "/stop remote host $remoteHostId"
|
||||||
is DeleteRemoteHost -> "/delete remote host $remoteHostId"
|
is DeleteRemoteHost -> "/delete remote host $remoteHostId"
|
||||||
is StartRemoteCtrl -> "/start remote ctrl"
|
is StartRemoteCtrl -> "/start remote ctrl"
|
||||||
is RegisterRemoteCtrl -> "/register remote ctrl ${remoteCtrlOOB.caFingerprint}"
|
is RegisterRemoteCtrl -> "/register remote ctrl ${remoteCtrlOOB.fingerprint}"
|
||||||
is AcceptRemoteCtrl -> "/accept remote ctrl $remoteCtrlId"
|
is AcceptRemoteCtrl -> "/accept remote ctrl $remoteCtrlId"
|
||||||
is RejectRemoteCtrl -> "/reject remote ctrl $remoteCtrlId"
|
is RejectRemoteCtrl -> "/reject remote ctrl $remoteCtrlId"
|
||||||
is ListRemoteCtrls -> "/list remote ctrls"
|
is ListRemoteCtrls -> "/list remote ctrls"
|
||||||
@ -2162,6 +2233,7 @@ sealed class CC {
|
|||||||
is ApiChatUnread -> "apiChatUnread"
|
is ApiChatUnread -> "apiChatUnread"
|
||||||
is ReceiveFile -> "receiveFile"
|
is ReceiveFile -> "receiveFile"
|
||||||
is CancelFile -> "cancelFile"
|
is CancelFile -> "cancelFile"
|
||||||
|
is SetLocalDeviceName -> "setLocalDeviceName"
|
||||||
is CreateRemoteHost -> "createRemoteHost"
|
is CreateRemoteHost -> "createRemoteHost"
|
||||||
is ListRemoteHosts -> "listRemoteHosts"
|
is ListRemoteHosts -> "listRemoteHosts"
|
||||||
is StartRemoteHost -> "startRemoteHost"
|
is StartRemoteHost -> "startRemoteHost"
|
||||||
@ -3246,7 +3318,8 @@ data class RemoteCtrl (
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class RemoteCtrlOOB (
|
data class RemoteCtrlOOB (
|
||||||
val caFingerprint: String
|
val fingerprint: String,
|
||||||
|
val displayName: String
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -3261,6 +3334,7 @@ data class RemoteHostInfo (
|
|||||||
val remoteHostId: Long,
|
val remoteHostId: Long,
|
||||||
val storePath: String,
|
val storePath: String,
|
||||||
val displayName: String,
|
val displayName: String,
|
||||||
|
val remoteCtrlOOB: RemoteCtrlOOB,
|
||||||
val sessionActive: Boolean
|
val sessionActive: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -3277,7 +3351,7 @@ val yaml = Yaml(configuration = YamlConfiguration(
|
|||||||
))
|
))
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class APIResponse(val resp: CR, val corr: String? = null) {
|
class APIResponse(val resp: CR, val remoteHostId: Long?, val corr: String? = null) {
|
||||||
companion object {
|
companion object {
|
||||||
fun decodeStr(str: String): APIResponse {
|
fun decodeStr(str: String): APIResponse {
|
||||||
return try {
|
return try {
|
||||||
@ -3287,48 +3361,35 @@ class APIResponse(val resp: CR, val corr: String? = null) {
|
|||||||
Log.d(TAG, e.localizedMessage ?: "")
|
Log.d(TAG, e.localizedMessage ?: "")
|
||||||
val data = json.parseToJsonElement(str).jsonObject
|
val data = json.parseToJsonElement(str).jsonObject
|
||||||
val resp = data["resp"]!!.jsonObject
|
val resp = data["resp"]!!.jsonObject
|
||||||
val type = resp["type"]?.jsonPrimitive?.content ?: "invalid"
|
val type = resp["type"]?.jsonPrimitive?.contentOrNull ?: "invalid"
|
||||||
|
val corr = data["corr"]?.toString()
|
||||||
|
val remoteHostId = data["remoteHostId"]?.jsonPrimitive?.longOrNull
|
||||||
try {
|
try {
|
||||||
if (type == "apiChats") {
|
if (type == "apiChats") {
|
||||||
val user: UserRef = json.decodeFromJsonElement(resp["user"]!!.jsonObject)
|
val user: UserRef = json.decodeFromJsonElement(resp["user"]!!.jsonObject)
|
||||||
val chats: List<Chat> = resp["chats"]!!.jsonArray.map {
|
val chats: List<Chat> = resp["chats"]!!.jsonArray.map {
|
||||||
parseChatData(it)
|
parseChatData(it)
|
||||||
}
|
}
|
||||||
return APIResponse(
|
return APIResponse(CR.ApiChats(user, chats), remoteHostId, corr)
|
||||||
resp = CR.ApiChats(user, chats),
|
|
||||||
corr = data["corr"]?.toString()
|
|
||||||
)
|
|
||||||
} else if (type == "apiChat") {
|
} else if (type == "apiChat") {
|
||||||
val user: UserRef = json.decodeFromJsonElement(resp["user"]!!.jsonObject)
|
val user: UserRef = json.decodeFromJsonElement(resp["user"]!!.jsonObject)
|
||||||
val chat = parseChatData(resp["chat"]!!)
|
val chat = parseChatData(resp["chat"]!!)
|
||||||
return APIResponse(
|
return APIResponse(CR.ApiChat(user, chat), remoteHostId, corr)
|
||||||
resp = CR.ApiChat(user, chat),
|
|
||||||
corr = data["corr"]?.toString()
|
|
||||||
)
|
|
||||||
} else if (type == "chatCmdError") {
|
} else if (type == "chatCmdError") {
|
||||||
val userObject = resp["user_"]?.jsonObject
|
val userObject = resp["user_"]?.jsonObject
|
||||||
val user = runCatching<UserRef?> { json.decodeFromJsonElement(userObject!!) }.getOrNull()
|
val user = runCatching<UserRef?> { json.decodeFromJsonElement(userObject!!) }.getOrNull()
|
||||||
return APIResponse(
|
return APIResponse(CR.ChatCmdError(user, ChatError.ChatErrorInvalidJSON(json.encodeToString(resp["chatError"]))), remoteHostId, corr)
|
||||||
resp = CR.ChatCmdError(user, ChatError.ChatErrorInvalidJSON(json.encodeToString(resp["chatError"]))),
|
|
||||||
corr = data["corr"]?.toString()
|
|
||||||
)
|
|
||||||
} else if (type == "chatError") {
|
} else if (type == "chatError") {
|
||||||
val userObject = resp["user_"]?.jsonObject
|
val userObject = resp["user_"]?.jsonObject
|
||||||
val user = runCatching<UserRef?> { json.decodeFromJsonElement(userObject!!) }.getOrNull()
|
val user = runCatching<UserRef?> { json.decodeFromJsonElement(userObject!!) }.getOrNull()
|
||||||
return APIResponse(
|
return APIResponse(CR.ChatRespError(user, ChatError.ChatErrorInvalidJSON(json.encodeToString(resp["chatError"]))), remoteHostId, corr)
|
||||||
resp = CR.ChatRespError(user, ChatError.ChatErrorInvalidJSON(json.encodeToString(resp["chatError"]))),
|
|
||||||
corr = data["corr"]?.toString()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error while parsing chat(s): " + e.stackTraceToString())
|
Log.e(TAG, "Error while parsing chat(s): " + e.stackTraceToString())
|
||||||
}
|
}
|
||||||
APIResponse(
|
APIResponse(CR.Response(type, json.encodeToString(data)), remoteHostId, corr)
|
||||||
resp = CR.Response(type, json.encodeToString(data)),
|
|
||||||
corr = data["corr"]?.toString()
|
|
||||||
)
|
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
APIResponse(CR.Invalid(str))
|
APIResponse(CR.Invalid(str), remoteHostId = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3484,17 +3545,17 @@ sealed class CR {
|
|||||||
@Serializable @SerialName("newContactConnection") class NewContactConnection(val user: UserRef, val connection: PendingContactConnection): CR()
|
@Serializable @SerialName("newContactConnection") class NewContactConnection(val user: UserRef, val connection: PendingContactConnection): CR()
|
||||||
@Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val user: UserRef, val connection: PendingContactConnection): CR()
|
@Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val user: UserRef, val connection: PendingContactConnection): CR()
|
||||||
// remote events (desktop)
|
// remote events (desktop)
|
||||||
@Serializable @SerialName("remoteHostCreated") class RemoteHostCreated(val remoteHostId: Long, val oobData: RemoteCtrlOOB): CR()
|
@Serializable @SerialName("remoteHostCreated") class RemoteHostCreated(val remoteHost: RemoteHostInfo): CR()
|
||||||
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
|
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
|
||||||
@Serializable @SerialName("remoteHostConnected") class RemoteHostConnected(val remoteHostId: Long): CR()
|
@Serializable @SerialName("remoteHostConnected") class RemoteHostConnected(val remoteHost: RemoteHostInfo): CR()
|
||||||
@Serializable @SerialName("remoteHostStopped") class RemoteHostStopped(val remoteHostId: Long): CR()
|
@Serializable @SerialName("remoteHostStopped") class RemoteHostStopped(val remoteHostId: Long): CR()
|
||||||
// remote events (mobile)
|
// remote events (mobile)
|
||||||
@Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List<RemoteCtrlInfo>): CR()
|
@Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List<RemoteCtrlInfo>): CR()
|
||||||
@Serializable @SerialName("remoteCtrlRegistered") class RemoteCtrlRegistered(val remoteCtrlId: Long): CR()
|
@Serializable @SerialName("remoteCtrlRegistered") class RemoteCtrlRegistered(val remoteCtrl: RemoteCtrlInfo): CR()
|
||||||
@Serializable @SerialName("remoteCtrlAnnounce") class RemoteCtrlAnnounce(val fingerprint: String): CR()
|
@Serializable @SerialName("remoteCtrlAnnounce") class RemoteCtrlAnnounce(val fingerprint: String): CR()
|
||||||
@Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrl): CR()
|
@Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo): CR()
|
||||||
@Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrlId: Long, val displayName: String): CR()
|
@Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrl: RemoteCtrlInfo): CR()
|
||||||
@Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrlId: Long, val displayName: String): CR()
|
@Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrl: RemoteCtrlInfo): CR()
|
||||||
@Serializable @SerialName("remoteCtrlStopped") class RemoteCtrlStopped(): CR()
|
@Serializable @SerialName("remoteCtrlStopped") class RemoteCtrlStopped(): CR()
|
||||||
@Serializable @SerialName("versionInfo") class VersionInfo(val versionInfo: CoreVersionInfo, val chatMigrations: List<UpMigration>, val agentMigrations: List<UpMigration>): CR()
|
@Serializable @SerialName("versionInfo") class VersionInfo(val versionInfo: CoreVersionInfo, val chatMigrations: List<UpMigration>, val agentMigrations: List<UpMigration>): CR()
|
||||||
@Serializable @SerialName("cmdOk") class CmdOk(val user: UserRef?): CR()
|
@Serializable @SerialName("cmdOk") class CmdOk(val user: UserRef?): CR()
|
||||||
@ -3767,17 +3828,17 @@ sealed class CR {
|
|||||||
is CallEnded -> withUser(user, "contact: ${contact.id}")
|
is CallEnded -> withUser(user, "contact: ${contact.id}")
|
||||||
is NewContactConnection -> withUser(user, json.encodeToString(connection))
|
is NewContactConnection -> withUser(user, json.encodeToString(connection))
|
||||||
is ContactConnectionDeleted -> withUser(user, json.encodeToString(connection))
|
is ContactConnectionDeleted -> withUser(user, json.encodeToString(connection))
|
||||||
is RemoteHostCreated -> "remote host ID: $remoteHostId\noobData ${json.encodeToString(oobData)}"
|
is RemoteHostCreated -> json.encodeToString(remoteHost)
|
||||||
is RemoteHostList -> "remote hosts: ${json.encodeToString(remoteHosts)}"
|
is RemoteHostList -> json.encodeToString(remoteHosts)
|
||||||
is RemoteHostConnected -> "remote host ID: $remoteHostId"
|
is RemoteHostConnected -> json.encodeToString(remoteHost)
|
||||||
is RemoteHostStopped -> "remote host ID: $remoteHostId"
|
is RemoteHostStopped -> "remote host ID: $remoteHostId"
|
||||||
is RemoteCtrlList -> json.encodeToString(remoteCtrls)
|
is RemoteCtrlList -> json.encodeToString(remoteCtrls)
|
||||||
is RemoteCtrlRegistered -> "remote ctrl ID: $remoteCtrlId"
|
is RemoteCtrlRegistered -> json.encodeToString(remoteCtrl)
|
||||||
is RemoteCtrlAnnounce -> "fingerprint: $fingerprint"
|
is RemoteCtrlAnnounce -> "fingerprint: $fingerprint"
|
||||||
is RemoteCtrlFound -> "remote ctrl: ${json.encodeToString(remoteCtrl)}"
|
is RemoteCtrlFound -> json.encodeToString(remoteCtrl)
|
||||||
is RemoteCtrlConnecting -> "remote ctrl ID: $remoteCtrlId\nhost displayName: $displayName"
|
is RemoteCtrlConnecting -> json.encodeToString(remoteCtrl)
|
||||||
is RemoteCtrlConnected -> "remote ctrl ID: $remoteCtrlId\nhost displayName: $displayName"
|
is RemoteCtrlConnected -> json.encodeToString(remoteCtrl)
|
||||||
is RemoteCtrlStopped -> ""
|
is RemoteCtrlStopped -> noDetails()
|
||||||
is VersionInfo -> "version ${json.encodeToString(versionInfo)}\n\n" +
|
is VersionInfo -> "version ${json.encodeToString(versionInfo)}\n\n" +
|
||||||
"chat migrations: ${json.encodeToString(chatMigrations.map { it.upName })}\n\n" +
|
"chat migrations: ${json.encodeToString(chatMigrations.map { it.upName })}\n\n" +
|
||||||
"agent migrations: ${json.encodeToString(agentMigrations.map { it.upName })}"
|
"agent migrations: ${json.encodeToString(agentMigrations.map { it.upName })}"
|
||||||
|
@ -70,6 +70,7 @@ import Simplex.Chat.Store.Files
|
|||||||
import Simplex.Chat.Store.Groups
|
import Simplex.Chat.Store.Groups
|
||||||
import Simplex.Chat.Store.Messages
|
import Simplex.Chat.Store.Messages
|
||||||
import Simplex.Chat.Store.Profiles
|
import Simplex.Chat.Store.Profiles
|
||||||
|
import Simplex.Chat.Store.Remote
|
||||||
import Simplex.Chat.Store.Shared
|
import Simplex.Chat.Store.Shared
|
||||||
import Simplex.Chat.Types
|
import Simplex.Chat.Types
|
||||||
import Simplex.Chat.Types.Preferences
|
import Simplex.Chat.Types.Preferences
|
||||||
@ -1900,7 +1901,7 @@ processChatCommand = \case
|
|||||||
StopRemoteHost rh -> closeRemoteHostSession rh >> ok_
|
StopRemoteHost rh -> closeRemoteHostSession rh >> ok_
|
||||||
DeleteRemoteHost rh -> deleteRemoteHost rh >> ok_
|
DeleteRemoteHost rh -> deleteRemoteHost rh >> ok_
|
||||||
StartRemoteCtrl -> startRemoteCtrl (execChatCommand Nothing) >> ok_
|
StartRemoteCtrl -> startRemoteCtrl (execChatCommand Nothing) >> ok_
|
||||||
RegisterRemoteCtrl oob -> CRRemoteCtrlRegistered <$> registerRemoteCtrl oob
|
RegisterRemoteCtrl oob -> CRRemoteCtrlRegistered <$> withStore' (`insertRemoteCtrl` oob)
|
||||||
AcceptRemoteCtrl rc -> acceptRemoteCtrl rc >> ok_
|
AcceptRemoteCtrl rc -> acceptRemoteCtrl rc >> ok_
|
||||||
RejectRemoteCtrl rc -> rejectRemoteCtrl rc >> ok_
|
RejectRemoteCtrl rc -> rejectRemoteCtrl rc >> ok_
|
||||||
StopRemoteCtrl -> stopRemoteCtrl >> ok_
|
StopRemoteCtrl -> stopRemoteCtrl >> ok_
|
||||||
|
@ -633,14 +633,14 @@ data ChatResponse
|
|||||||
| CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection}
|
| CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection}
|
||||||
| CRRemoteHostCreated {remoteHost :: RemoteHostInfo}
|
| CRRemoteHostCreated {remoteHost :: RemoteHostInfo}
|
||||||
| CRRemoteHostList {remoteHosts :: [RemoteHostInfo]}
|
| CRRemoteHostList {remoteHosts :: [RemoteHostInfo]}
|
||||||
| CRRemoteHostConnected {remoteHostId :: RemoteHostId} -- TODO add displayName
|
| CRRemoteHostConnected {remoteHost :: RemoteHostInfo}
|
||||||
| CRRemoteHostStopped {remoteHostId :: RemoteHostId}
|
| CRRemoteHostStopped {remoteHostId :: RemoteHostId}
|
||||||
| CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]}
|
| CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]}
|
||||||
| CRRemoteCtrlRegistered {remoteCtrlId :: RemoteCtrlId}
|
| CRRemoteCtrlRegistered {remoteCtrl :: RemoteCtrlInfo}
|
||||||
| CRRemoteCtrlAnnounce {fingerprint :: C.KeyHash} -- unregistered fingerprint, needs confirmation
|
| CRRemoteCtrlAnnounce {fingerprint :: C.KeyHash} -- unregistered fingerprint, needs confirmation
|
||||||
| CRRemoteCtrlFound {remoteCtrl :: RemoteCtrl} -- registered fingerprint, may connect
|
| CRRemoteCtrlFound {remoteCtrl :: RemoteCtrlInfo} -- registered fingerprint, may connect
|
||||||
| CRRemoteCtrlConnecting {remoteCtrlId :: RemoteCtrlId, displayName :: Text}
|
| CRRemoteCtrlConnecting {remoteCtrl :: RemoteCtrlInfo}
|
||||||
| CRRemoteCtrlConnected {remoteCtrlId :: RemoteCtrlId, displayName :: Text}
|
| CRRemoteCtrlConnected {remoteCtrl :: RemoteCtrlInfo}
|
||||||
| CRRemoteCtrlStopped
|
| CRRemoteCtrlStopped
|
||||||
| CRSQLResult {rows :: [Text]}
|
| CRSQLResult {rows :: [Text]}
|
||||||
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
|
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
|
||||||
@ -693,34 +693,6 @@ logResponseToFile = \case
|
|||||||
CRMessageError {} -> True
|
CRMessageError {} -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
data RemoteCtrlOOB = RemoteCtrlOOB
|
|
||||||
{ caFingerprint :: C.KeyHash,
|
|
||||||
displayName :: Text
|
|
||||||
}
|
|
||||||
deriving (Show, Generic, FromJSON)
|
|
||||||
|
|
||||||
instance ToJSON RemoteCtrlOOB where toEncoding = J.genericToEncoding J.defaultOptions
|
|
||||||
|
|
||||||
data RemoteHostInfo = RemoteHostInfo
|
|
||||||
{ remoteHostId :: RemoteHostId,
|
|
||||||
storePath :: FilePath,
|
|
||||||
displayName :: Text,
|
|
||||||
remoteCtrlOOB :: RemoteCtrlOOB,
|
|
||||||
sessionActive :: Bool
|
|
||||||
}
|
|
||||||
deriving (Show, Generic, FromJSON)
|
|
||||||
|
|
||||||
instance ToJSON RemoteHostInfo where toEncoding = J.genericToEncoding J.defaultOptions
|
|
||||||
|
|
||||||
data RemoteCtrlInfo = RemoteCtrlInfo
|
|
||||||
{ remoteCtrlId :: RemoteCtrlId,
|
|
||||||
displayName :: Text,
|
|
||||||
sessionActive :: Bool
|
|
||||||
}
|
|
||||||
deriving (Eq, Show, Generic, FromJSON)
|
|
||||||
|
|
||||||
instance ToJSON RemoteCtrlInfo where toEncoding = J.genericToEncoding J.defaultOptions
|
|
||||||
|
|
||||||
data ConnectionPlan
|
data ConnectionPlan
|
||||||
= CPInvitationLink {invitationLinkPlan :: InvitationLinkPlan}
|
= CPInvitationLink {invitationLinkPlan :: InvitationLinkPlan}
|
||||||
| CPContactAddress {contactAddressPlan :: ContactAddressPlan}
|
| CPContactAddress {contactAddressPlan :: ContactAddressPlan}
|
||||||
|
@ -33,6 +33,7 @@ import Data.Int (Int64)
|
|||||||
import Data.List.NonEmpty (NonEmpty (..))
|
import Data.List.NonEmpty (NonEmpty (..))
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
|
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
|
||||||
import qualified Network.HTTP.Types as HTTP
|
import qualified Network.HTTP.Types as HTTP
|
||||||
@ -93,7 +94,7 @@ startRemoteHost remoteHostId = do
|
|||||||
M.lookup remoteHostId <$> chatReadVar remoteHostSessions >>= \case
|
M.lookup remoteHostId <$> chatReadVar remoteHostSessions >>= \case
|
||||||
Nothing -> logInfo $ "Session already closed for remote host " <> tshow remoteHostId
|
Nothing -> logInfo $ "Session already closed for remote host " <> tshow remoteHostId
|
||||||
Just _ -> closeRemoteHostSession remoteHostId >> toView (CRRemoteHostStopped remoteHostId)
|
Just _ -> closeRemoteHostSession remoteHostId >> toView (CRRemoteHostStopped remoteHostId)
|
||||||
run RemoteHost {storePath, caKey, caCert} = do
|
run rh@RemoteHost {storePath, caKey, caCert} = do
|
||||||
finished <- newTVarIO False
|
finished <- newTVarIO False
|
||||||
let parent = (C.signatureKeyPair caKey, caCert)
|
let parent = (C.signatureKeyPair caKey, caCert)
|
||||||
sessionCreds <- liftIO $ genCredentials (Just parent) (0, 24) "Session"
|
sessionCreds <- liftIO $ genCredentials (Just parent) (0, 24) "Session"
|
||||||
@ -120,7 +121,9 @@ startRemoteHost remoteHostId = do
|
|||||||
Nothing -> toViewRemote chatResponse
|
Nothing -> toViewRemote chatResponse
|
||||||
Just localFile -> toViewRemote CRRcvFileComplete {user = ru, chatItem = AChatItem c d i ci {file = Just localFile}}
|
Just localFile -> toViewRemote CRRcvFileComplete {user = ru, chatItem = AChatItem c d i ci {file = Just localFile}}
|
||||||
_ -> toViewRemote chatResponse
|
_ -> toViewRemote chatResponse
|
||||||
toView CRRemoteHostConnected {remoteHostId}
|
rcName <- chatReadVar localDeviceName
|
||||||
|
-- TODO what sets session active?
|
||||||
|
toView CRRemoteHostConnected {remoteHost = remoteHostInfo rh True rcName}
|
||||||
|
|
||||||
sendHello :: (ChatMonad m) => HTTP2Client -> m (Either HTTP2.HTTP2ClientError HTTP2.HTTP2Response)
|
sendHello :: (ChatMonad m) => HTTP2Client -> m (Either HTTP2.HTTP2ClientError HTTP2.HTTP2Response)
|
||||||
sendHello http = liftIO (HTTP2.sendRequestDirect http req Nothing)
|
sendHello http = liftIO (HTTP2.sendRequestDirect http req Nothing)
|
||||||
@ -155,13 +158,13 @@ cancelRemoteHostSession = \case
|
|||||||
|
|
||||||
createRemoteHost :: (ChatMonad m) => m RemoteHostInfo
|
createRemoteHost :: (ChatMonad m) => m RemoteHostInfo
|
||||||
createRemoteHost = do
|
createRemoteHost = do
|
||||||
let hostDisplayName = "TODO" -- you don't have remote host name here, it will be passed from remote host
|
let rhName = "TODO" -- you don't have remote host name here, it will be passed from remote host
|
||||||
((_, caKey), caCert) <- liftIO $ genCredentials Nothing (-25, 24 * 365) hostDisplayName
|
((_, caKey), caCert) <- liftIO $ genCredentials Nothing (-25, 24 * 365) rhName
|
||||||
storePath <- liftIO randomStorePath
|
storePath <- liftIO randomStorePath
|
||||||
remoteHostId <- withStore' $ \db -> insertRemoteHost db storePath hostDisplayName caKey caCert
|
remoteHostId <- withStore' $ \db -> insertRemoteHost db storePath rhName caKey caCert
|
||||||
displayName <- chatReadVar localDeviceName
|
rcName <- chatReadVar localDeviceName
|
||||||
let remoteCtrlOOB = RemoteCtrlOOB {caFingerprint = C.certificateFingerprint caCert, displayName}
|
let remoteCtrlOOB = RemoteCtrlOOB {fingerprint = C.certificateFingerprint caCert, displayName = rcName}
|
||||||
pure RemoteHostInfo {remoteHostId, storePath, displayName, remoteCtrlOOB, sessionActive = False}
|
pure RemoteHostInfo {remoteHostId, storePath, displayName = rhName, remoteCtrlOOB, sessionActive = False}
|
||||||
|
|
||||||
-- | Generate a random 16-char filepath without / in it by using base64url encoding.
|
-- | Generate a random 16-char filepath without / in it by using base64url encoding.
|
||||||
randomStorePath :: IO FilePath
|
randomStorePath :: IO FilePath
|
||||||
@ -173,10 +176,13 @@ listRemoteHosts = do
|
|||||||
rcName <- chatReadVar localDeviceName
|
rcName <- chatReadVar localDeviceName
|
||||||
map (rhInfo active rcName) <$> withStore' getRemoteHosts
|
map (rhInfo active rcName) <$> withStore' getRemoteHosts
|
||||||
where
|
where
|
||||||
rhInfo active rcName RemoteHost {remoteHostId, storePath, displayName, caCert} =
|
rhInfo active rcName rh@RemoteHost {remoteHostId} =
|
||||||
let sessionActive = M.member remoteHostId active
|
remoteHostInfo rh (M.member remoteHostId active) rcName
|
||||||
remoteCtrlOOB = RemoteCtrlOOB {caFingerprint = C.certificateFingerprint caCert, displayName = rcName}
|
|
||||||
in RemoteHostInfo {remoteHostId, storePath, displayName, remoteCtrlOOB, sessionActive}
|
remoteHostInfo :: RemoteHost -> Bool -> Text -> RemoteHostInfo
|
||||||
|
remoteHostInfo RemoteHost {remoteHostId, storePath, displayName, caCert} sessionActive rcName =
|
||||||
|
let remoteCtrlOOB = RemoteCtrlOOB {fingerprint = C.certificateFingerprint caCert, displayName = rcName}
|
||||||
|
in RemoteHostInfo {remoteHostId, storePath, displayName, remoteCtrlOOB, sessionActive}
|
||||||
|
|
||||||
deleteRemoteHost :: (ChatMonad m) => RemoteHostId -> m ()
|
deleteRemoteHost :: (ChatMonad m) => RemoteHostId -> m ()
|
||||||
deleteRemoteHost remoteHostId = withRemoteHost remoteHostId $ \RemoteHost {storePath} -> do
|
deleteRemoteHost remoteHostId = withRemoteHost remoteHostId $ \RemoteHost {storePath} -> do
|
||||||
@ -405,13 +411,13 @@ startRemoteCtrl execChatCommand =
|
|||||||
accepted <- newEmptyTMVarIO
|
accepted <- newEmptyTMVarIO
|
||||||
supervisor <- async $ do
|
supervisor <- async $ do
|
||||||
remoteCtrlId <- atomically (readTMVar accepted)
|
remoteCtrlId <- atomically (readTMVar accepted)
|
||||||
withRemoteCtrl remoteCtrlId $ \RemoteCtrl {displayName, fingerprint} -> do
|
withRemoteCtrl remoteCtrlId $ \rc@RemoteCtrl {fingerprint} -> do
|
||||||
source <- atomically $ TM.lookup fingerprint discovered >>= maybe retry pure
|
source <- atomically $ TM.lookup fingerprint discovered >>= maybe retry pure
|
||||||
toView $ CRRemoteCtrlConnecting {remoteCtrlId, displayName}
|
toView $ CRRemoteCtrlConnecting $ remoteCtrlInfo rc False
|
||||||
atomically $ writeTVar discovered mempty -- flush unused sources
|
atomically $ writeTVar discovered mempty -- flush unused sources
|
||||||
server <- async $ Discovery.connectRevHTTP2 source fingerprint (processControllerRequest execChatCommand)
|
server <- async $ Discovery.connectRevHTTP2 source fingerprint (processControllerRequest execChatCommand)
|
||||||
chatModifyVar remoteCtrlSession $ fmap $ \s -> s {hostServer = Just server}
|
chatModifyVar remoteCtrlSession $ fmap $ \s -> s {hostServer = Just server}
|
||||||
toView $ CRRemoteCtrlConnected {remoteCtrlId, displayName}
|
toView $ CRRemoteCtrlConnected $ remoteCtrlInfo rc True
|
||||||
_ <- waitCatch server
|
_ <- waitCatch server
|
||||||
chatWriteVar remoteCtrlSession Nothing
|
chatWriteVar remoteCtrlSession Nothing
|
||||||
toView CRRemoteCtrlStopped
|
toView CRRemoteCtrlStopped
|
||||||
@ -436,7 +442,7 @@ discoverRemoteCtrls discovered = Discovery.withListener go
|
|||||||
withStore' (`getRemoteCtrlByFingerprint` fingerprint) >>= \case
|
withStore' (`getRemoteCtrlByFingerprint` fingerprint) >>= \case
|
||||||
Nothing -> toView $ CRRemoteCtrlAnnounce fingerprint -- unknown controller, ui "register" action required
|
Nothing -> toView $ CRRemoteCtrlAnnounce fingerprint -- unknown controller, ui "register" action required
|
||||||
Just found@RemoteCtrl {remoteCtrlId, accepted = storedChoice} -> case storedChoice of
|
Just found@RemoteCtrl {remoteCtrlId, accepted = storedChoice} -> case storedChoice of
|
||||||
Nothing -> toView $ CRRemoteCtrlFound found -- first-time controller, ui "accept" action required
|
Nothing -> toView $ CRRemoteCtrlFound $ remoteCtrlInfo found False -- first-time controller, ui "accept" action required
|
||||||
Just False -> pure () -- skipping a rejected item
|
Just False -> pure () -- skipping a rejected item
|
||||||
Just True ->
|
Just True ->
|
||||||
chatReadVar remoteCtrlSession >>= \case
|
chatReadVar remoteCtrlSession >>= \case
|
||||||
@ -444,11 +450,6 @@ discoverRemoteCtrls discovered = Discovery.withListener go
|
|||||||
Just RemoteCtrlSession {accepted} -> atomically $ void $ tryPutTMVar accepted remoteCtrlId -- previously accepted controller, connect automatically
|
Just RemoteCtrlSession {accepted} -> atomically $ void $ tryPutTMVar accepted remoteCtrlId -- previously accepted controller, connect automatically
|
||||||
_nonV4 -> go sock
|
_nonV4 -> go sock
|
||||||
|
|
||||||
registerRemoteCtrl :: (ChatMonad m) => RemoteCtrlOOB -> m RemoteCtrlId
|
|
||||||
registerRemoteCtrl RemoteCtrlOOB {caFingerprint, displayName} = do
|
|
||||||
remoteCtrlId <- withStore' $ \db -> insertRemoteCtrl db displayName caFingerprint
|
|
||||||
pure remoteCtrlId
|
|
||||||
|
|
||||||
listRemoteCtrls :: (ChatMonad m) => m [RemoteCtrlInfo]
|
listRemoteCtrls :: (ChatMonad m) => m [RemoteCtrlInfo]
|
||||||
listRemoteCtrls = do
|
listRemoteCtrls = do
|
||||||
active <-
|
active <-
|
||||||
@ -456,9 +457,12 @@ listRemoteCtrls = do
|
|||||||
$>>= \RemoteCtrlSession {accepted} -> atomically $ tryReadTMVar accepted
|
$>>= \RemoteCtrlSession {accepted} -> atomically $ tryReadTMVar accepted
|
||||||
map (rcInfo active) <$> withStore' getRemoteCtrls
|
map (rcInfo active) <$> withStore' getRemoteCtrls
|
||||||
where
|
where
|
||||||
rcInfo active RemoteCtrl {remoteCtrlId, displayName} =
|
rcInfo active rc@RemoteCtrl {remoteCtrlId} =
|
||||||
let sessionActive = active == Just remoteCtrlId
|
remoteCtrlInfo rc $ active == Just remoteCtrlId
|
||||||
in RemoteCtrlInfo {remoteCtrlId, displayName, sessionActive}
|
|
||||||
|
remoteCtrlInfo :: RemoteCtrl -> Bool -> RemoteCtrlInfo
|
||||||
|
remoteCtrlInfo RemoteCtrl {remoteCtrlId, displayName, fingerprint, accepted} sessionActive =
|
||||||
|
RemoteCtrlInfo {remoteCtrlId, displayName, fingerprint, accepted, sessionActive}
|
||||||
|
|
||||||
acceptRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ()
|
acceptRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ()
|
||||||
acceptRemoteCtrl remoteCtrlId = do
|
acceptRemoteCtrl remoteCtrlId = do
|
||||||
|
@ -24,6 +24,25 @@ data RemoteHost = RemoteHost
|
|||||||
}
|
}
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
|
data RemoteCtrlOOB = RemoteCtrlOOB
|
||||||
|
{ fingerprint :: C.KeyHash,
|
||||||
|
displayName :: Text
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
$(J.deriveJSON J.defaultOptions ''RemoteCtrlOOB)
|
||||||
|
|
||||||
|
data RemoteHostInfo = RemoteHostInfo
|
||||||
|
{ remoteHostId :: RemoteHostId,
|
||||||
|
storePath :: FilePath,
|
||||||
|
displayName :: Text,
|
||||||
|
remoteCtrlOOB :: RemoteCtrlOOB,
|
||||||
|
sessionActive :: Bool
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
$(J.deriveJSON J.defaultOptions ''RemoteHostInfo)
|
||||||
|
|
||||||
type RemoteCtrlId = Int64
|
type RemoteCtrlId = Int64
|
||||||
|
|
||||||
data RemoteCtrl = RemoteCtrl
|
data RemoteCtrl = RemoteCtrl
|
||||||
@ -34,4 +53,15 @@ data RemoteCtrl = RemoteCtrl
|
|||||||
}
|
}
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
$(J.deriveJSON J.defaultOptions ''RemoteCtrl)
|
$(J.deriveJSON J.defaultOptions {J.omitNothingFields = True} ''RemoteCtrl)
|
||||||
|
|
||||||
|
data RemoteCtrlInfo = RemoteCtrlInfo
|
||||||
|
{ remoteCtrlId :: RemoteCtrlId,
|
||||||
|
displayName :: Text,
|
||||||
|
fingerprint :: C.KeyHash,
|
||||||
|
accepted :: Maybe Bool,
|
||||||
|
sessionActive :: Bool
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
$(J.deriveJSON J.defaultOptions {J.omitNothingFields = True} ''RemoteCtrlInfo)
|
||||||
|
@ -9,14 +9,15 @@ import Data.Text (Text)
|
|||||||
import Database.SQLite.Simple (Only (..))
|
import Database.SQLite.Simple (Only (..))
|
||||||
import qualified Database.SQLite.Simple as SQL
|
import qualified Database.SQLite.Simple as SQL
|
||||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||||
import Simplex.Chat.Remote.Types (RemoteCtrl (..), RemoteCtrlId, RemoteHost (..), RemoteHostId)
|
import Simplex.Chat.Store.Shared (insertedRowId)
|
||||||
|
import Simplex.Chat.Remote.Types
|
||||||
import Simplex.Messaging.Agent.Store.SQLite (maybeFirstRow)
|
import Simplex.Messaging.Agent.Store.SQLite (maybeFirstRow)
|
||||||
import qualified Simplex.Messaging.Crypto as C
|
import qualified Simplex.Messaging.Crypto as C
|
||||||
|
|
||||||
insertRemoteHost :: DB.Connection -> FilePath -> Text -> C.APrivateSignKey -> C.SignedCertificate -> IO RemoteHostId
|
insertRemoteHost :: DB.Connection -> FilePath -> Text -> C.APrivateSignKey -> C.SignedCertificate -> IO RemoteHostId
|
||||||
insertRemoteHost db storePath displayName caKey caCert = do
|
insertRemoteHost db storePath displayName caKey caCert = do
|
||||||
DB.execute db "INSERT INTO remote_hosts (store_path, display_name, ca_key, ca_cert) VALUES (?,?,?,?)" (storePath, displayName, caKey, C.SignedObject caCert)
|
DB.execute db "INSERT INTO remote_hosts (store_path, display_name, ca_key, ca_cert) VALUES (?,?,?,?)" (storePath, displayName, caKey, C.SignedObject caCert)
|
||||||
fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()"
|
insertedRowId db
|
||||||
|
|
||||||
getRemoteHosts :: DB.Connection -> IO [RemoteHost]
|
getRemoteHosts :: DB.Connection -> IO [RemoteHost]
|
||||||
getRemoteHosts db =
|
getRemoteHosts db =
|
||||||
@ -37,10 +38,11 @@ toRemoteHost (remoteHostId, storePath, displayName, caKey, C.SignedObject caCert
|
|||||||
deleteRemoteHostRecord :: DB.Connection -> RemoteHostId -> IO ()
|
deleteRemoteHostRecord :: DB.Connection -> RemoteHostId -> IO ()
|
||||||
deleteRemoteHostRecord db remoteHostId = DB.execute db "DELETE FROM remote_hosts WHERE remote_host_id = ?" (Only remoteHostId)
|
deleteRemoteHostRecord db remoteHostId = DB.execute db "DELETE FROM remote_hosts WHERE remote_host_id = ?" (Only remoteHostId)
|
||||||
|
|
||||||
insertRemoteCtrl :: DB.Connection -> Text -> C.KeyHash -> IO RemoteCtrlId
|
insertRemoteCtrl :: DB.Connection -> RemoteCtrlOOB -> IO RemoteCtrlInfo
|
||||||
insertRemoteCtrl db displayName fingerprint = do
|
insertRemoteCtrl db RemoteCtrlOOB {fingerprint, displayName} = do
|
||||||
DB.execute db "INSERT INTO remote_controllers (display_name, fingerprint) VALUES (?,?)" (displayName, fingerprint)
|
DB.execute db "INSERT INTO remote_controllers (display_name, fingerprint) VALUES (?,?)" (displayName, fingerprint)
|
||||||
fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()"
|
remoteCtrlId <- insertedRowId db
|
||||||
|
pure RemoteCtrlInfo {remoteCtrlId, displayName, fingerprint, accepted = Nothing, sessionActive = False}
|
||||||
|
|
||||||
getRemoteCtrls :: DB.Connection -> IO [RemoteCtrl]
|
getRemoteCtrls :: DB.Connection -> IO [RemoteCtrl]
|
||||||
getRemoteCtrls db =
|
getRemoteCtrls db =
|
||||||
|
@ -264,14 +264,14 @@ responseToView (currentRH, user_) ChatConfig {logLevel, showReactions, showRecei
|
|||||||
CRNtfMessages {} -> []
|
CRNtfMessages {} -> []
|
||||||
CRRemoteHostCreated RemoteHostInfo {remoteHostId, remoteCtrlOOB} -> ("remote host " <> sShow remoteHostId <> " created") : viewRemoteCtrlOOBData remoteCtrlOOB
|
CRRemoteHostCreated RemoteHostInfo {remoteHostId, remoteCtrlOOB} -> ("remote host " <> sShow remoteHostId <> " created") : viewRemoteCtrlOOBData remoteCtrlOOB
|
||||||
CRRemoteHostList hs -> viewRemoteHosts hs
|
CRRemoteHostList hs -> viewRemoteHosts hs
|
||||||
CRRemoteHostConnected rhId -> ["remote host " <> sShow rhId <> " connected"]
|
CRRemoteHostConnected RemoteHostInfo {remoteHostId = rhId} -> ["remote host " <> sShow rhId <> " connected"]
|
||||||
CRRemoteHostStopped rhId -> ["remote host " <> sShow rhId <> " stopped"]
|
CRRemoteHostStopped rhId -> ["remote host " <> sShow rhId <> " stopped"]
|
||||||
CRRemoteCtrlList cs -> viewRemoteCtrls cs
|
CRRemoteCtrlList cs -> viewRemoteCtrls cs
|
||||||
CRRemoteCtrlRegistered rcId -> ["remote controller " <> sShow rcId <> " registered"]
|
CRRemoteCtrlRegistered RemoteCtrlInfo {remoteCtrlId = rcId} -> ["remote controller " <> sShow rcId <> " registered"]
|
||||||
CRRemoteCtrlAnnounce fingerprint -> ["remote controller announced", "connection code:", plain $ strEncode fingerprint]
|
CRRemoteCtrlAnnounce fingerprint -> ["remote controller announced", "connection code:", plain $ strEncode fingerprint]
|
||||||
CRRemoteCtrlFound rc -> ["remote controller found:", viewRemoteCtrl rc]
|
CRRemoteCtrlFound rc -> ["remote controller found:", viewRemoteCtrl rc]
|
||||||
CRRemoteCtrlConnecting rcId rcName -> ["remote controller " <> sShow rcId <> " connecting to " <> plain rcName]
|
CRRemoteCtrlConnecting RemoteCtrlInfo {remoteCtrlId = rcId, displayName = rcName} -> ["remote controller " <> sShow rcId <> " connecting to " <> plain rcName]
|
||||||
CRRemoteCtrlConnected rcId rcName -> ["remote controller " <> sShow rcId <> " connected, " <> plain rcName]
|
CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, displayName = rcName} -> ["remote controller " <> sShow rcId <> " connected, " <> plain rcName]
|
||||||
CRRemoteCtrlStopped -> ["remote controller stopped"]
|
CRRemoteCtrlStopped -> ["remote controller stopped"]
|
||||||
CRSQLResult rows -> map plain rows
|
CRSQLResult rows -> map plain rows
|
||||||
CRSlowSQLQueries {chatQueries, agentQueries} ->
|
CRSlowSQLQueries {chatQueries, agentQueries} ->
|
||||||
@ -1633,8 +1633,8 @@ viewVersionInfo logLevel CoreVersionInfo {version, simplexmqVersion, simplexmqCo
|
|||||||
parens s = " (" <> s <> ")"
|
parens s = " (" <> s <> ")"
|
||||||
|
|
||||||
viewRemoteCtrlOOBData :: RemoteCtrlOOB -> [StyledString]
|
viewRemoteCtrlOOBData :: RemoteCtrlOOB -> [StyledString]
|
||||||
viewRemoteCtrlOOBData RemoteCtrlOOB {caFingerprint} =
|
viewRemoteCtrlOOBData RemoteCtrlOOB {fingerprint} =
|
||||||
["connection code:", plain $ strEncode caFingerprint]
|
["connection code:", plain $ strEncode fingerprint]
|
||||||
|
|
||||||
viewRemoteHosts :: [RemoteHostInfo] -> [StyledString]
|
viewRemoteHosts :: [RemoteHostInfo] -> [StyledString]
|
||||||
viewRemoteHosts = \case
|
viewRemoteHosts = \case
|
||||||
@ -1653,8 +1653,8 @@ viewRemoteCtrls = \case
|
|||||||
plain $ tshow remoteCtrlId <> ". " <> displayName <> if sessionActive then " (active)" else ""
|
plain $ tshow remoteCtrlId <> ". " <> displayName <> if sessionActive then " (active)" else ""
|
||||||
|
|
||||||
-- TODO fingerprint, accepted?
|
-- TODO fingerprint, accepted?
|
||||||
viewRemoteCtrl :: RemoteCtrl -> StyledString
|
viewRemoteCtrl :: RemoteCtrlInfo -> StyledString
|
||||||
viewRemoteCtrl RemoteCtrl {remoteCtrlId, displayName} =
|
viewRemoteCtrl RemoteCtrlInfo {remoteCtrlId, displayName} =
|
||||||
plain $ tshow remoteCtrlId <> ". " <> displayName
|
plain $ tshow remoteCtrlId <> ". " <> displayName
|
||||||
|
|
||||||
viewChatError :: ChatLogLevel -> ChatError -> [StyledString]
|
viewChatError :: ChatLogLevel -> ChatError -> [StyledString]
|
||||||
|
Loading…
Reference in New Issue
Block a user