// // GroupChat.swift // GroupChat // // Created by QuentinArguillere on 08/09/2021. // Copyright © 2021 BelledonneCommunications. All rights reserved. // import linphonesw class AdvancedChatTutorialContext : ObservableObject { var mCore: Core! @Published var coreVersion: String = Core.getVersion var mRegistrationDelegate : CoreDelegate! @Published var username : String = "user" @Published var passwd : String = "pwd" @Published var domain : String = "sip.example.org" @Published var loggedIn: Bool = false @Published var transportType : String = "TLS" /*------------ Advanced chat tutorial related variables -------*/ var mChatroom : ChatRoom? var mChatroomDelegate : ChatRoomDelegate! var mChatMessageDelegate : ChatMessageDelegate! var mChatMessage : ChatMessage? var mLastFileMessageReceived : ChatMessage? @Published var msgToSend : String = "msg" @Published var remoteAddress : String = "sip:remote@sip.example.org" @Published var canEditAddress : Bool = true @Published var isDownloading : Bool = false @Published var messagesReceived : String = "" var fileFolderUrl : URL! var fileUrl : URL! init() { LoggingService.Instance.logLevel = LogLevel.Debug try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) try? mCore.start() mRegistrationDelegate = CoreDelegateStub(onMessageReceived : { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in if (self.mChatroom == nil) { // Check it is an one-to-one encrypted chat room if (chatRoom.hasCapability(mask: ChatRoomCapabilities.OneToOne.rawValue) && chatRoom.hasCapability(mask: ChatRoomCapabilities.Encrypted.rawValue)) { // Keep the chatRoom object to use it to send messages if it hasn't been created yet self.mChatroom = chatRoom self.mChatroom!.addDelegate(delegate: self.mChatroomDelegate) self.enableEphemeral() if let remoteAddress = chatRoom.peerAddress?.asStringUriOnly() { self.remoteAddress = remoteAddress } self.canEditAddress = false } } chatRoom.markAsRead() for content in message.contents { if (content.isFileTransfer) { self.mLastFileMessageReceived = message self.messagesReceived += "\n--File available for download--" } else if (content.isText) { self.messagesReceived += "\nThem: \(message.utf8Text)" } } }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") if (state == .Ok) { self.loggedIn = true } else if (state == .Cleared) { self.loggedIn = false } }) mCore.addDelegate(delegate: mRegistrationDelegate) // This delegate has to be attached to each specific chat message we want to monitor, before sending it mChatMessageDelegate = ChatMessageDelegateStub(onMsgStateChanged : { (message: ChatMessage, state: ChatMessage.State) in print("MessageTrace - msg state changed: \(state)\n") if (state == .InProgress) { } else if (state == .Delivered) { // The proxy server has acknowledged the message with a 200 OK self.messagesReceived += "\nMe: \(message.utf8Text)" } else if (state == .DeliveredToUser) { // User has received it } else if (state == .Displayed) { // User has read it (client called chatRoom.markAsRead() } else if (state == .NotDelivered) { // User might be invalid or not registered } else if (state == .FileTransferDone && self.isDownloading == true) { // We finished uploading/downloading the file self.isDownloading = false } }) mChatroomDelegate = ChatRoomDelegateStub ( onStateChanged: { (chatRoom: ChatRoom, newState: ChatRoom.State?) in if (newState == ChatRoom.State.Created) { self.enableEphemeral() } }, onEphemeralEvent: { (chatRoom: ChatRoom, eventLog: EventLog) in // This event is generated when the chat room ephemeral settings are being changed }, onEphemeralMessageTimerStarted: { (chatRoom: ChatRoom, eventLog: EventLog) in // This is called when a message has been read by all recipient, so the timer has started }, onEphemeralMessageDeleted: { (chatRoom: ChatRoom, eventLog: EventLog) in // This is called when a message has expired and we should remove it from the view }) // example file to send let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]) fileFolderUrl = documentsPath.appendingPathComponent("TutorialFiles") fileUrl = fileFolderUrl?.appendingPathComponent("file_to_transfer.txt") do{ try FileManager.default.createDirectory(atPath: fileFolderUrl!.path, withIntermediateDirectories: true, attributes: nil) try String("My file content").write(to: fileUrl!, atomically: false, encoding: .utf8) }catch let error as NSError{ print("Unable to create d)irectory",error) } } func login() { do { var transport : TransportType if (transportType == "TLS") { transport = TransportType.Tls } else if (transportType == "TCP") { transport = TransportType.Tcp } else { transport = TransportType.Udp } let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) let accountParams = try mCore.createAccountParams() let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) try! accountParams.setIdentityaddress(newValue: identity) let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) try address.setTransport(newValue: transport) try accountParams.setServeraddress(newValue: address) accountParams.registerEnabled = true // We need a conference factory URI set on the Account to be able to create chat rooms with flexisip backend accountParams.conferenceFactoryUri = "sip:conference-factory@sip.linphone.org" mCore.addAuthInfo(info: authInfo) let account = try mCore.createAccount(params: accountParams) try mCore.addAccount(account: account) mCore.defaultAccount = account // We also need a LIME X3DH server URL configured for end to end encryption mCore.limeX3DhServerUrl = "https://lime.linphone.org/lime-server/lime-server.php" } catch { NSLog(error.localizedDescription) } } func unregister() { if let account = mCore.defaultAccount { let params = account.params let clonedParams = params?.clone() clonedParams?.registerEnabled = false account.params = clonedParams } } func delete() { if let account = mCore.defaultAccount { mCore.removeAccount(account: account) mCore.clearAccounts() mCore.clearAllAuthInfo() } } func createFlexisipChatRoom() { do { // In this tutorial we will create a Flexisip one-to-one chat room with end-to-end encryption // For it to work, the proxy server we connect to must be an instance of Flexisip // And we must have configured on the Account a conference-factory URI let params = try mCore.createDefaultChatRoomParams() // We won't create a group chat, only a 1-1 with advanced features such as end-to-end encryption params.backend = ChatRoomBackend.FlexisipChat params.groupEnabled = false // We will rely on LIME encryption backend (we must have configured the core.limex3dhServerUrl first) params.encryptionEnabled = true params.encryptionBackend = ChatRoomEncryptionBackend.Lime // A flexisip chat room must have a subject // But as we are doing a 1-1 chat room here we won't display it, so we can set whatever we want params.subject = "dummy subject" if (params.isValid) { // We also need the SIP address of the person we will chat with let remote = try Factory.Instance.createAddress(addr: remoteAddress) // And finally we will need our local SIP address let localAddress = mCore.defaultAccount?.params?.identityAddress mChatroom = try mCore.createChatRoom(params: params, localAddr: localAddress, participants: [remote]) // If chat room isn't created yet, wait for it to go in state Created // as Flexisip chat room creation process is asynchronous mChatroom!.addDelegate(delegate: mChatroomDelegate) // Chat room may already be created (for example if you logged in with an account for which the chat room already exists) if (mChatroom!.state == ChatRoom.State.Created) { enableEphemeral() canEditAddress = false } } } catch { NSLog(error.localizedDescription) } } func sendMessage() { do { if (mChatroom == nil) { createFlexisipChatRoom() } mChatMessage = nil mChatMessage = try mChatroom!.createMessageFromUtf8(message: msgToSend) mChatMessage!.addDelegate(delegate: mChatMessageDelegate) mChatMessage!.send() msgToSend.removeAll() } catch { NSLog(error.localizedDescription) } } func sendFile() { do { if (mChatroom == nil) { createFlexisipChatRoom() } let content = try Factory.Instance.createContent() content.name = "file_to_transfer.txt" content.type = "text" content.subtype = "plain" content.filePath = fileUrl.path let chatMessage = try mChatroom!.createFileTransferMessage(initialContent: content) chatMessage.addDelegate(delegate: mChatMessageDelegate) mCore.fileTransferServer = "https://www.linphone.org:444/lft.php" chatMessage.send() } catch { NSLog(error.localizedDescription) } } func downloadLastFileMessage() { if let message = mLastFileMessageReceived { for content in message.contents { if (content.isFileTransfer && content.filePath.isEmpty) { let contentName = content.name if (!contentName.isEmpty) { content.filePath = fileFolderUrl!.appendingPathComponent(contentName).path print("Start downloading \(content.name) into \(content.filePath)") isDownloading = true if (!message.downloadContent(content: content)) { print ("Download of \(contentName) failed") } } } } } } func enableEphemeral() { // Once chat room has been created, we can enable ephemeral feature // We enable ephemeral messages at the chat room level // Please note this only affects messages we send, not the ones we receive mChatroom?.ephemeralEnabled = true // Here we ask for a lifetime of 60 seconds, starting the moment the message has been read mChatroom?.ephemeralLifetime = 60 } }