DBManager.swift 28.7 KB
Newer Older
1
/*
2
 *  Copyright (C) 2017-2019 Savoir-faire Linux Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */

import Foundation
import RxSwift
23
import SQLite
24 25 26 27 28 29

enum ProfileType: String {
    case ring = "RING"
    case sip = "SIP"
}

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
enum GeneratedMessage: Int {
    case outgoingCall
    case incomingCall
    case missedOutgoingCall
    case missedIncomingCall
    case contactAdded
    case invitationReceived
    case invitationAccepted
    case unknown

    func toString() -> String {
        return String(self.rawValue)
    }
    init(from: String) {
        if let intValue = Int(from) {
            self = GeneratedMessage(rawValue: intValue) ?? .unknown
        } else {
            self =  .unknown
        }
    }
    func toMessage(with duration: Int) -> String {
        let time = Date.convertSecondsToTimeString(seconds: Double(duration))
        switch self {
        case .contactAdded:
            return L10n.GeneratedMessage.contactAdded
        case .invitationReceived:
            return L10n.GeneratedMessage.invitationReceived
        case .invitationAccepted:
            return L10n.GeneratedMessage.invitationAccepted
        case .missedOutgoingCall:
            return L10n.GeneratedMessage.missedOutgoingCall
        case .missedIncomingCall:
            return L10n.GeneratedMessage.missedIncomingCall
        case .outgoingCall:
            return L10n.GeneratedMessage.outgoingCall + " - " + time
        case .incomingCall:
            return L10n.GeneratedMessage.incomingCall + " - " + time
        default:
            return ""
        }
    }
71 72 73 74 75 76 77 78 79 80
}

enum InteractionStatus: String {
    case invalid = "INVALID"
    case unknown = "UNKNOWN"
    case sending = "SENDING"
    case failed = "FAILED"
    case succeed = "SUCCEED"
    case read = "READ"
    case unread = "UNREAD"
81 82 83 84 85 86
    case transferCreated = "TRANSFER_CREATED"
    case transferAwaiting = "TRANSFER_AWAITING"
    case transferCanceled = "TRANSFER_CANCELED"
    case transferOngoing = "TRANSFER_ONGOING"
    case transferSuccess = "TRANSFER_FINISHED"
    case transferError = "TRANSFER_ERROR"
87 88 89

    func toMessageStatus() -> MessageStatus {
        switch self {
90 91 92 93 94 95 96 97
        case .invalid: return MessageStatus.unknown
        case .unknown: return MessageStatus.unknown
        case .sending: return MessageStatus.sending
        case .failed: return MessageStatus.failure
        case .succeed: return MessageStatus.sent
        case .read: return MessageStatus.read
        case .unread: return MessageStatus.unknown
        default: return MessageStatus.unknown
98 99 100 101 102
        }
    }

    init(status: MessageStatus) {
        switch status {
103 104 105 106 107
        case .unknown: self = .unknown
        case .sending: self = .sending
        case .sent: self = .succeed
        case .read: self = .read
        case .failure: self = .failed
108 109
        @unknown default:
            self = .unknown
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
        }
    }

    func toDataTransferStatus() -> DataTransferStatus {
        switch self {
        case .transferCreated: return DataTransferStatus.created
        case .transferAwaiting: return DataTransferStatus.awaiting
        case .transferCanceled: return DataTransferStatus.canceled
        case .transferOngoing: return DataTransferStatus.ongoing
        case .transferSuccess: return DataTransferStatus.success
        case .transferError: return DataTransferStatus.error
        default: return DataTransferStatus.unknown
        }
    }

    init(status: DataTransferStatus) {
        switch status {
        case .created: self = .transferCreated
        case .awaiting: self = .transferAwaiting
        case .canceled: self = .transferCanceled
        case .ongoing: self = .transferOngoing
        case .success: self = .transferSuccess
        case .error: self = .transferError
        case .unknown: self = .unknown
134 135 136 137 138 139 140
        }
    }
}

enum DBBridgingError: Error {
    case saveMessageFailed
    case getConversationFailed
141
    case updateIntercationFailed
142
    case deleteConversationFailed
143
    case getProfileFailed
144 145 146
}

enum InteractionType: String {
147 148 149 150 151 152
    case invalid    = "INVALID"
    case text       = "TEXT"
    case call       = "CALL"
    case contact    = "CONTACT"
    case iTransfer  = "INCOMING_DATA_TRANSFER"
    case oTransfer  = "OUTGOING_DATA_TRANSFER"
153 154
}

155
typealias SavedMessageForConversation = (messageID: Int64, conversationID: Int64)
156

157 158
// swiftlint:disable type_body_length
// swiftlint:disable file_length
159 160 161 162 163
class DBManager {

    let profileHepler: ProfileDataHelper
    let conversationHelper: ConversationDataHelper
    let interactionHepler: InteractionDataHelper
164 165
    let dbConnections: DBContainer
    let disposeBag = DisposeBag()
166 167 168 169 170

    // used to create object to save to db. When inserting in table defaultID will be replaced by autoincrementedID
    let defaultID: Int64 = 1

    init(profileHepler: ProfileDataHelper, conversationHelper: ConversationDataHelper,
171
         interactionHepler: InteractionDataHelper, dbConnections: DBContainer) {
172 173 174
        self.profileHepler = profileHepler
        self.conversationHelper = conversationHelper
        self.interactionHepler = interactionHepler
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
        self.dbConnections = dbConnections
    }

    func isNeedMigrationToAccountDB(accountId: String) -> Bool {
        return self.dbConnections.isDBExistsFor(account: accountId)
    }

    func createDatabaseForAccount(accountId: String) throws -> Bool {
        guard let newDB = self.dbConnections.forAccount(account: accountId) else {
            return false
        }
        do {
            try newDB.transaction {
                profileHepler.createAccountTable(accountDb: newDB)
                profileHepler.createContactsTable(accountDb: newDB)
                conversationHelper.createTable(accountDb: newDB)
                interactionHepler.createTable(accountDb: newDB)
            }
        } catch {
            throw DataAccessError.databaseError
        }
        return true
197 198
    }

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    func migrateToAccountDB(accountId: String, jamiId: String) throws {
        do {
            guard let newDB = self.dbConnections.forAccount(account: accountId) else {
                throw DataAccessError.datastoreConnectionError
            }
            try newDB.transaction {
                profileHepler.createAccountTable(accountDb: newDB)
                profileHepler.createContactsTable(accountDb: newDB)
                conversationHelper.createTable(accountDb: newDB)
                interactionHepler.createTable(accountDb: newDB)
                guard let oldDb = self.dbConnections.getJamiDB() else {throw DataAccessError.datastoreConnectionError}
                let profileFromJamiId = try self.profileHepler.getLegacyProfileID(profileURI: jamiId, dataBase: oldDb)
                let profileFromAccountId = try self.profileHepler.getLegacyProfileID(profileURI: accountId, dataBase: oldDb)
                let profile = profileFromJamiId != nil ? profileFromJamiId : profileFromAccountId
                guard let accountProfile = profile else {throw DataAccessError.datastoreConnectionError}
                let allProfiles = try self.profileHepler.getLegacyProfiles(accountURI: jamiId, accountId: accountId, database: oldDb)
                try profileHepler.migrateToDBForAccount(from: oldDb, to: newDB, jamiId: jamiId, accountId: accountId)
                VCardUtils.loadVCard(named: VCardFiles.myProfile.rawValue,
                                     inFolder: VCardFolders.profile.rawValue)
                    .subscribe(onSuccess: { [unowned self] card in
                        let name = card.familyName
                        let imageData: String? = card.imageData?.base64EncodedString()
                        _ = self.saveAccountProfile(alias: name, photo: imageData,
                                                    accountId: accountId)
                    }).disposed(by: self.disposeBag)
                try conversationHelper.migrateToDBForAccount(from: oldDb, to: newDB,
                                                             accountProfileId: accountProfile,
                                                             contactsMap: allProfiles)
                try interactionHepler.migrateToDBForAccount(from: oldDb, to: newDB,
                                                            accountProfileId: accountProfile,
                                                            contactsMap: allProfiles)
            }
        } catch {
            throw DataAccessError.databaseMigrationError
        }
234 235
    }

236 237 238 239
    func removeDBForAccount(accountId: String) {
        self.dbConnections.removeDBForAccount(account: accountId)
    }

240 241 242 243
    // swiftlint:disable:next function_parameter_count
    func saveMessage(for accountId: String, with contactUri: String,
                     message: MessageModel, incoming: Bool,
                     interactionType: InteractionType, duration: Int) -> Observable<SavedMessageForConversation> {
244 245

        //create completable which will be executed on background thread
246
        return Observable.create { [weak self] observable in
247
            do {
248
                guard let dataBase = self?.dbConnections.forAccount(account: accountId) else {
249 250 251
                    throw DataAccessError.datastoreConnectionError
                }
                try dataBase.transaction {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
252
                    let author: String? = incoming ? contactUri : nil
253 254 255
                    guard let conversationID = try self?.getConversationsFor(contactUri: contactUri,
                                                                             createIfNotExists: true,
                                                                             dataBase: dataBase) else {
256 257
                            throw DBBridgingError.saveMessageFailed
                    }
258 259 260
                    let result = self?.addMessageTo(conversation: conversationID, author: author,
                                                    interactionType: interactionType, message: message,
                                                    duration: duration, dataBase: dataBase)
261
                    if let messageID = result {
262
                        let savedMessage = SavedMessageForConversation(messageID, conversationID)
263
                        observable.onNext(savedMessage)
264
                        observable.on(.completed)
265
                    } else {
266
                        observable.on(.error(DBBridgingError.saveMessageFailed))
267 268 269
                    }
                }
            } catch {
270
                observable.on(.error(DBBridgingError.saveMessageFailed))
271 272 273 274 275
            }
            return Disposables.create { }
            }
    }

276
    func getConversationsObservable(for accountId: String) -> Observable<[ConversationModel]> {
277 278
        return Observable.create { observable in
            do {
279
                guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
280 281
                    throw DBBridgingError.getConversationFailed
                }
282
                try dataBase.transaction(Connection.TransactionMode.immediate, block: {
283
                    let conversations = try self.buildConversationsForAccount(accountId: accountId)
284 285
                    observable.onNext(conversations)
                    observable.on(.completed)
286
                })
287 288 289 290 291 292 293
            } catch {
                observable.on(.error(DBBridgingError.getConversationFailed))
            }
            return Disposables.create { }
            }
    }

294
    func updateMessageStatus(daemonID: String, withStatus status: MessageStatus, accountId: String) -> Completable {
295
        return Completable.create { [unowned self] completable in
296 297 298 299 300 301 302 303 304 305
            if let dataBase = self.dbConnections.forAccount(account: accountId) {
                let success = self.interactionHepler
                    .updateInteractionWithDaemonID(interactionDaemonID: daemonID,
                                                   interactionStatus: InteractionStatus(status: status).rawValue,
                                                   dataBase: dataBase)
                if success {
                    completable(.completed)
                } else {
                    completable(.error(DBBridgingError.updateIntercationFailed))
                }
306
            } else {
307
                completable(.error(DBBridgingError.updateIntercationFailed))
308 309
            }
            return Disposables.create { }
310
        }
311 312
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
313 314 315 316 317 318 319 320 321 322 323
    func getProfilesForAccount(accountID: String) -> [Profile]? {
        guard let database = self.dbConnections.forAccount(account: accountID) else {
            return nil
        }
        do {
            return try self.profileHepler.selectAll(dataBase: database)
        } catch {
            return nil
        }
    }

324
    func updateFileName(interactionID: Int64, name: String, accountId: String) -> Completable {
325
        return Completable.create { [unowned self] completable in
326 327 328 329 330 331 332 333
            if let dataBase = self.dbConnections.forAccount(account: accountId) {
                let success = self.interactionHepler
                    .updateInteractionContentWithID(interactionID: interactionID, content: name, dataBase: dataBase)
                if success {
                    completable(.completed)
                } else {
                    completable(.error(DBBridgingError.updateIntercationFailed))
                }
334
            } else {
335
                completable(.error(DBBridgingError.updateIntercationFailed))
336 337 338 339 340
            }
            return Disposables.create { }
        }
    }

341
    func updateTransferStatus(daemonID: String, withStatus transferStatus: DataTransferStatus, accountId: String) -> Completable {
342
        return Completable.create { [unowned self] completable in
343 344 345 346 347 348 349 350 351 352
            if let dataBase = self.dbConnections.forAccount(account: accountId) {
                let success = self.interactionHepler
                    .updateInteractionWithDaemonID(interactionDaemonID: daemonID,
                                                   interactionStatus: InteractionStatus(status: transferStatus).rawValue,
                                                   dataBase: dataBase)
                if success {
                    completable(.completed)
                } else {
                    completable(.error(DBBridgingError.updateIntercationFailed))
                }
353
            } else {
354
                completable(.error(DBBridgingError.updateIntercationFailed))
355 356 357 358 359
            }
            return Disposables.create { }
        }
    }

360
    func setMessagesAsRead(messagesIDs: [Int64], withStatus status: MessageStatus, accountId: String) -> Completable {
361
        return Completable.create { [unowned self] completable in
362 363 364 365 366 367 368 369 370 371 372 373 374 375
            if let dataBase = self.dbConnections.forAccount(account: accountId) {
                var success = true
                for messageId in messagesIDs {
                    if !self.interactionHepler
                        .updateInteractionStatusWithID(interactionID: messageId,
                                                 interactionStatus: InteractionStatus(status: status).rawValue,
                                                 dataBase: dataBase) {
                        success = false
                    }
                }
                if success {
                    completable(.completed)
                } else {
                    completable(.error(DBBridgingError.saveMessageFailed))
376 377 378 379 380
                }
            } else {
                completable(.error(DBBridgingError.saveMessageFailed))
            }
            return Disposables.create { }
381
        }
382 383
    }

384 385 386
    func clearHistoryFor(accountId: String,
                         and participantUri: String,
                         keepConversation: Bool) -> Completable {
387 388
        return Completable.create { [unowned self] completable in
            do {
389
                guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
390 391 392
                    throw DBBridgingError.deleteConversationFailed
                }
                try dataBase.transaction {
393
                    guard (try self.getProfile(for: participantUri, createIfNotExists: false, accounId: accountId)) != nil else {
394 395
                        throw DBBridgingError.deleteConversationFailed
                    }
396 397 398
                    guard let conversationsId = try self.getConversationsFor(contactUri: participantUri,
                                                                             createIfNotExists: true,
                                                                             dataBase: dataBase) else {
399 400
                            throw DBBridgingError.deleteConversationFailed
                    }
401
                    guard let interactions = try self.interactionHepler
402 403 404
                        .selectInteractionsForConversation(
                            conv: conversationsId,
                            dataBase: dataBase) else {
405 406 407 408
                                throw DBBridgingError.deleteConversationFailed
                    }
                    if !interactions.isEmpty {
                        if !self.interactionHepler
409
                            .deleteAllIntercations(conv: conversationsId, dataBase: dataBase) {
410
                            completable(.error(DBBridgingError.deleteConversationFailed))
411
                        }
412 413 414
                    }
                    if keepConversation {
                        completable(.completed)
415
                    } else {
416
                        let successConversations = self.conversationHelper
417
                            .deleteConversations(conversationID: conversationsId, dataBase: dataBase)
418 419
                        if successConversations {
                            completable(.completed)
420 421 422 423 424 425 426 427 428
                        } else {
                            completable(.error(DBBridgingError.deleteConversationFailed))
                        }
                    }
                }
            } catch {
                completable(.error(DBBridgingError.deleteConversationFailed))
            }
            return Disposables.create { }
429
        }
430 431
    }

432
    func profileObservable(for profileUri: String, createIfNotExists: Bool, accountId: String) -> Observable<Profile> {
433 434
        return Observable.create { observable in
            do {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
435
                if let profile = try self.getProfile(for: profileUri,
436 437
                                                     createIfNotExists: createIfNotExists,
                                                     accounId: accountId) {
438 439 440 441 442 443 444 445 446 447
                    observable.onNext(profile)
                    observable.on(.completed)
                }
            } catch {
                observable.on(.error(DBBridgingError.getProfileFailed))
            }
            return Disposables.create { }
        }
    }

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
    func accountProfileObservable(for accountId: String) -> Observable<AccountProfile> {
        return Observable.create { observable in
            guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
                observable.on(.error(DBBridgingError.getProfileFailed))
                return Disposables.create { }
            }
            guard let profile = self.profileHepler.getAccountProfile(dataBase: dataBase) else {
                observable.on(.error(DBBridgingError.getProfileFailed))
                return Disposables.create { }
            }
            observable.onNext(profile)
            observable.on(.completed)
            return Disposables.create { }
        }
    }
463

464 465 466 467 468 469
    func accountProfile(for accountId: String) -> AccountProfile? {
        guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
            return nil
        }
        return self.profileHepler.getAccountProfile(dataBase: dataBase)
    }
470

471 472 473 474
    func createOrUpdateRingProfile(profileUri: String, alias: String?, image: String?, accountId: String) -> Bool {
        guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
            return false
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
475
        let profile = Profile(profileUri, alias, image, ProfileType.ring.rawValue)
476 477 478 479 480 481 482
        do {
            try self.profileHepler.insertOrUpdateProfile(item: profile, dataBase: dataBase)
        } catch {
            return  false
        }
        return true
    }
483

484 485 486 487 488 489 490 491 492 493 494 495
    func saveAccountProfile(alias: String?, photo: String?, accountId: String) -> Bool {
        guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
            return false
        }
        return self.profileHepler.updateAccountProfile(accountAlias: alias,
                                                       accountPhoto: photo,
                                                       dataBase: dataBase)
    }

    // MARK: Private functions
    private func buildConversationsForAccount(accountId: String) throws -> [ConversationModel] {
        guard let dataBase = self.dbConnections.forAccount(account: accountId) else {
496 497
            throw DBBridgingError.getConversationFailed
        }
498 499 500 501
        var conversationsToReturn = [ConversationModel]()

        guard let conversations = try self.conversationHelper.selectAll(dataBase: dataBase),
            !conversations.isEmpty else {
502 503 504
                // if there is no conversation for account return empty list
                return conversationsToReturn
        }
505 506 507 508
        for conversationID in conversations.map({$0.id}) {
            guard let participants = try self.getParticipantsForConversation(conversationID: conversationID,
                dataBase: dataBase),
                let partisipant = participants.first else {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
509
                    continue
510
            }
511 512
            guard let participantProfile = try self.profileHepler.selectProfile(profileURI: partisipant,
                                                                                dataBase: dataBase) else {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
513
                continue
514
            }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
515 516 517 518
            let type = participantProfile.uri.contains("sip:") ? URIType.sip : URIType.ring
            let uri = JamiURI.init(schema: type, infoHach: participantProfile.uri)
            let conversationModel = ConversationModel(withParticipantUri: uri,
                                                      accountId: accountId)
519
            conversationModel.participantProfile = participantProfile
520
            conversationModel.conversationId = String(conversationID)
521 522
            var messages = [MessageModel]()
            guard let interactions = try self.interactionHepler
523 524 525
                .selectInteractionsForConversation(
                    conv: conversationID,
                    dataBase: dataBase) else {
526
                        continue
527 528
            }
            for interaction in interactions {
529
                let author = interaction.author == participantProfile.uri
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
530
                    ? participantProfile.uri : ""
531
                if let message = self.convertToMessage(interaction: interaction, author: author) {
532 533 534
                    messages.append(message)
                }
            }
535 536
            conversationModel.messages = messages
            conversationsToReturn.append(conversationModel)
537 538 539 540
        }
        return conversationsToReturn
    }

541 542 543 544
    private func getParticipantsForConversation(conversationID: Int64, dataBase: Connection) throws -> [String]? {
        guard let conversations = try self.conversationHelper
            .selectConversations(conversationId: conversationID,
                                 dataBase: dataBase) else {
545 546
            return nil
        }
547
        return conversations.map({$0.participant})
548 549
    }

550
    private func convertToMessage(interaction: Interaction, author: String) -> MessageModel? {
551
        if interaction.type != InteractionType.text.rawValue &&
552
            interaction.type != InteractionType.contact.rawValue &&
553 554 555
            interaction.type != InteractionType.call.rawValue &&
            interaction.type != InteractionType.iTransfer.rawValue &&
            interaction.type != InteractionType.oTransfer.rawValue {
556 557
            return nil
        }
558 559 560 561
        let content = (interaction.type == InteractionType.call.rawValue
        || interaction.type == InteractionType.contact.rawValue) ?
            GeneratedMessage.init(from: interaction.body).toMessage(with: Int(interaction.duration))
            : interaction.body
562 563 564
        let date = Date(timeIntervalSince1970: TimeInterval(interaction.timestamp))
        let message = MessageModel(withId: interaction.daemonID,
                                   receivedDate: date,
565
                                   content: content,
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
566
                                   authorURI: author,
567
                                   incoming: interaction.incoming)
568 569 570 571
        let isTransfer =    interaction.type == InteractionType.iTransfer.rawValue ||
                            interaction.type == InteractionType.oTransfer.rawValue
        if  interaction.type == InteractionType.contact.rawValue ||
            interaction.type == InteractionType.call.rawValue {
572
            message.isGenerated = true
573 574 575
        } else if isTransfer {
            message.isGenerated = false
            message.isTransfer = true
576
        }
577
        if let status: InteractionStatus = InteractionStatus(rawValue: interaction.status) {
578 579 580 581 582
            if isTransfer {
                message.transferStatus = status.toDataTransferStatus()
            } else {
                message.status = status.toMessageStatus()
            }
583
        }
584
        message.messageId = interaction.id
585 586 587
        return message
    }

588
    // swiftlint:disable:next function_parameter_count
589
    private func addMessageTo(conversation conversationID: Int64,
590
                              author: String?,
591
                              interactionType: InteractionType,
592 593 594
                              message: MessageModel,
                              duration: Int,
                              dataBase: Connection) -> Int64? {
595 596 597 598
        var status = InteractionStatus.unknown.rawValue
        if interactionType == .oTransfer {
            status = InteractionStatus(status: message.transferStatus).rawValue
        }
599
        let timeInterval = message.receivedDate.timeIntervalSince1970
600 601
        let interaction = Interaction(defaultID, author,
                                      conversationID, Int64(timeInterval), Int64(duration),
602
                                      message.content, interactionType.rawValue,
603
                                     status, message.daemonId,
604
                                      message.incoming)
605
        return self.interactionHepler.insert(item: interaction, dataBase: dataBase)
606 607
    }

608 609 610
    private func getProfile(for profileUri: String, createIfNotExists: Bool, accounId: String) throws -> Profile? {
        guard let dataBase = self.dbConnections.forAccount(account: accounId) else {
            throw DataAccessError.datastoreConnectionError
611
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
612
        if let profile = try self.profileHepler.selectProfile(profileURI: profileUri, dataBase: dataBase) {
613 614
            return profile
        }
615 616 617 618
        if !createIfNotExists {
            return nil
        }
        // for now we use template profile
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
619
        let profile = self.createTemplateProfile(uri: profileUri)
620
        if self.profileHepler.insert(item: profile, dataBase: dataBase) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
621
            return try self.profileHepler.selectProfile(profileURI: profileUri, dataBase: dataBase)
622 623 624 625
        }
        return nil
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
626 627 628
    private func createTemplateProfile(uri: String) -> Profile {
        let type = uri.contains("ring") ? ProfileType.ring : ProfileType.sip
        return Profile(uri, nil, nil, type.rawValue)
629 630
    }

631 632 633
    private func getConversationsFor(contactUri: String,
                                     createIfNotExists: Bool, dataBase: Connection) throws -> Int64? {
        if let contactConversations = try self.conversationHelper
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
634 635 636
            .selectConversationsForProfile(profileUri: contactUri, dataBase: dataBase),
            let conv = contactConversations.first {
            return conv.id
637 638 639 640 641
        }
        if !createIfNotExists {
            return nil
        }
        let conversationID = Int64(arc4random_uniform(10000000))
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
642 643
        _ = self.profileHepler.insert(item: self.createTemplateProfile(uri: contactUri), dataBase: dataBase)
        let conversationForContact = Conversation(conversationID, contactUri)
644
        if !self.conversationHelper.insert(item: conversationForContact, dataBase: dataBase) {
645 646
            return nil
        }
647
        return try self.conversationHelper
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
648
            .selectConversationsForProfile(profileUri: contactUri, dataBase: dataBase)?.first?.id
649 650
    }
}