mirror of https://github.com/oxen-io/session-ios
Started adding logic for the outbox endpoint
Moved the BlindedIdMapping retrieval logic to ContactUtilities so it's reusable Added the 'outbox' endpoints (need testing as they aren't deployed to test yet)pull/592/head
parent
6936f35f2a
commit
8a7db1d48f
@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
enum ContactUtilities {
|
|
||||||
private static func approvedContact(in threadObject: Any, using transaction: Any) -> Contact? {
|
|
||||||
guard let thread: TSContactThread = threadObject as? TSContactThread else { return nil }
|
|
||||||
guard thread.shouldBeVisible else { return nil }
|
|
||||||
guard let contact: Contact = Storage.shared.getContact(with: thread.contactSessionID(), using: transaction) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard contact.didApproveMe else { return nil }
|
|
||||||
|
|
||||||
return contact
|
|
||||||
}
|
|
||||||
|
|
||||||
static func getAllContacts() -> [String] {
|
|
||||||
// Collect all contacts
|
|
||||||
var result: [Contact] = []
|
|
||||||
Storage.read { transaction in
|
|
||||||
TSContactThread.enumerateCollectionObjects(with: transaction) { object, _ in
|
|
||||||
guard let contact: Contact = approvedContact(in: object, using: transaction) else { return }
|
|
||||||
|
|
||||||
result.append(contact)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func getDisplayName(for publicKey: String) -> String {
|
|
||||||
return Storage.shared.getContact(with: publicKey)?.displayName(for: .regular) ?? publicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the current user
|
|
||||||
if let index = result.firstIndex(where: { $0.sessionID == getUserHexEncodedPublicKey() }) {
|
|
||||||
result.remove(at: index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort alphabetically
|
|
||||||
return result
|
|
||||||
.sorted(by: { lhs, rhs in
|
|
||||||
(lhs.displayName(for: .regular) ?? lhs.sessionID) < (rhs.displayName(for: .regular) ?? rhs.sessionID)
|
|
||||||
})
|
|
||||||
.map { $0.sessionID }
|
|
||||||
}
|
|
||||||
|
|
||||||
static func enumerateApprovedContactThreads(with block: @escaping (TSContactThread, Contact, UnsafeMutablePointer<ObjCBool>) -> ()) {
|
|
||||||
Storage.read { transaction in
|
|
||||||
TSContactThread.enumerateCollectionObjects(with: transaction) { object, stop in
|
|
||||||
guard let contactThread: TSContactThread = object as? TSContactThread else { return }
|
|
||||||
guard let contact: Contact = approvedContact(in: object, using: transaction) else { return }
|
|
||||||
|
|
||||||
block(contactThread, contact, stop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,99 @@
|
|||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
enum ContactUtilities {
|
||||||
|
private static func approvedContact(in threadObject: Any, using transaction: Any) -> Contact? {
|
||||||
|
guard let thread: TSContactThread = threadObject as? TSContactThread else { return nil }
|
||||||
|
guard thread.shouldBeVisible else { return nil }
|
||||||
|
guard let contact: Contact = Storage.shared.getContact(with: thread.contactSessionID(), using: transaction) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard contact.didApproveMe else { return nil }
|
||||||
|
|
||||||
|
return contact
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getAllContacts() -> [String] {
|
||||||
|
// Collect all contacts
|
||||||
|
var result: [Contact] = []
|
||||||
|
Storage.read { transaction in
|
||||||
|
TSContactThread.enumerateCollectionObjects(with: transaction) { object, _ in
|
||||||
|
guard let contact: Contact = approvedContact(in: object, using: transaction) else { return }
|
||||||
|
|
||||||
|
result.append(contact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func getDisplayName(for publicKey: String) -> String {
|
||||||
|
return Storage.shared.getContact(with: publicKey)?.displayName(for: .regular) ?? publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the current user
|
||||||
|
if let index = result.firstIndex(where: { $0.sessionID == getUserHexEncodedPublicKey() }) {
|
||||||
|
result.remove(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort alphabetically
|
||||||
|
return result
|
||||||
|
.sorted(by: { lhs, rhs in
|
||||||
|
(lhs.displayName(for: .regular) ?? lhs.sessionID) < (rhs.displayName(for: .regular) ?? rhs.sessionID)
|
||||||
|
})
|
||||||
|
.map { $0.sessionID }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func enumerateApprovedContactThreads(with block: @escaping (TSContactThread, Contact, UnsafeMutablePointer<ObjCBool>) -> ()) {
|
||||||
|
Storage.read { transaction in
|
||||||
|
TSContactThread.enumerateCollectionObjects(with: transaction) { object, stop in
|
||||||
|
guard let contactThread: TSContactThread = object as? TSContactThread else { return }
|
||||||
|
guard let contact: Contact = approvedContact(in: object, using: transaction) else { return }
|
||||||
|
|
||||||
|
block(contactThread, contact, stop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func mapping(for blindedId: String, serverPublicKey: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> BlindedIdMapping? {
|
||||||
|
// TODO: Ensure the above case isn't going to be an issue due to legacy messages?.
|
||||||
|
// Unfortunately the whole point of id-blinding is to make it hard to reverse-engineer a standard
|
||||||
|
// sessionId, as a result in order to see if there is an unblinded contact for this blindedId we
|
||||||
|
// can only really generate blinded ids for each contact and check if any match
|
||||||
|
//
|
||||||
|
// Due to this we have made a few optimisations to try and early-out as often as possible, first
|
||||||
|
// we try to retrieve a direct cached mapping
|
||||||
|
var mappingResult: BlindedIdMapping? = dependencies.storage.getBlindedIdMapping(with: blindedId)
|
||||||
|
|
||||||
|
// No need to continue if we already have a result
|
||||||
|
if let mapping: BlindedIdMapping = mappingResult { return mapping }
|
||||||
|
|
||||||
|
// Then we try loop through all approved contact threads to see if one of those contacts can be blinded to match
|
||||||
|
ContactUtilities.enumerateApprovedContactThreads { contactThread, contact, stop in
|
||||||
|
guard dependencies.sodium.sessionId(contact.sessionID, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the mapping
|
||||||
|
let newMapping: BlindedIdMapping = BlindedIdMapping(blindedId: blindedId, sessionId: contact.sessionID, serverPublicKey: serverPublicKey)
|
||||||
|
dependencies.storage.cacheBlindedIdMapping(newMapping)
|
||||||
|
mappingResult = newMapping
|
||||||
|
stop.pointee = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish if we have a result
|
||||||
|
if let mapping: BlindedIdMapping = mappingResult { return mapping }
|
||||||
|
|
||||||
|
// Lastly loop through existing id mappings (in case the user is looking at a different SOGS but once had
|
||||||
|
// a thread with this contact in a different SOGS and had cached the mapping)
|
||||||
|
dependencies.storage.enumerateBlindedIdMapping { mapping, stop in
|
||||||
|
guard mapping.serverPublicKey != serverPublicKey else { return }
|
||||||
|
guard dependencies.sodium.sessionId(mapping.sessionId, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the new mapping
|
||||||
|
let newMapping: BlindedIdMapping = BlindedIdMapping(blindedId: blindedId, sessionId: mapping.sessionId, serverPublicKey: serverPublicKey)
|
||||||
|
dependencies.storage.cacheBlindedIdMapping(newMapping)
|
||||||
|
mappingResult = newMapping
|
||||||
|
stop.pointee = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappingResult
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue