simplify remote api, add ios api (#3213)

This commit is contained in:
Evgeny Poberezkin 2023-10-13 22:35:30 +01:00 committed by GitHub
parent 193361c09a
commit 5e6aaffb09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 184 additions and 125 deletions

View File

@ -882,6 +882,38 @@ func apiCancelFile(fileId: Int64) async -> AChatItem? {
} }
} }
func startRemoteCtrl() async throws {
try await sendCommandOkResp(.startRemoteCtrl)
}
func registerRemoteCtrl(_ remoteCtrlOOB: RemoteCtrlOOB) async throws -> Int64 {
let r = await chatSendCmd(.registerRemoteCtrl(remoteCtrlOOB: remoteCtrlOOB))
if case let .remoteCtrlRegistered(rcId) = r { return rcId }
throw r
}
func listRemoteCtrls() async throws -> [RemoteCtrlInfo] {
let r = await chatSendCmd(.listRemoteCtrls)
if case let .remoteCtrlList(rcInfo) = r { return rcInfo }
throw r
}
func acceptRemoteCtrl(_ rcId: Int64) async throws {
try await sendCommandOkResp(.acceptRemoteCtrl(remoteCtrlId: rcId))
}
func rejectRemoteCtrl(_ rcId: Int64) async throws {
try await sendCommandOkResp(.rejectRemoteCtrl(remoteCtrlId: rcId))
}
func stopRemoteCtrl() async throws {
try await sendCommandOkResp(.stopRemoteCtrl)
}
func deleteRemoteCtrl(_ rcId: Int64) async throws {
try await sendCommandOkResp(.deleteRemoteCtrl(remoteCtrlId: rcId))
}
func networkErrorAlert(_ r: ChatResponse) -> Alert? { func networkErrorAlert(_ r: ChatResponse) -> Alert? {
switch r { switch r {
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))): case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))):

View File

@ -117,6 +117,13 @@ 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 startRemoteCtrl
case registerRemoteCtrl(remoteCtrlOOB: RemoteCtrlOOB)
case listRemoteCtrls
case acceptRemoteCtrl(remoteCtrlId: Int64)
case rejectRemoteCtrl(remoteCtrlId: Int64)
case stopRemoteCtrl
case deleteRemoteCtrl(remoteCtrlId: Int64)
case showVersion case showVersion
case string(String) case string(String)
@ -255,6 +262,13 @@ 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 .startRemoteCtrl: return "/start remote ctrl"
case let .registerRemoteCtrl(oob): return "/register remote ctrl \(oob.caFingerprint)"
case let .acceptRemoteCtrl(rcId): return "/accept remote ctrl \(rcId)"
case let .rejectRemoteCtrl(rcId): return "/reject remote ctrl \(rcId)"
case .listRemoteCtrls: return "/list remote ctrls"
case .stopRemoteCtrl: return "/stop remote ctrl"
case let .deleteRemoteCtrl(rcId): return "/delete remote ctrl \(rcId)"
case .showVersion: return "/version" case .showVersion: return "/version"
case let .string(str): return str case let .string(str): return str
} }
@ -367,6 +381,13 @@ 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 .startRemoteCtrl: return "startRemoteCtrl"
case .registerRemoteCtrl: return "registerRemoteCtrl"
case .listRemoteCtrls: return "listRemoteCtrls"
case .acceptRemoteCtrl: return "acceptRemoteCtrl"
case .rejectRemoteCtrl: return "rejectRemoteCtrl"
case .stopRemoteCtrl: return "stopRemoteCtrl"
case .deleteRemoteCtrl: return "deleteRemoteCtrl"
case .showVersion: return "showVersion" case .showVersion: return "showVersion"
case .string: return "console command" case .string: return "console command"
} }
@ -563,6 +584,13 @@ public enum ChatResponse: Decodable, Error {
case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo]) case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
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 remoteCtrlRegistered(remoteCtrlId: Int64)
case remoteCtrlAnnounce(fingerprint: String)
case remoteCtrlFound(remoteCtrl: RemoteCtrl)
case remoteCtrlConnecting(remoteCtrlId: Int64, displayName: String)
case remoteCtrlConnected(remoteCtrlId: Int64, displayName: String)
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?)
case chatCmdError(user_: UserRef?, chatError: ChatError) case chatCmdError(user_: UserRef?, chatError: ChatError)
@ -699,6 +727,13 @@ public enum ChatResponse: Decodable, Error {
case .ntfMessages: return "ntfMessages" case .ntfMessages: return "ntfMessages"
case .newContactConnection: return "newContactConnection" case .newContactConnection: return "newContactConnection"
case .contactConnectionDeleted: return "contactConnectionDeleted" case .contactConnectionDeleted: return "contactConnectionDeleted"
case .remoteCtrlList: return "remoteCtrlList"
case .remoteCtrlRegistered: return "remoteCtrlRegistered"
case .remoteCtrlAnnounce: return "remoteCtrlAnnounce"
case .remoteCtrlFound: return "remoteCtrlFound"
case .remoteCtrlConnecting: return "remoteCtrlConnecting"
case .remoteCtrlConnected: return "remoteCtrlConnected"
case .remoteCtrlStopped: return "remoteCtrlStopped"
case .versionInfo: return "versionInfo" case .versionInfo: return "versionInfo"
case .cmdOk: return "cmdOk" case .cmdOk: return "cmdOk"
case .chatCmdError: return "chatCmdError" case .chatCmdError: return "chatCmdError"
@ -838,6 +873,13 @@ public enum ChatResponse: Decodable, Error {
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))") case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
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 .remoteCtrlRegistered(rcId): return "remote ctrl ID: \(rcId)"
case let .remoteCtrlAnnounce(fingerprint): return "fingerprint: \(fingerprint)"
case let .remoteCtrlFound(remoteCtrl): return "remote ctrl: \(String(describing: remoteCtrl))"
case let .remoteCtrlConnecting(rcId, displayName): return "remote ctrl ID: \(rcId)\nhost displayName: \(displayName)"
case let .remoteCtrlConnected(rcId, displayName): return "remote ctrl ID: \(rcId)\nhost displayName: \(displayName)"
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
case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError)) case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError))
@ -1461,6 +1503,23 @@ public enum NotificationPreviewMode: String, SelectableItem {
public static var values: [NotificationPreviewMode] = [.message, .contact, .hidden] public static var values: [NotificationPreviewMode] = [.message, .contact, .hidden]
} }
public struct RemoteCtrlOOB {
public var caFingerprint: String
}
public struct RemoteCtrlInfo: Decodable {
public var remoteCtrlId: Int64
public var displayName: String
public var sessionActive: Bool
}
public struct RemoteCtrl: Decodable {
var remoteCtrlId: Int64
var displayName: String
var fingerprint: String
var accepted: Bool?
}
public struct CoreVersionInfo: Decodable { public struct CoreVersionInfo: Decodable {
public var version: String public var version: String
public var simplexmqVersion: String public var simplexmqVersion: String
@ -1488,6 +1547,7 @@ public enum ChatError: Decodable {
case errorAgent(agentError: AgentErrorType) case errorAgent(agentError: AgentErrorType)
case errorStore(storeError: StoreError) case errorStore(storeError: StoreError)
case errorDatabase(databaseError: DatabaseError) case errorDatabase(databaseError: DatabaseError)
case errorRemoteCtrl(remoteCtrlError: RemoteCtrlError)
case invalidJSON(json: String) case invalidJSON(json: String)
} }
@ -1739,3 +1799,15 @@ public enum ArchiveError: Decodable {
case `import`(chatError: ChatError) case `import`(chatError: ChatError)
case importFile(file: String, chatError: ChatError) case importFile(file: String, chatError: ChatError)
} }
public enum RemoteCtrlError: Decodable {
case missing(remoteCtrlId: Int64)
case inactive
case busy
case timeout
case disconnected(remoteCtrlId: Int64, reason: String)
case connectionLost(remoteCtrlId: Int64, reason: String)
case certificateExpired(remoteCtrlId: Int64)
case certificateUntrusted(remoteCtrlId: Int64)
case badFingerprint
}

View File

@ -1938,8 +1938,8 @@ sealed class CC {
class StartRemoteHost(val remoteHostId: Long): CC() class StartRemoteHost(val remoteHostId: Long): CC()
class StopRemoteHost(val remoteHostId: Long): CC() class StopRemoteHost(val remoteHostId: Long): CC()
class DeleteRemoteHost(val remoteHostId: Long): CC() class DeleteRemoteHost(val remoteHostId: Long): CC()
class RegisterRemoteCtrl(val remoteCtrlOOB: RemoteCtrlOOB): CC()
class StartRemoteCtrl(): CC() class StartRemoteCtrl(): CC()
class RegisterRemoteCtrl(val remoteCtrlOOB: RemoteCtrlOOB): CC()
class ListRemoteCtrls(): CC() class ListRemoteCtrls(): CC()
class AcceptRemoteCtrl(val remoteCtrlId: Long): CC() class AcceptRemoteCtrl(val remoteCtrlId: Long): CC()
class RejectRemoteCtrl(val remoteCtrlId: Long): CC() class RejectRemoteCtrl(val remoteCtrlId: Long): CC()
@ -2167,8 +2167,8 @@ sealed class CC {
is StartRemoteHost -> "startRemoteHost" is StartRemoteHost -> "startRemoteHost"
is StopRemoteHost -> "stopRemoteHost" is StopRemoteHost -> "stopRemoteHost"
is DeleteRemoteHost -> "deleteRemoteHost" is DeleteRemoteHost -> "deleteRemoteHost"
is RegisterRemoteCtrl -> "registerRemoteCtrl"
is StartRemoteCtrl -> "startRemoteCtrl" is StartRemoteCtrl -> "startRemoteCtrl"
is RegisterRemoteCtrl -> "registerRemoteCtrl"
is ListRemoteCtrls -> "listRemoteCtrls" is ListRemoteCtrls -> "listRemoteCtrls"
is AcceptRemoteCtrl -> "acceptRemoteCtrl" is AcceptRemoteCtrl -> "acceptRemoteCtrl"
is RejectRemoteCtrl -> "rejectRemoteCtrl" is RejectRemoteCtrl -> "rejectRemoteCtrl"
@ -3483,30 +3483,24 @@ sealed class CR {
@Serializable @SerialName("callEnded") class CallEnded(val user: UserRef, val contact: Contact): CR() @Serializable @SerialName("callEnded") class CallEnded(val user: UserRef, val contact: Contact): 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)
@Serializable @SerialName("remoteHostCreated") class RemoteHostCreated(val remoteHostId: Long, val oobData: RemoteCtrlOOB): CR()
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
@Serializable @SerialName("remoteHostConnected") class RemoteHostConnected(val remoteHostId: Long): CR()
@Serializable @SerialName("remoteHostStopped") class RemoteHostStopped(val remoteHostId: Long): CR()
// remote events (mobile)
@Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List<RemoteCtrlInfo>): CR()
@Serializable @SerialName("remoteCtrlRegistered") class RemoteCtrlRegistered(val remoteCtrlId: Long): CR()
@Serializable @SerialName("remoteCtrlAnnounce") class RemoteCtrlAnnounce(val fingerprint: String): CR()
@Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrl): CR()
@Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrlId: Long, val displayName: String): CR()
@Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrlId: Long, val displayName: String): 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()
@Serializable @SerialName("chatCmdError") class ChatCmdError(val user_: UserRef?, val chatError: ChatError): CR() @Serializable @SerialName("chatCmdError") class ChatCmdError(val user_: UserRef?, val chatError: ChatError): CR()
@Serializable @SerialName("chatError") class ChatRespError(val user_: UserRef?, val chatError: ChatError): CR() @Serializable @SerialName("chatError") class ChatRespError(val user_: UserRef?, val chatError: ChatError): CR()
@Serializable @SerialName("archiveImported") class ArchiveImported(val archiveErrors: List<ArchiveError>): CR() @Serializable @SerialName("archiveImported") class ArchiveImported(val archiveErrors: List<ArchiveError>): CR()
// remote events (desktop)
@Serializable @SerialName("remoteHostCreated") class RemoteHostCreated(val remoteHostId: Long, val oobData: RemoteCtrlOOB): CR()
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
@Serializable @SerialName("remoteHostStarted") class RemoteHostStarted(val remoteHostId: Long): CR()
@Serializable @SerialName("remoteHostConnected") class RemoteHostConnected(val remoteHostId: Long): CR()
@Serializable @SerialName("remoteHostStopped") class RemoteHostStopped(val remoteHostId: Long): CR()
@Serializable @SerialName("remoteHostDeleted") class RemoteHostDeleted(val remoteHostId: Long): CR()
// remote events (mobile)
@Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List<RemoteCtrlInfo>): CR()
@Serializable @SerialName("remoteCtrlRegistered") class RemoteCtrlRegistered(val remoteCtrlId: Long): CR()
@Serializable @SerialName("remoteCtrlStarted") class RemoteCtrlStarted(): CR()
@Serializable @SerialName("remoteCtrlAnnounce") class RemoteCtrlAnnounce(val fingerprint: String): CR()
@Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrl): CR()
@Serializable @SerialName("remoteCtrlAccepted") class RemoteCtrlAccepted(val remoteCtrlId: Long): CR()
@Serializable @SerialName("remoteCtrlRejected") class RemoteCtrlRejected(val remoteCtrlId: Long): CR()
@Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrlId: Long, val displayName: String): CR()
@Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrlId: Long, val displayName: String): CR()
@Serializable @SerialName("remoteCtrlStopped") class RemoteCtrlStopped(): CR()
@Serializable @SerialName("remoteCtrlDeleted") class RemoteCtrlDeleted(val remoteCtrlId: Long): CR()
// general // general
@Serializable class Response(val type: String, val json: String): CR() @Serializable class Response(val type: String, val json: String): CR()
@Serializable class Invalid(val str: String): CR() @Serializable class Invalid(val str: String): CR()
@ -3632,28 +3626,22 @@ sealed class CR {
is CallEnded -> "callEnded" is CallEnded -> "callEnded"
is NewContactConnection -> "newContactConnection" is NewContactConnection -> "newContactConnection"
is ContactConnectionDeleted -> "contactConnectionDeleted" is ContactConnectionDeleted -> "contactConnectionDeleted"
is RemoteHostCreated -> "remoteHostCreated"
is RemoteHostList -> "remoteHostList"
is RemoteHostConnected -> "remoteHostConnected"
is RemoteHostStopped -> "remoteHostStopped"
is RemoteCtrlList -> "remoteCtrlList"
is RemoteCtrlRegistered -> "remoteCtrlRegistered"
is RemoteCtrlAnnounce -> "remoteCtrlAnnounce"
is RemoteCtrlFound -> "remoteCtrlFound"
is RemoteCtrlConnecting -> "remoteCtrlConnecting"
is RemoteCtrlConnected -> "remoteCtrlConnected"
is RemoteCtrlStopped -> "remoteCtrlStopped"
is VersionInfo -> "versionInfo" is VersionInfo -> "versionInfo"
is CmdOk -> "cmdOk" is CmdOk -> "cmdOk"
is ChatCmdError -> "chatCmdError" is ChatCmdError -> "chatCmdError"
is ChatRespError -> "chatError" is ChatRespError -> "chatError"
is ArchiveImported -> "archiveImported" is ArchiveImported -> "archiveImported"
is RemoteHostCreated -> "remoteHostCreated"
is RemoteHostList -> "remoteHostList"
is RemoteHostStarted -> "remoteHostStarted"
is RemoteHostConnected -> "remoteHostConnected"
is RemoteHostStopped -> "remoteHostStopped"
is RemoteHostDeleted -> "remoteHostDeleted"
is RemoteCtrlList -> "remoteCtrlList"
is RemoteCtrlRegistered -> "remoteCtrlRegistered"
is RemoteCtrlStarted -> "remoteCtrlStarted"
is RemoteCtrlAnnounce -> "remoteCtrlAnnounce"
is RemoteCtrlFound -> "remoteCtrlFound"
is RemoteCtrlAccepted -> "remoteCtrlAccepted"
is RemoteCtrlRejected -> "remoteCtrlRejected"
is RemoteCtrlConnecting -> "remoteCtrlConnecting"
is RemoteCtrlConnected -> "remoteCtrlConnected"
is RemoteCtrlStopped -> "remoteCtrlStopped"
is RemoteCtrlDeleted -> "remoteCtrlDeleted"
is Response -> "* $type" is Response -> "* $type"
is Invalid -> "* invalid json" is Invalid -> "* invalid json"
} }
@ -3779,6 +3767,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 RemoteHostList -> "remote hosts: ${json.encodeToString(remoteHosts)}"
is RemoteHostConnected -> "remote host ID: $remoteHostId"
is RemoteHostStopped -> "remote host ID: $remoteHostId"
is RemoteCtrlList -> json.encodeToString(remoteCtrls)
is RemoteCtrlRegistered -> "remote ctrl ID: $remoteCtrlId"
is RemoteCtrlAnnounce -> "fingerprint: $fingerprint"
is RemoteCtrlFound -> "remote ctrl: ${json.encodeToString(remoteCtrl)}"
is RemoteCtrlConnecting -> "remote ctrl ID: $remoteCtrlId\nhost displayName: $displayName"
is RemoteCtrlConnected -> "remote ctrl ID: $remoteCtrlId\nhost displayName: $displayName"
is RemoteCtrlStopped -> ""
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 })}"
@ -3786,23 +3785,6 @@ sealed class CR {
is ChatCmdError -> withUser(user_, chatError.string) is ChatCmdError -> withUser(user_, chatError.string)
is ChatRespError -> withUser(user_, chatError.string) is ChatRespError -> withUser(user_, chatError.string)
is ArchiveImported -> "${archiveErrors.map { it.string } }" is ArchiveImported -> "${archiveErrors.map { it.string } }"
is RemoteHostCreated -> "remote host ID: $remoteHostId\noobData ${json.encodeToString(oobData)}"
is RemoteHostList -> "remote hosts: ${json.encodeToString(remoteHosts)}"
is RemoteHostStarted -> "remote host $remoteHostId"
is RemoteHostConnected -> "remote host ID: $remoteHostId"
is RemoteHostStopped -> "remote host ID: $remoteHostId"
is RemoteHostDeleted -> "remote host ID: $remoteHostId"
is RemoteCtrlList -> json.encodeToString(remoteCtrls)
is RemoteCtrlRegistered -> "remote ctrl ID: $remoteCtrlId"
is RemoteCtrlStarted -> ""
is RemoteCtrlAnnounce -> "fingerprint: $fingerprint"
is RemoteCtrlFound -> "remote ctrl: ${json.encodeToString(remoteCtrl)}"
is RemoteCtrlAccepted -> "remote ctrl ID: $remoteCtrlId"
is RemoteCtrlRejected -> "remote ctrl ID: $remoteCtrlId"
is RemoteCtrlConnecting -> "remote ctrl ID: $remoteCtrlId\nhost displayName: $displayName"
is RemoteCtrlConnected -> "remote ctrl ID: $remoteCtrlId\nhost displayName: $displayName"
is RemoteCtrlStopped -> ""
is RemoteCtrlDeleted -> "remote ctrl ID: $remoteCtrlId"
is Response -> json is Response -> json
is Invalid -> str is Invalid -> str
} }
@ -3948,16 +3930,16 @@ sealed class ChatError {
is ChatErrorAgent -> "agent ${agentError.string}" is ChatErrorAgent -> "agent ${agentError.string}"
is ChatErrorStore -> "store ${storeError.string}" is ChatErrorStore -> "store ${storeError.string}"
is ChatErrorDatabase -> "database ${databaseError.string}" is ChatErrorDatabase -> "database ${databaseError.string}"
is ChatErrorRemoteCtrl -> "remoteCtrl ${remoteCtrlError.string}"
is ChatErrorRemoteHost -> "remoteHost ${remoteHostError.string}" is ChatErrorRemoteHost -> "remoteHost ${remoteHostError.string}"
is ChatErrorRemoteCtrl -> "remoteCtrl ${remoteCtrlError.string}"
is ChatErrorInvalidJSON -> "invalid json ${json}" is ChatErrorInvalidJSON -> "invalid json ${json}"
} }
@Serializable @SerialName("error") class ChatErrorChat(val errorType: ChatErrorType): ChatError() @Serializable @SerialName("error") class ChatErrorChat(val errorType: ChatErrorType): ChatError()
@Serializable @SerialName("errorAgent") class ChatErrorAgent(val agentError: AgentErrorType): ChatError() @Serializable @SerialName("errorAgent") class ChatErrorAgent(val agentError: AgentErrorType): ChatError()
@Serializable @SerialName("errorStore") class ChatErrorStore(val storeError: StoreError): ChatError() @Serializable @SerialName("errorStore") class ChatErrorStore(val storeError: StoreError): ChatError()
@Serializable @SerialName("errorDatabase") class ChatErrorDatabase(val databaseError: DatabaseError): ChatError() @Serializable @SerialName("errorDatabase") class ChatErrorDatabase(val databaseError: DatabaseError): ChatError()
@Serializable @SerialName("errorRemoteCtrl") class ChatErrorRemoteCtrl(val remoteCtrlError: RemoteCtrlError): ChatError()
@Serializable @SerialName("errorRemoteHost") class ChatErrorRemoteHost(val remoteHostError: RemoteHostError): ChatError() @Serializable @SerialName("errorRemoteHost") class ChatErrorRemoteHost(val remoteHostError: RemoteHostError): ChatError()
@Serializable @SerialName("errorRemoteCtrl") class ChatErrorRemoteCtrl(val remoteCtrlError: RemoteCtrlError): ChatError()
@Serializable @SerialName("invalidJSON") class ChatErrorInvalidJSON(val json: String): ChatError() @Serializable @SerialName("invalidJSON") class ChatErrorInvalidJSON(val json: String): ChatError()
} }

View File

@ -1891,18 +1891,18 @@ processChatCommand = \case
let pref = uncurry TimedMessagesGroupPreference $ maybe (FEOff, Just 86400) (\ttl -> (FEOn, Just ttl)) ttl_ let pref = uncurry TimedMessagesGroupPreference $ maybe (FEOff, Just 86400) (\ttl -> (FEOn, Just ttl)) ttl_
updateGroupProfileByName gName $ \p -> updateGroupProfileByName gName $ \p ->
p {groupPreferences = Just . setGroupPreference' SGFTimedMessages pref $ groupPreferences p} p {groupPreferences = Just . setGroupPreference' SGFTimedMessages pref $ groupPreferences p}
CreateRemoteHost -> createRemoteHost CreateRemoteHost -> uncurry CRRemoteHostCreated <$> createRemoteHost
ListRemoteHosts -> listRemoteHosts ListRemoteHosts -> CRRemoteHostList <$> listRemoteHosts
StartRemoteHost rh -> startRemoteHost rh StartRemoteHost rh -> startRemoteHost rh >> ok_
StopRemoteHost rh -> closeRemoteHostSession rh StopRemoteHost rh -> closeRemoteHostSession rh >> ok_
DeleteRemoteHost rh -> deleteRemoteHost rh DeleteRemoteHost rh -> deleteRemoteHost rh >> ok_
StartRemoteCtrl -> startRemoteCtrl (execChatCommand Nothing) StartRemoteCtrl -> startRemoteCtrl (execChatCommand Nothing) >> ok_
AcceptRemoteCtrl rc -> acceptRemoteCtrl rc AcceptRemoteCtrl rc -> acceptRemoteCtrl rc >> ok_
RejectRemoteCtrl rc -> rejectRemoteCtrl rc RejectRemoteCtrl rc -> rejectRemoteCtrl rc >> ok_
StopRemoteCtrl -> stopRemoteCtrl StopRemoteCtrl -> stopRemoteCtrl >> ok_
RegisterRemoteCtrl oob -> registerRemoteCtrl oob RegisterRemoteCtrl oob -> CRRemoteCtrlRegistered <$> registerRemoteCtrl oob
ListRemoteCtrls -> listRemoteCtrls ListRemoteCtrls -> CRRemoteCtrlList <$> listRemoteCtrls
DeleteRemoteCtrl rc -> deleteRemoteCtrl rc DeleteRemoteCtrl rc -> deleteRemoteCtrl rc >> ok_
QuitChat -> liftIO exitSuccess QuitChat -> liftIO exitSuccess
ShowVersion -> do ShowVersion -> do
let versionInfo = coreVersionInfo $(simplexmqCommitQ) let versionInfo = coreVersionInfo $(simplexmqCommitQ)

View File

@ -425,8 +425,8 @@ data ChatCommand
-- | SwitchRemoteHost (Maybe RemoteHostId) -- ^ Switch current remote host -- | SwitchRemoteHost (Maybe RemoteHostId) -- ^ Switch current remote host
| StopRemoteHost RemoteHostId -- ^ Shut down a running session | StopRemoteHost RemoteHostId -- ^ Shut down a running session
| DeleteRemoteHost RemoteHostId -- ^ Unregister remote host and remove its data | DeleteRemoteHost RemoteHostId -- ^ Unregister remote host and remove its data
| RegisterRemoteCtrl RemoteCtrlOOB -- ^ Register OOB data for satellite discovery and handshake
| StartRemoteCtrl -- ^ Start listening for announcements from all registered controllers | StartRemoteCtrl -- ^ Start listening for announcements from all registered controllers
| RegisterRemoteCtrl RemoteCtrlOOB -- ^ Register OOB data for satellite discovery and handshake
| ListRemoteCtrls | ListRemoteCtrls
| AcceptRemoteCtrl RemoteCtrlId -- ^ Accept discovered data and store confirmation | AcceptRemoteCtrl RemoteCtrlId -- ^ Accept discovered data and store confirmation
| RejectRemoteCtrl RemoteCtrlId -- ^ Reject and blacklist discovered data | RejectRemoteCtrl RemoteCtrlId -- ^ Reject and blacklist discovered data
@ -631,21 +631,15 @@ data ChatResponse
| CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection} | CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection}
| CRRemoteHostCreated {remoteHostId :: RemoteHostId, oobData :: RemoteCtrlOOB} | CRRemoteHostCreated {remoteHostId :: RemoteHostId, oobData :: RemoteCtrlOOB}
| CRRemoteHostList {remoteHosts :: [RemoteHostInfo]} -- XXX: RemoteHostInfo is mostly concerned with session setup | CRRemoteHostList {remoteHosts :: [RemoteHostInfo]} -- XXX: RemoteHostInfo is mostly concerned with session setup
| CRRemoteHostStarted {remoteHostId :: RemoteHostId}
| CRRemoteHostConnected {remoteHostId :: RemoteHostId} | CRRemoteHostConnected {remoteHostId :: RemoteHostId}
| CRRemoteHostStopped {remoteHostId :: RemoteHostId} | CRRemoteHostStopped {remoteHostId :: RemoteHostId}
| CRRemoteHostDeleted {remoteHostId :: RemoteHostId}
| CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]} | CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]}
| CRRemoteCtrlRegistered {remoteCtrlId :: RemoteCtrlId} | CRRemoteCtrlRegistered {remoteCtrlId :: RemoteCtrlId}
| CRRemoteCtrlStarted
| 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 :: RemoteCtrl} -- registered fingerprint, may connect
| CRRemoteCtrlAccepted {remoteCtrlId :: RemoteCtrlId}
| CRRemoteCtrlRejected {remoteCtrlId :: RemoteCtrlId}
| CRRemoteCtrlConnecting {remoteCtrlId :: RemoteCtrlId, displayName :: Text} | CRRemoteCtrlConnecting {remoteCtrlId :: RemoteCtrlId, displayName :: Text}
| CRRemoteCtrlConnected {remoteCtrlId :: RemoteCtrlId, displayName :: Text} | CRRemoteCtrlConnected {remoteCtrlId :: RemoteCtrlId, displayName :: Text}
| CRRemoteCtrlStopped | CRRemoteCtrlStopped
| CRRemoteCtrlDeleted {remoteCtrlId :: RemoteCtrlId}
| CRSQLResult {rows :: [Text]} | CRSQLResult {rows :: [Text]}
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]} | CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
| CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks} | CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks}
@ -667,21 +661,15 @@ allowRemoteEvent :: ChatResponse -> Bool
allowRemoteEvent = \case allowRemoteEvent = \case
CRRemoteHostCreated {} -> False CRRemoteHostCreated {} -> False
CRRemoteHostList {} -> False CRRemoteHostList {} -> False
CRRemoteHostStarted {} -> False
CRRemoteHostConnected {} -> False CRRemoteHostConnected {} -> False
CRRemoteHostStopped {} -> False CRRemoteHostStopped {} -> False
CRRemoteHostDeleted {} -> False
CRRemoteCtrlList {} -> False CRRemoteCtrlList {} -> False
CRRemoteCtrlRegistered {} -> False CRRemoteCtrlRegistered {} -> False
CRRemoteCtrlStarted {} -> False
CRRemoteCtrlAnnounce {} -> False CRRemoteCtrlAnnounce {} -> False
CRRemoteCtrlFound {} -> False CRRemoteCtrlFound {} -> False
CRRemoteCtrlAccepted {} -> False
CRRemoteCtrlRejected {} -> False
CRRemoteCtrlConnecting {} -> False CRRemoteCtrlConnecting {} -> False
CRRemoteCtrlConnected {} -> False CRRemoteCtrlConnected {} -> False
CRRemoteCtrlStopped {} -> False CRRemoteCtrlStopped {} -> False
CRRemoteCtrlDeleted {} -> False
_ -> True _ -> True
logResponseToFile :: ChatResponse -> Bool logResponseToFile :: ChatResponse -> Bool

View File

@ -79,21 +79,20 @@ withRemoteHost remoteHostId action =
Nothing -> throwError $ ChatErrorRemoteHost remoteHostId RHMissing Nothing -> throwError $ ChatErrorRemoteHost remoteHostId RHMissing
Just rh -> action rh Just rh -> action rh
startRemoteHost :: (ChatMonad m) => RemoteHostId -> m ChatResponse startRemoteHost :: (ChatMonad m) => RemoteHostId -> m ()
startRemoteHost remoteHostId = do startRemoteHost remoteHostId = do
asks remoteHostSessions >>= atomically . TM.lookup remoteHostId >>= \case asks remoteHostSessions >>= atomically . TM.lookup remoteHostId >>= \case
Just _ -> throwError $ ChatErrorRemoteHost remoteHostId RHBusy Just _ -> throwError $ ChatErrorRemoteHost remoteHostId RHBusy
Nothing -> withRemoteHost remoteHostId $ \rh -> do Nothing -> withRemoteHost remoteHostId $ \rh -> do
announcer <- async $ run rh announcer <- async $ run rh
chatModifyVar remoteHostSessions $ M.insert remoteHostId RemoteHostSessionStarting {announcer} chatModifyVar remoteHostSessions $ M.insert remoteHostId RemoteHostSessionStarting {announcer}
pure CRRemoteHostStarted {remoteHostId}
where where
cleanup finished = do cleanup finished = do
logInfo "Remote host http2 client fininshed" logInfo "Remote host http2 client fininshed"
atomically $ writeTVar finished True atomically $ writeTVar finished True
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 Just _ -> closeRemoteHostSession remoteHostId >> toView (CRRemoteHostStopped remoteHostId)
run RemoteHost {storePath, caKey, caCert} = do run RemoteHost {storePath, caKey, caCert} = do
finished <- newTVarIO False finished <- newTVarIO False
let parent = (C.signatureKeyPair caKey, caCert) let parent = (C.signatureKeyPair caKey, caCert)
@ -142,42 +141,41 @@ pollRemote finished http path action = loop
readTVarIO finished >>= (`unless` loop) readTVarIO finished >>= (`unless` loop)
req = HTTP2Client.requestNoBody "GET" path mempty req = HTTP2Client.requestNoBody "GET" path mempty
closeRemoteHostSession :: (ChatMonad m) => RemoteHostId -> m ChatResponse closeRemoteHostSession :: (ChatMonad m) => RemoteHostId -> m ()
closeRemoteHostSession remoteHostId = withRemoteHostSession remoteHostId $ \session -> do closeRemoteHostSession remoteHostId = withRemoteHostSession remoteHostId $ \session -> do
logInfo $ "Closing remote host session for " <> tshow remoteHostId logInfo $ "Closing remote host session for " <> tshow remoteHostId
liftIO $ cancelRemoteHostSession session liftIO $ cancelRemoteHostSession session
chatWriteVar currentRemoteHost Nothing chatWriteVar currentRemoteHost Nothing
chatModifyVar remoteHostSessions $ M.delete remoteHostId chatModifyVar remoteHostSessions $ M.delete remoteHostId
pure CRRemoteHostStopped {remoteHostId}
cancelRemoteHostSession :: (MonadUnliftIO m) => RemoteHostSession -> m () cancelRemoteHostSession :: (MonadUnliftIO m) => RemoteHostSession -> m ()
cancelRemoteHostSession = \case cancelRemoteHostSession = \case
RemoteHostSessionStarting {announcer} -> cancel announcer RemoteHostSessionStarting {announcer} -> cancel announcer
RemoteHostSessionStarted {ctrlClient} -> liftIO $ HTTP2.closeHTTP2Client ctrlClient RemoteHostSessionStarted {ctrlClient} -> liftIO $ HTTP2.closeHTTP2Client ctrlClient
createRemoteHost :: (ChatMonad m) => m ChatResponse createRemoteHost :: (ChatMonad m) => m (RemoteHostId, RemoteCtrlOOB)
createRemoteHost = do createRemoteHost = do
let displayName = "TODO" -- you don't have remote host name here, it will be passed from remote host let displayName = "TODO" -- you don't have remote host name here, it will be passed from remote host
((_, caKey), caCert) <- liftIO $ genCredentials Nothing (-25, 24 * 365) displayName ((_, caKey), caCert) <- liftIO $ genCredentials Nothing (-25, 24 * 365) displayName
storePath <- liftIO randomStorePath storePath <- liftIO randomStorePath
remoteHostId <- withStore' $ \db -> insertRemoteHost db storePath displayName caKey caCert remoteHostId <- withStore' $ \db -> insertRemoteHost db storePath displayName caKey caCert
let oobData = RemoteCtrlOOB {caFingerprint = C.certificateFingerprint caCert} let oobData = RemoteCtrlOOB {caFingerprint = C.certificateFingerprint caCert}
pure CRRemoteHostCreated {remoteHostId, oobData} pure (remoteHostId, oobData)
-- | 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
randomStorePath = B.unpack . B64U.encode <$> getRandomBytes 12 randomStorePath = B.unpack . B64U.encode <$> getRandomBytes 12
listRemoteHosts :: (ChatMonad m) => m ChatResponse listRemoteHosts :: (ChatMonad m) => m [RemoteHostInfo]
listRemoteHosts = do listRemoteHosts = do
stored <- withStore' getRemoteHosts stored <- withStore' getRemoteHosts
active <- chatReadVar remoteHostSessions active <- chatReadVar remoteHostSessions
pure $ CRRemoteHostList $ do pure $ do
RemoteHost {remoteHostId, storePath, displayName} <- stored RemoteHost {remoteHostId, storePath, displayName} <- stored
let sessionActive = M.member remoteHostId active let sessionActive = M.member remoteHostId active
pure RemoteHostInfo {remoteHostId, storePath, displayName, sessionActive} pure RemoteHostInfo {remoteHostId, storePath, displayName, sessionActive}
deleteRemoteHost :: (ChatMonad m) => RemoteHostId -> m ChatResponse deleteRemoteHost :: (ChatMonad m) => RemoteHostId -> m ()
deleteRemoteHost remoteHostId = withRemoteHost remoteHostId $ \RemoteHost {storePath} -> do deleteRemoteHost remoteHostId = withRemoteHost remoteHostId $ \RemoteHost {storePath} -> do
chatReadVar filesFolder >>= \case chatReadVar filesFolder >>= \case
Just baseDir -> do Just baseDir -> do
@ -185,7 +183,6 @@ deleteRemoteHost remoteHostId = withRemoteHost remoteHostId $ \RemoteHost {store
logError $ "TODO: remove " <> tshow hostStore logError $ "TODO: remove " <> tshow hostStore
Nothing -> logWarn "Local file store not available while deleting remote host" Nothing -> logWarn "Local file store not available while deleting remote host"
withStore' $ \db -> deleteRemoteHostRecord db remoteHostId withStore' $ \db -> deleteRemoteHostRecord db remoteHostId
pure CRRemoteHostDeleted {remoteHostId}
processRemoteCommand :: (ChatMonad m) => RemoteHostSession -> (ByteString, ChatCommand) -> m ChatResponse processRemoteCommand :: (ChatMonad m) => RemoteHostSession -> (ByteString, ChatCommand) -> m ChatResponse
processRemoteCommand RemoteHostSessionStarting {} _ = pure . CRChatError Nothing . ChatError $ CEInternalError "sending remote commands before session started" processRemoteCommand RemoteHostSessionStarting {} _ = pure . CRChatError Nothing . ChatError $ CEInternalError "sending remote commands before session started"
@ -393,7 +390,7 @@ processControllerRequest execChatCommand HTTP2.HTTP2Request {request, reqBody, s
-- * ChatRequest handlers -- * ChatRequest handlers
startRemoteCtrl :: (ChatMonad m) => (ByteString -> m ChatResponse) -> m ChatResponse startRemoteCtrl :: (ChatMonad m) => (ByteString -> m ChatResponse) -> m ()
startRemoteCtrl execChatCommand = startRemoteCtrl execChatCommand =
chatReadVar remoteCtrlSession >>= \case chatReadVar remoteCtrlSession >>= \case
Just _busy -> throwError $ ChatErrorRemoteCtrl RCEBusy Just _busy -> throwError $ ChatErrorRemoteCtrl RCEBusy
@ -416,7 +413,6 @@ startRemoteCtrl execChatCommand =
chatWriteVar remoteCtrlSession Nothing chatWriteVar remoteCtrlSession Nothing
toView CRRemoteCtrlStopped toView CRRemoteCtrlStopped
chatWriteVar remoteCtrlSession $ Just RemoteCtrlSession {discoverer, supervisor, hostServer = Nothing, discovered, accepted, remoteOutputQ} chatWriteVar remoteCtrlSession $ Just RemoteCtrlSession {discoverer, supervisor, hostServer = Nothing, discovered, accepted, remoteOutputQ}
pure CRRemoteCtrlStarted
discoverRemoteCtrls :: (ChatMonad m) => TM.TMap C.KeyHash TransportHost -> m () discoverRemoteCtrls :: (ChatMonad m) => TM.TMap C.KeyHash TransportHost -> m ()
discoverRemoteCtrls discovered = Discovery.withListener go discoverRemoteCtrls discovered = Discovery.withListener go
@ -445,33 +441,32 @@ 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 ChatResponse registerRemoteCtrl :: (ChatMonad m) => RemoteCtrlOOB -> m RemoteCtrlId
registerRemoteCtrl RemoteCtrlOOB {caFingerprint} = do registerRemoteCtrl RemoteCtrlOOB {caFingerprint} = do
let displayName = "TODO" -- maybe include into OOB data let displayName = "TODO" -- maybe include into OOB data
remoteCtrlId <- withStore' $ \db -> insertRemoteCtrl db displayName caFingerprint remoteCtrlId <- withStore' $ \db -> insertRemoteCtrl db displayName caFingerprint
pure $ CRRemoteCtrlRegistered {remoteCtrlId} pure remoteCtrlId
listRemoteCtrls :: (ChatMonad m) => m ChatResponse listRemoteCtrls :: (ChatMonad m) => m [RemoteCtrlInfo]
listRemoteCtrls = do listRemoteCtrls = do
stored <- withStore' getRemoteCtrls stored <- withStore' getRemoteCtrls
active <- active <-
chatReadVar remoteCtrlSession >>= \case chatReadVar remoteCtrlSession >>= \case
Nothing -> pure Nothing Nothing -> pure Nothing
Just RemoteCtrlSession {accepted} -> atomically (tryReadTMVar accepted) Just RemoteCtrlSession {accepted} -> atomically (tryReadTMVar accepted)
pure $ CRRemoteCtrlList $ do pure $ do
RemoteCtrl {remoteCtrlId, displayName} <- stored RemoteCtrl {remoteCtrlId, displayName} <- stored
let sessionActive = active == Just remoteCtrlId let sessionActive = active == Just remoteCtrlId
pure RemoteCtrlInfo {remoteCtrlId, displayName, sessionActive} pure RemoteCtrlInfo {remoteCtrlId, displayName, sessionActive}
acceptRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ChatResponse acceptRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ()
acceptRemoteCtrl remoteCtrlId = do acceptRemoteCtrl remoteCtrlId = do
withStore' $ \db -> markRemoteCtrlResolution db remoteCtrlId True withStore' $ \db -> markRemoteCtrlResolution db remoteCtrlId True
chatReadVar remoteCtrlSession >>= \case chatReadVar remoteCtrlSession >>= \case
Nothing -> throwError $ ChatErrorRemoteCtrl RCEInactive Nothing -> throwError $ ChatErrorRemoteCtrl RCEInactive
Just RemoteCtrlSession {accepted} -> atomically . void $ tryPutTMVar accepted remoteCtrlId -- the remote host can now proceed with connection Just RemoteCtrlSession {accepted} -> atomically . void $ tryPutTMVar accepted remoteCtrlId -- the remote host can now proceed with connection
pure $ CRRemoteCtrlAccepted {remoteCtrlId}
rejectRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ChatResponse rejectRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ()
rejectRemoteCtrl remoteCtrlId = do rejectRemoteCtrl remoteCtrlId = do
withStore' $ \db -> markRemoteCtrlResolution db remoteCtrlId False withStore' $ \db -> markRemoteCtrlResolution db remoteCtrlId False
chatReadVar remoteCtrlSession >>= \case chatReadVar remoteCtrlSession >>= \case
@ -479,9 +474,8 @@ rejectRemoteCtrl remoteCtrlId = do
Just RemoteCtrlSession {discoverer, supervisor} -> do Just RemoteCtrlSession {discoverer, supervisor} -> do
cancel discoverer cancel discoverer
cancel supervisor cancel supervisor
pure $ CRRemoteCtrlRejected {remoteCtrlId}
stopRemoteCtrl :: (ChatMonad m) => m ChatResponse stopRemoteCtrl :: (ChatMonad m) => m ()
stopRemoteCtrl = stopRemoteCtrl =
chatReadVar remoteCtrlSession >>= \case chatReadVar remoteCtrlSession >>= \case
Nothing -> throwError $ ChatErrorRemoteCtrl RCEInactive Nothing -> throwError $ ChatErrorRemoteCtrl RCEInactive
@ -489,7 +483,6 @@ stopRemoteCtrl =
cancelRemoteCtrlSession rcs $ do cancelRemoteCtrlSession rcs $ do
chatWriteVar remoteCtrlSession Nothing chatWriteVar remoteCtrlSession Nothing
toView CRRemoteCtrlStopped toView CRRemoteCtrlStopped
pure $ CRCmdOk Nothing
cancelRemoteCtrlSession_ :: (MonadUnliftIO m) => RemoteCtrlSession -> m () cancelRemoteCtrlSession_ :: (MonadUnliftIO m) => RemoteCtrlSession -> m ()
cancelRemoteCtrlSession_ rcs = cancelRemoteCtrlSession rcs $ pure () cancelRemoteCtrlSession_ rcs = cancelRemoteCtrlSession rcs $ pure ()
@ -503,12 +496,10 @@ cancelRemoteCtrlSession RemoteCtrlSession {discoverer, supervisor, hostServer} c
cancel supervisor -- supervisor is blocked until session progresses cancel supervisor -- supervisor is blocked until session progresses
cleanup cleanup
deleteRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ChatResponse deleteRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> m ()
deleteRemoteCtrl remoteCtrlId = deleteRemoteCtrl remoteCtrlId =
chatReadVar remoteCtrlSession >>= \case chatReadVar remoteCtrlSession >>= \case
Nothing -> do Nothing -> withStore' $ \db -> deleteRemoteCtrlRecord db remoteCtrlId
withStore' $ \db -> deleteRemoteCtrlRecord db remoteCtrlId
pure $ CRRemoteCtrlDeleted {remoteCtrlId}
Just _ -> throwError $ ChatErrorRemoteCtrl RCEBusy Just _ -> throwError $ ChatErrorRemoteCtrl RCEBusy
withRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> (RemoteCtrl -> m a) -> m a withRemoteCtrl :: (ChatMonad m) => RemoteCtrlId -> (RemoteCtrl -> m a) -> m a

View File

@ -264,21 +264,15 @@ responseToView (currentRH, user_) ChatConfig {logLevel, showReactions, showRecei
CRNtfMessages {} -> [] CRNtfMessages {} -> []
CRRemoteHostCreated rhId oobData -> ("remote host " <> sShow rhId <> " created") : viewRemoteCtrlOOBData oobData CRRemoteHostCreated rhId oobData -> ("remote host " <> sShow rhId <> " created") : viewRemoteCtrlOOBData oobData
CRRemoteHostList hs -> viewRemoteHosts hs CRRemoteHostList hs -> viewRemoteHosts hs
CRRemoteHostStarted rhId -> ["remote host " <> sShow rhId <> " started"]
CRRemoteHostConnected rhId -> ["remote host " <> sShow rhId <> " connected"] CRRemoteHostConnected rhId -> ["remote host " <> sShow rhId <> " connected"]
CRRemoteHostStopped rhId -> ["remote host " <> sShow rhId <> " stopped"] CRRemoteHostStopped rhId -> ["remote host " <> sShow rhId <> " stopped"]
CRRemoteHostDeleted rhId -> ["remote host " <> sShow rhId <> " deleted"]
CRRemoteCtrlList cs -> viewRemoteCtrls cs CRRemoteCtrlList cs -> viewRemoteCtrls cs
CRRemoteCtrlRegistered rcId -> ["remote controller " <> sShow rcId <> " registered"] CRRemoteCtrlRegistered rcId -> ["remote controller " <> sShow rcId <> " registered"]
CRRemoteCtrlStarted -> ["remote controller started"]
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]
CRRemoteCtrlAccepted rcId -> ["remote controller " <> sShow rcId <> " accepted"]
CRRemoteCtrlRejected rcId -> ["remote controller " <> sShow rcId <> " rejected"]
CRRemoteCtrlConnecting rcId rcName -> ["remote controller " <> sShow rcId <> " connecting to " <> plain rcName] CRRemoteCtrlConnecting rcId rcName -> ["remote controller " <> sShow rcId <> " connecting to " <> plain rcName]
CRRemoteCtrlConnected rcId rcName -> ["remote controller " <> sShow rcId <> " connected, " <> plain rcName] CRRemoteCtrlConnected rcId rcName -> ["remote controller " <> sShow rcId <> " connected, " <> plain rcName]
CRRemoteCtrlStopped -> ["remote controller stopped"] CRRemoteCtrlStopped -> ["remote controller stopped"]
CRRemoteCtrlDeleted rcId -> ["remote controller " <> sShow rcId <> " deleted"]
CRSQLResult rows -> map plain rows CRSQLResult rows -> map plain rows
CRSlowSQLQueries {chatQueries, agentQueries} -> CRSlowSQLQueries {chatQueries, agentQueries} ->
let viewQuery SlowSQLQuery {query, queryStats = SlowQueryStats {count, timeMax, timeAvg}} = let viewQuery SlowSQLQuery {query, queryStats = SlowQueryStats {count, timeMax, timeAvg}} =

View File

@ -110,10 +110,10 @@ remoteHandshakeTest = testChat2 aliceProfile bobProfile $ \desktop mobile -> do
desktop <## "Remote hosts:" desktop <## "Remote hosts:"
desktop <## "1. TODO" -- TODO host name probably should be Maybe, as when host is created there is no name yet desktop <## "1. TODO" -- TODO host name probably should be Maybe, as when host is created there is no name yet
desktop ##> "/start remote host 1" desktop ##> "/start remote host 1"
desktop <## "remote host 1 started" desktop <## "ok"
mobile ##> "/start remote ctrl" mobile ##> "/start remote ctrl"
mobile <## "remote controller started" mobile <## "ok"
mobile <## "remote controller announced" mobile <## "remote controller announced"
mobile <## "connection code:" mobile <## "connection code:"
fingerprint' <- getTermLine mobile fingerprint' <- getTermLine mobile
@ -126,7 +126,7 @@ remoteHandshakeTest = testChat2 aliceProfile bobProfile $ \desktop mobile -> do
mobile <## "Remote controllers:" mobile <## "Remote controllers:"
mobile <## "1. TODO" mobile <## "1. TODO"
mobile ##> "/accept remote ctrl 1" mobile ##> "/accept remote ctrl 1"
mobile <## "remote controller 1 accepted" -- alternative scenario: accepted before controller start mobile <## "ok" -- alternative scenario: accepted before controller start
mobile <## "remote controller 1 connecting to TODO" mobile <## "remote controller 1 connecting to TODO"
mobile <## "remote controller 1 connected, TODO" mobile <## "remote controller 1 connected, TODO"
@ -140,9 +140,9 @@ remoteHandshakeTest = testChat2 aliceProfile bobProfile $ \desktop mobile -> do
traceM " - Shutting desktop" traceM " - Shutting desktop"
desktop ##> "/stop remote host 1" desktop ##> "/stop remote host 1"
desktop <## "remote host 1 stopped" desktop <## "ok"
desktop ##> "/delete remote host 1" desktop ##> "/delete remote host 1"
desktop <## "remote host 1 deleted" desktop <## "ok"
desktop ##> "/list remote hosts" desktop ##> "/list remote hosts"
desktop <## "No remote hosts" desktop <## "No remote hosts"
@ -151,7 +151,7 @@ remoteHandshakeTest = testChat2 aliceProfile bobProfile $ \desktop mobile -> do
mobile <## "ok" mobile <## "ok"
mobile <## "remote controller stopped" mobile <## "remote controller stopped"
mobile ##> "/delete remote ctrl 1" mobile ##> "/delete remote ctrl 1"
mobile <## "remote controller 1 deleted" mobile <## "ok"
mobile ##> "/list remote ctrls" mobile ##> "/list remote ctrls"
mobile <## "No remote controllers" mobile <## "No remote controllers"
@ -173,10 +173,10 @@ remoteCommandTest = testChat3 aliceProfile aliceDesktopProfile bobProfile $ \mob
fingerprint <- getTermLine desktop fingerprint <- getTermLine desktop
desktop ##> "/start remote host 1" desktop ##> "/start remote host 1"
desktop <## "remote host 1 started" desktop <## "ok"
mobile ##> "/start remote ctrl" mobile ##> "/start remote ctrl"
mobile <## "remote controller started" mobile <## "ok"
mobile <## "remote controller announced" mobile <## "remote controller announced"
mobile <## "connection code:" mobile <## "connection code:"
fingerprint' <- getTermLine mobile fingerprint' <- getTermLine mobile
@ -184,7 +184,7 @@ remoteCommandTest = testChat3 aliceProfile aliceDesktopProfile bobProfile $ \mob
mobile ##> ("/register remote ctrl " <> fingerprint') mobile ##> ("/register remote ctrl " <> fingerprint')
mobile <## "remote controller 1 registered" mobile <## "remote controller 1 registered"
mobile ##> "/accept remote ctrl 1" mobile ##> "/accept remote ctrl 1"
mobile <## "remote controller 1 accepted" -- alternative scenario: accepted before controller start mobile <## "ok" -- alternative scenario: accepted before controller start
mobile <## "remote controller 1 connecting to TODO" mobile <## "remote controller 1 connecting to TODO"
mobile <## "remote controller 1 connected, TODO" mobile <## "remote controller 1 connected, TODO"
desktop <## "remote host 1 connected" desktop <## "remote host 1 connected"

View File

@ -33,7 +33,7 @@ main = do
describe "SimpleX chat client" chatTests describe "SimpleX chat client" chatTests
xdescribe'' "SimpleX Broadcast bot" broadcastBotTests xdescribe'' "SimpleX Broadcast bot" broadcastBotTests
xdescribe'' "SimpleX Directory service bot" directoryServiceTests xdescribe'' "SimpleX Directory service bot" directoryServiceTests
describe "Remote session" remoteTests fdescribe "Remote session" remoteTests
where where
testBracket test = do testBracket test = do
t <- getSystemTime t <- getSystemTime