ContactsService.swift 12.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 *  Copyright (C) 2017 Savoir-faire Linux Inc.
 *
 *  Author: Silbino Gonçalves Matado <silbino.gmatado@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 Contacts
import SwiftyBeaver
import RxSwift

enum ContactServiceError: Error {
    case acceptTrustRequestFailed
    case diacardTrusRequestFailed
    case vCardSerializationFailed
    case loadVCardFailed
    case saveVCardFailed
}

class ContactsService {

    fileprivate let contactsAdapter: ContactsAdapter
    fileprivate let log = SwiftyBeaver.self
37
    fileprivate let disposeBag = DisposeBag()
38 39 40 41

    let contactRequests = Variable([ContactRequestModel]())
    let contacts = Variable([ContactModel]())

42 43
    let contactStatus = PublishSubject<ContactModel>()

44 45
    fileprivate let responseStream = PublishSubject<ServiceEvent>()
    var sharedResponseStream: Observable<ServiceEvent>
46
    let dbManager = DBManager(profileHepler: ProfileDataHelper(), conversationHelper: ConversationDataHelper(), interactionHepler: InteractionDataHelper())
47

48 49
    init(withContactsAdapter contactsAdapter: ContactsAdapter) {
        self.contactsAdapter = contactsAdapter
50 51
        self.responseStream.disposed(by: disposeBag)
        self.sharedResponseStream = responseStream.share()
52 53 54 55 56 57 58 59 60 61 62 63
        ContactsAdapter.delegate = self
    }

    func contact(withRingId ringId: String) -> ContactModel? {
        guard let contact = self.contacts.value.filter({ $0.ringId == ringId }).first else {
            return nil
        }

        return contact
    }

    func contactRequest(withRingId ringId: String) -> ContactRequestModel? {
64
        guard let contactRequest = self.contactRequests.value.filter({ $0.ringId == ringId }).first else {
65 66 67
            return nil
        }

68
        return contactRequest
69 70 71 72 73 74 75 76 77 78 79
    }

    func loadContacts(withAccount account: AccountModel) {
        //Load contacts from daemon
        let contactsDictionaries = self.contactsAdapter.contacts(withAccountId: account.id)

        //Serialize them
        if let contacts = contactsDictionaries?.map({ contactDict in
            return ContactModel(withDictionary: contactDict)
        }) {
            for contact in contacts {
80 81
                if self.contacts.value.index(of: contact) == nil {
                    self.contacts.value.append(contact)
82
                    self.log.debug("contact: \(String(describing: contact.userName))")
83
                }
84 85 86 87 88 89 90 91 92 93 94 95 96
            }
        }
    }

    func loadContactRequests(withAccount account: AccountModel) {
        //Load trust requests from daemon
        let trustRequestsDictionaries = self.contactsAdapter.trustRequests(withAccountId: account.id)

        //Create contact requests from daemon trust requests
        if let contactRequests = trustRequestsDictionaries?.map({ dictionary in
            return ContactRequestModel(withDictionary: dictionary, accountId: account.id)
        }) {
            for contactRequest in contactRequests {
97 98 99
                if self.contactRequest(withRingId: contactRequest.ringId) == nil {
                    self.contactRequests.value.append(contactRequest)
                }
100 101 102 103 104 105 106
            }
        }
    }

    func accept(contactRequest: ContactRequestModel, withAccount account: AccountModel) -> Observable<Void> {
        return Observable.create { [unowned self] observable in
            let success = self.contactsAdapter.acceptTrustRequest(fromContact: contactRequest.ringId,
107
                                                                  withAccountId: account.id)
108
            if success {
109 110 111 112 113 114 115 116 117 118
                var stringImage: String?
                if let vCard = contactRequest.vCard, let image = vCard.imageData {
                    stringImage = image.base64EncodedString()
                }
                let name = VCardUtils.getName(from: contactRequest.vCard)
                _ = self.dbManager
                    .createOrUpdateRingProfile(profileUri: contactRequest.ringId,
                                               alias: name,
                                               image: stringImage,
                                               status: ProfileStatus.trusted)
119 120 121 122 123
                var event = ServiceEvent(withEventType: .contactAdded)
                event.addEventInput(.accountId, value: account.id)
                event.addEventInput(.state, value: true)
                event.addEventInput(.uri, value: contactRequest.ringId)
                self.responseStream.onNext(event)
124 125 126 127 128 129 130 131 132
                observable.on(.completed)
            } else {
                observable.on(.error(ContactServiceError.acceptTrustRequestFailed))
            }

            return Disposables.create { }
        }
    }

133
    func discard(contactRequest: ContactRequestModel, withAccountId accountId: String) -> Observable<Void> {
134 135
        return Observable.create { [unowned self] observable in
            let success = self.contactsAdapter.discardTrustRequest(fromContact: contactRequest.ringId,
136
                                                                   withAccountId: accountId)
137 138 139 140 141

            //Update the Contact request list
            self.removeContactRequest(withRingId: contactRequest.ringId)

            if success {
142
                var event = ServiceEvent(withEventType: .contactRequestDiscarded)
143
                event.addEventInput(.accountId, value: accountId)
144 145
                event.addEventInput(.uri, value: contactRequest.ringId)
                self.responseStream.onNext(event)
146 147 148 149 150 151 152 153
                observable.on(.completed)
            } else {
                observable.on(.error(ContactServiceError.diacardTrusRequestFailed))
            }
            return Disposables.create { }
        }
    }

154
    func sendContactRequest(toContactRingId ringId: String, vCard: CNContact?, withAccount account: AccountModel) -> Completable {
155 156
        return Completable.create { [unowned self] completable in
            do {
157 158 159

                var payload: Data?
                if let vCard = vCard {
160
                  payload = try CNContactVCardSerialization.dataWithImageAndUUID(from: vCard, andImageCompression: 40000)
161
                }
162
                self.contactsAdapter.sendTrustRequest(toContact: ringId, payload: payload, withAccountId: account.id)
163 164 165 166
                var event = ServiceEvent(withEventType: .contactRequestSended)
                event.addEventInput(.accountId, value: account.id)
                event.addEventInput(.uri, value: ringId)
                self.responseStream.onNext(event)
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
                completable(.completed)
            } catch {
                completable(.error(ContactServiceError.vCardSerializationFailed))
            }
            return Disposables.create { }
        }
    }

    func addContact(contact: ContactModel, withAccount account: AccountModel) -> Observable<Void> {
        return Observable.create { [unowned self] observable in
            self.contactsAdapter.addContact(withURI: contact.ringId, accountId: account.id)
            self.contacts.value.append(contact)
            observable.on(.completed)
            return Disposables.create { }
        }
    }

184
    func removeContact(withRingId ringId: String, ban: Bool, withAccountId accountId: String) -> Observable<Void> {
185
        return Observable.create { [unowned self] observable in
186
            self.contactsAdapter.removeContact(withURI: ringId, accountId: accountId, ban: ban)
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
            self.removeContactRequest(withRingId: ringId)
            observable.on(.completed)
            return Disposables.create { }
        }
    }

    fileprivate func removeContactRequest(withRingId ringId: String) {
        guard let contactRequestToRemove = self.contactRequests.value.filter({ $0.ringId == ringId}).first else {
            return
        }
        guard let index = self.contactRequests.value.index(where: { $0 === contactRequestToRemove }) else {
            return
        }
        self.contactRequests.value.remove(at: index)
    }
}

extension ContactsService: ContactsAdapterDelegate {

    func incomingTrustRequestReceived(from senderAccount: String, to accountId: String, withPayload payload: Data, receivedDate: Date) {

208 209 210 211
        var vCard: CNContact?
        do {
            let vCards = try CNContactVCardSerialization.contacts(with: payload)
            vCard = vCards.first
212
        } catch {
213
            vCard = nil
214 215
            log.error("Unable to parse the vCard :\(error)")
        }
216
        
217 218 219 220 221 222 223
        //Update trust request list
        if self.contactRequest(withRingId: senderAccount) == nil {
            let contactRequest = ContactRequestModel(withRingId: senderAccount,
                                                     vCard: vCard,
                                                     receivedDate: receivedDate,
                                                     accountId: accountId)
            self.contactRequests.value.append(contactRequest)
224 225 226 227 228
            var event = ServiceEvent(withEventType: .contactRequestReceived)
            event.addEventInput(.accountId, value: accountId)
            event.addEventInput(.uri, value: senderAccount)
            event.addEventInput(.date, value: receivedDate)
            self.responseStream.onNext(event)
229 230 231 232 233 234 235 236 237
        } else {
            // If the contact request already exists, update it's relevant data
            if let contactRequest = self.contactRequest(withRingId: senderAccount) {
                contactRequest.vCard = vCard
                contactRequest.receivedDate = receivedDate
            }
            log.debug("Incoming trust request received from :\(senderAccount)")
        }

238 239 240 241 242
    }

    func contactAdded(contact uri: String, withAccountId accountId: String, confirmed: Bool) {
        //Update trust request list
        self.removeContactRequest(withRingId: uri)
243 244 245 246
        // update contact status
        if let contact = self.contact(withRingId: uri) {
            if contact.confirmed != confirmed {
                contact.confirmed = confirmed
247
                self.contactStatus.onNext(contact)
248 249 250 251 252 253 254
                if confirmed {
                    var event = ServiceEvent(withEventType: .contactAdded)
                    event.addEventInput(.state, value: confirmed)
                    event.addEventInput(.accountId, value: accountId)
                    event.addEventInput(.uri, value: uri)
                    self.responseStream.onNext(event)
                }
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
            }
        }
            //sync contacts with daemon contacts
        else {

            let contactsDictionaries = self.contactsAdapter.contacts(withAccountId: accountId)

            //Serialize them
            if let contacts = contactsDictionaries?.map({ contactDict in
                return ContactModel(withDictionary: contactDict)
            }) {
                for contact in contacts {
                    if self.contacts.value.index(of: contact) == nil {
                        self.contacts.value.append(contact)
                        contactStatus.onNext(contact)
                    }
                }
            }

        }
275 276 277 278
        log.debug("Contact added :\(uri)")
    }

    func contactRemoved(contact uri: String, withAccountId accountId: String, banned: Bool) {
279 280 281 282 283
        guard let contactToRemove = self.contacts.value.filter({ $0.ringId == uri}).first else {
            return
        }
        contactToRemove.banned = banned
        self.contactStatus.onNext(contactToRemove)
284 285
        log.debug("Contact removed :\(uri)")
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
286 287 288 289 290 291 292 293 294

    // MARK: - profile

    func saveVCard(vCard: CNContact, forContactWithRingId ringID: String) -> Observable<Void> {
        let vCardSaved = VCardUtils.saveVCard(vCard: vCard, withName: ringID, inFolder: VCardFolders.contacts.rawValue)
        return vCardSaved
    }

    func loadVCard(forContactWithRingId ringID: String) -> Single<CNContact> {
295 296
        let vCard = VCardUtils.loadVCard(named: ringID, inFolder: VCardFolders.contacts.rawValue, contactService: self)
        return vCard
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
297
    }
298 299 300 301 302

    func getProfileForUri(uri: String) ->Observable<Profile> {
        return self.dbManager.profileObservable(for: uri, createIfNotExists: false)
            .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
    }
303
}