chatsecureios / ChatSecure / Classes / Controllers / OTROMEMOStorageManager.swift @ 8d76e2e3
History | View | Annotate | Download (7.87 KB)
1 |
// |
---|---|
2 |
// OTROMEMOStorageCoordinator.swift |
3 |
// ChatSecure |
4 |
// |
5 |
// Created by David Chiles on 9/15/16. |
6 |
// Copyright © 2016 Chris Ballinger. All rights reserved. |
7 |
// |
8 |
|
9 |
import Foundation |
10 |
import YapDatabase |
11 |
|
12 |
/** |
13 |
* Storage for XMPP-OMEMO |
14 |
* This class handles storage of devies as it relates to our and our buddies device(s). |
15 |
* Create one per XMPP account. |
16 |
*/ |
17 |
open class OTROMEMOStorageManager { |
18 |
let databaseConnection:YapDatabaseConnection |
19 |
let accountKey:String |
20 |
let accountCollection:String |
21 |
|
22 |
/** |
23 |
Create an OTROMEMOStorageManager. |
24 |
|
25 |
- parameter accountKey: The yap account key |
26 |
- parameter accountCollection: They yap account collection |
27 |
- parameter databaseConnection: The yap Datatbase connection to perform all the saves and gets from. |
28 |
*/ |
29 |
init(accountKey:String, accountCollection:String, databaseConnection:YapDatabaseConnection) { |
30 |
self.accountKey = accountKey |
31 |
self.accountCollection = accountCollection |
32 |
self.databaseConnection = databaseConnection |
33 |
} |
34 |
|
35 |
/** |
36 |
Convenience method that uses the class database connection. |
37 |
Retrievs all the devices for a given yap key and collection. Could be either for a buddy or an account. |
38 |
|
39 |
- parameter yapKey: The yap key for the account or buddy |
40 |
- parameter yapCollection: The yap collection for the account or buddy |
41 |
|
42 |
- returns: An Array of OTROMEMODevices. If there are no devices the array will be empty. |
43 |
**/ |
44 |
open func getDevicesForParentYapKey(_ yapKey:String, yapCollection:String, trusted:Bool?) -> [OTROMEMODevice] { |
45 |
var result:[OTROMEMODevice]? |
46 |
self.databaseConnection.read { (transaction) in |
47 |
if let trust = trusted { |
48 |
result = OTROMEMODevice.allDevices(forParentKey: yapKey, collection: yapCollection, trusted: trust, transaction: transaction) |
49 |
} else { |
50 |
result = OTROMEMODevice.allDevices(forParentKey: yapKey, collection: yapCollection, transaction: transaction) |
51 |
} |
52 |
} |
53 |
return result ?? [OTROMEMODevice](); |
54 |
} |
55 |
|
56 |
/** |
57 |
Uses the class account key and collection to get all devices. |
58 |
|
59 |
- returns: An Array of OTROMEMODevices. If there are no devices the array will be empty. |
60 |
*/ |
61 |
open func getDevicesForOurAccount(_ trusted:Bool?) -> [OTROMEMODevice] { |
62 |
return self.getDevicesForParentYapKey(self.accountKey, yapCollection: self.accountCollection, trusted: trusted) |
63 |
} |
64 |
|
65 |
/** |
66 |
Uses the class account key and collection to get all devices for a given bare JID. Uses the class database connection. |
67 |
|
68 |
- parameter username: The bare JID for the buddy. |
69 |
|
70 |
- returns: An Array of OTROMEMODevices. If there are no devices the array will be empty. |
71 |
*/ |
72 |
open func getDevicesForBuddy(_ username:String, trusted:Bool?) -> [OTROMEMODevice] { |
73 |
guard let jid = XMPPJID(string: username) else { return [] } |
74 |
var result: [OTROMEMODevice] = [] |
75 |
self.databaseConnection.read { (transaction) in |
76 |
if let buddy = OTRXMPPBuddy.fetchBuddy(jid: jid, accountUniqueId: self.accountKey, transaction: transaction) { |
77 |
if let trust = trusted { |
78 |
result = OTROMEMODevice.allDevices(forParentKey: buddy.uniqueId, collection: OTRBuddy.collection, trusted: trust, transaction: transaction) |
79 |
} else { |
80 |
result = OTROMEMODevice.allDevices(forParentKey: buddy.uniqueId, collection: OTRBuddy.collection, transaction: transaction) |
81 |
} |
82 |
} |
83 |
} |
84 |
return result; |
85 |
} |
86 |
|
87 |
/** |
88 |
Store devices for a yap key/collection |
89 |
|
90 |
- parameter devices: An array of the device numbers. Should be UInt32. |
91 |
- parameter parentYapKey: The yap key to attach the device to |
92 |
- parameter parentYapCollection: the yap collection to attach the device to |
93 |
- parameter transaction: the database transaction to perform the saves on |
94 |
*/ |
95 |
fileprivate func storeDevices(_ devices:[NSNumber], parentYapKey:String, parentYapCollection:String, transaction:YapDatabaseReadWriteTransaction) { |
96 |
|
97 |
let previouslyStoredDevices = OTROMEMODevice.allDevices(forParentKey: parentYapKey, collection: parentYapCollection, transaction: transaction) |
98 |
let previouslyStoredDevicesIds = previouslyStoredDevices.map({ (device) -> NSNumber in |
99 |
return device.deviceId |
100 |
}) |
101 |
let previouslyStoredDevicesIdSet = Set(previouslyStoredDevicesIds) |
102 |
let newDeviceSet = Set(devices) |
103 |
|
104 |
if (devices.count == 0) { |
105 |
// Remove all devices |
106 |
previouslyStoredDevices.forEach({ (device) in |
107 |
device.remove(with: transaction) |
108 |
}) |
109 |
} else if (previouslyStoredDevicesIdSet != newDeviceSet) { |
110 |
//New Devices to be saved and list to be reworked |
111 |
let devicesToRemove:Set<NSNumber> = previouslyStoredDevicesIdSet.subtracting(newDeviceSet) |
112 |
let devicesToAdd:Set<NSNumber> = newDeviceSet.subtracting(previouslyStoredDevicesIdSet) |
113 |
|
114 |
// Instead of fulling removing devices, mark them as removed for historical purposes |
115 |
devicesToRemove.forEach({ (deviceId) in |
116 |
let deviceKey = OTROMEMODevice.yapKey(withDeviceId: deviceId, parentKey: parentYapKey, parentCollection: parentYapCollection) |
117 |
guard var device = transaction.object(forKey: deviceKey, inCollection: OTROMEMODevice.collection) as? OTROMEMODevice else { |
118 |
return |
119 |
} |
120 |
device = device.copy() as! OTROMEMODevice |
121 |
device.trustLevel = .removed |
122 |
transaction.setObject(device, forKey: device.uniqueId, inCollection: OTROMEMODevice.collection) |
123 |
}) |
124 |
|
125 |
devicesToAdd.forEach({ (deviceId) in |
126 |
|
127 |
var trustLevel = OMEMOTrustLevel.untrustedNew |
128 |
if (previouslyStoredDevices.count == 0) { |
129 |
//This is the first time we're seeing a device list for this account/buddy so it should be saved as TOFU |
130 |
trustLevel = .trustedTofu |
131 |
} |
132 |
|
133 |
let newDevice = OTROMEMODevice(deviceId: deviceId, trustLevel:trustLevel, parentKey: parentYapKey, parentCollection: parentYapCollection, publicIdentityKeyData: nil, lastSeenDate:Date()) |
134 |
newDevice.save(with: transaction) |
135 |
}) |
136 |
|
137 |
} |
138 |
} |
139 |
|
140 |
/** |
141 |
Store devices for this account. These should come from the OMEMO device-list |
142 |
|
143 |
- parameter devices: An array of the device numbers. Should be UInt32. |
144 |
*/ |
145 |
open func storeOurDevices(_ devices:[NSNumber]) { |
146 |
self.databaseConnection.asyncReadWrite { (transaction) in |
147 |
self.storeDevices(devices, parentYapKey: self.accountKey, parentYapCollection: self.accountCollection, transaction: transaction) |
148 |
} |
149 |
} |
150 |
|
151 |
/** |
152 |
Store devices for a buddy connected to this account. These should come from the OMEMO device-list |
153 |
|
154 |
- parameter devices: An array of the device numbers. Should be UInt32. |
155 |
- parameter buddyUsername: The bare JID for the buddy. |
156 |
*/ |
157 |
open func storeBuddyDevices(_ devices:[NSNumber], buddyUsername:String, completion:(()->Void)?) { |
158 |
self.databaseConnection.asyncReadWrite { (transaction) in |
159 |
// Fetch the buddy from the database. |
160 |
guard let jid = XMPPJID(string: buddyUsername), let buddy = OTRXMPPBuddy.fetchBuddy(jid: jid, accountUniqueId: self.accountKey, transaction: transaction) else { |
161 |
// If this is teh first launch the buddy will not be in the buddy list becuase the roster comes in after device list from PEP. |
162 |
DDLogWarn("Could not find buddy to store devices \(buddyUsername)") |
163 |
return |
164 |
} |
165 |
self.storeDevices(devices, parentYapKey: buddy.uniqueId, parentYapCollection: buddy.threadCollection, transaction: transaction) |
166 |
completion?() |
167 |
} |
168 |
} |
169 |
} |