diff --git a/SessionMessagingKit/LibSessionUtil/Config Handling/SessionUtil+Contacts.swift b/SessionMessagingKit/LibSessionUtil/Config Handling/SessionUtil+Contacts.swift index 26ce1fce0..703d6fe63 100644 --- a/SessionMessagingKit/LibSessionUtil/Config Handling/SessionUtil+Contacts.swift +++ b/SessionMessagingKit/LibSessionUtil/Config Handling/SessionUtil+Contacts.swift @@ -155,7 +155,7 @@ internal extension SessionUtil { .bytes .map { CChar(bitPattern: $0) } var contact: contacts_contact = contacts_contact() - guard contacts_get_or_create(conf, &contact, &sessionId) else { + guard contacts_get_or_construct(conf, &contact, &sessionId) else { SNLog("Unable to upsert contact from Config Message") return } diff --git a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64/libsession-util.a b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64/libsession-util.a index 3f991bb6a..16f752d7c 100644 Binary files a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64/libsession-util.a and b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64/libsession-util.a differ diff --git a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64_x86_64-simulator/libsession-util.a b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64_x86_64-simulator/libsession-util.a index 4ee48d353..3c72d9390 100644 Binary files a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64_x86_64-simulator/libsession-util.a and b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/ios-arm64_x86_64-simulator/libsession-util.a differ diff --git a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.h b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.h index a889c7cb9..ba27f79b9 100644 --- a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.h +++ b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.h @@ -67,7 +67,7 @@ bool contacts_get(const config_object* conf, contacts_contact* contact, const ch /// /// This is the method that should usually be used to create or update a contact, followed by /// setting fields in the contact, and then giving it to contacts_set(). -bool contacts_get_or_create( +bool contacts_get_or_construct( const config_object* conf, contacts_contact* contact, const char* session_id) __attribute__((warn_unused_result)); @@ -79,7 +79,7 @@ void contacts_set(config_object* conf, const contacts_contact* contact); // is simple enough; for example to update `approved` and leave everything else unchanged: // // contacts_contact c; -// if (contacts_get_or_create(conf, &c, some_session_id)) { +// if (contacts_get_or_construct(conf, &c, some_session_id)) { // const char* new_nickname = "Joe"; // c.approved = new_nickname; // contacts_set_or_create(conf, &c); @@ -92,6 +92,9 @@ void contacts_set(config_object* conf, const contacts_contact* contact); /// iteration; see details below. bool contacts_erase(config_object* conf, const char* session_id); +/// Returns the number of contacts. +size_t contacts_size(const config_object* conf); + /// Functions for iterating through the entire contact list, in sorted order. Intended use is: /// /// contacts_contact c; diff --git a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.hpp b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.hpp index 1601c6146..f97199c53 100644 --- a/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.hpp +++ b/SessionMessagingKit/LibSessionUtil/libsession-util.xcframework/session/config/contacts.hpp @@ -84,16 +84,17 @@ class Contacts : public ConfigBase { /// Similar to get(), but if the session ID does not exist this returns a filled-out /// contact_info containing the session_id (all other fields will be empty/defaulted). This is - /// intended to be combined with `set` to set-or-create a record. Note that this does not add - /// the session id to the contact list when called: that requires also calling `set` with this - /// value. - contact_info get_or_create(std::string_view pubkey_hex) const; + /// intended to be combined with `set` to set-or-create a record. + /// + /// NB: calling this does *not* add the session id to the contact list when called: that + /// requires also calling `set` with this value. + contact_info get_or_construct(std::string_view pubkey_hex) const; /// Sets or updates multiple contact info values at once with the given info. The usual use is /// to access the current info, change anything desired, then pass it back into set_contact, /// e.g.: /// - /// auto c = contacts.get_or_create(pubkey); + /// auto c = contacts.get_or_construct(pubkey); /// c.name = "Session User 42"; /// c.nickname = "BFF"; /// contacts.set(c); @@ -119,6 +120,12 @@ class Contacts : public ConfigBase { /// example. iterator erase(iterator it); + /// Returns the number of contacts. + size_t size() const; + + /// Returns true if the contact list is empty. + bool empty() const { return size() == 0; } + /// Iterators for iterating through all contacts. Typically you access this implicit via a for /// loop over the `Contacts` object: /// diff --git a/SessionMessagingKitTests/LibSessionUtil/ConfigContactsSpec.swift b/SessionMessagingKitTests/LibSessionUtil/ConfigContactsSpec.swift index b2f9c7744..f1426c85b 100644 --- a/SessionMessagingKitTests/LibSessionUtil/ConfigContactsSpec.swift +++ b/SessionMessagingKitTests/LibSessionUtil/ConfigContactsSpec.swift @@ -38,8 +38,10 @@ class ConfigContactsSpec: QuickSpec { let contactPtr: UnsafeMutablePointer? = nil expect(contacts_get(conf, contactPtr, &definitelyRealId)).to(beFalse()) + expect(contacts_size(conf)).to(equal(0)) + var contact2: contacts_contact = contacts_contact() - expect(contacts_get_or_create(conf, &contact2, &definitelyRealId)).to(beTrue()) + expect(contacts_get_or_construct(conf, &contact2, &definitelyRealId)).to(beTrue()) expect(contact2.name).to(beNil()) expect(contact2.nickname).to(beNil()) expect(contact2.approved).to(beFalse()) @@ -155,7 +157,7 @@ class ConfigContactsSpec: QuickSpec { .bytes .map { CChar(bitPattern: $0) } var contact5: contacts_contact = contacts_contact() - expect(contacts_get_or_create(conf2, &contact5, &anotherId)).to(beTrue()) + expect(contacts_get_or_construct(conf2, &contact5, &anotherId)).to(beTrue()) expect(contact5.name).to(beNil()) expect(contact5.nickname).to(beNil()) expect(contact5.approved).to(beFalse()) @@ -193,6 +195,8 @@ class ConfigContactsSpec: QuickSpec { // Iterate through and make sure we got everything we expected var sessionIds: [String] = [] var nicknames: [String] = [] + expect(contacts_size(conf)).to(equal(2)) + var contact6: contacts_contact = contacts_contact() let contactIterator: UnsafeMutablePointer = contacts_iterator_new(conf) while !contacts_iterator_done(contactIterator, &contact6) { @@ -211,6 +215,7 @@ class ConfigContactsSpec: QuickSpec { contacts_iterator_free(contactIterator) // Need to free the iterator expect(sessionIds.count).to(equal(2)) + expect(sessionIds.count).to(equal(contacts_size(conf))) expect(sessionIds.first).to(equal(String(cString: definitelyRealId.nullTerminated()))) expect(sessionIds.last).to(equal(String(cString: anotherId.nullTerminated()))) expect(nicknames.first).to(equal("Joey")) @@ -233,7 +238,7 @@ class ConfigContactsSpec: QuickSpec { .map { CChar(bitPattern: $0) } let profileKey7: [UInt8] = "qwerty".bytes var contact7: contacts_contact = contacts_contact() - expect(contacts_get_or_create(conf2, &contact7, &thirdId)).to(beTrue()) + expect(contacts_get_or_construct(conf2, &contact7, &thirdId)).to(beTrue()) nickname7.withUnsafeBufferPointer { contact7.nickname = $0.baseAddress } contact7.approved = true contact7.approved_me = true @@ -297,6 +302,8 @@ class ConfigContactsSpec: QuickSpec { // Validate the changes var sessionIds2: [String] = [] var nicknames2: [String] = [] + expect(contacts_size(conf)).to(equal(2)) + var contact8: contacts_contact = contacts_contact() let contactIterator2: UnsafeMutablePointer = contacts_iterator_new(conf) while !contacts_iterator_done(contactIterator2, &contact8) { diff --git a/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift b/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift index d0bee4fff..d97f36586 100644 --- a/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift +++ b/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift @@ -27,9 +27,9 @@ class OpenGroupAPISpec: QuickSpec { var dependencies: SMKDependencies! var response: (ResponseInfoType, Codable)? = nil - var pollResponse: [OpenGroupAPI.Endpoint: (ResponseInfoType, Codable?)]? + var pollResponse: (info: ResponseInfoType, data: [OpenGroupAPI.Endpoint: Codable])? var error: Error? - + describe("an OpenGroupAPI") { // MARK: - Configuration @@ -205,14 +205,16 @@ class OpenGroupAPISpec: QuickSpec { expect(error?.localizedDescription).to(beNil()) // Validate the response data - expect(pollResponse?.values).to(haveCount(3)) - expect(pollResponse?.keys).to(contain(.capabilities)) - expect(pollResponse?.keys).to(contain(.roomPollInfo("testRoom", 0))) - expect(pollResponse?.keys).to(contain(.roomMessagesRecent("testRoom"))) - expect(pollResponse?[.capabilities]?.0).to(beAKindOf(TestOnionRequestAPI.ResponseInfo.self)) + + expect(pollResponse?.data.count).to(equal(3)) + expect(pollResponse?.data.keys).to(contain(.capabilities)) + expect(pollResponse?.data.keys).to(contain(.roomPollInfo("testRoom", 0))) + expect(pollResponse?.data.keys).to(contain(.roomMessagesRecent("testRoom"))) + expect(pollResponse?.data[.capabilities]) + .to(beAKindOf(TestOnionRequestAPI.ResponseInfo.self)) // Validate request data - let requestData: TestOnionRequestAPI.RequestData? = (pollResponse?[.capabilities]?.0 as? TestOnionRequestAPI.ResponseInfo)?.requestData + let requestData: TestOnionRequestAPI.RequestData? = (pollResponse?.data[.capabilities] as? TestOnionRequestAPI.ResponseInfo)?.requestData expect(requestData?.urlString).to(equal("testserver/batch")) expect(requestData?.httpMethod).to(equal("POST")) expect(requestData?.publicKey).to(equal("88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b")) @@ -241,7 +243,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.roomMessagesRecent("testRoom"))) + expect(pollResponse?.data.keys).to(contain(.roomMessagesRecent("testRoom"))) } it("retrieves recent messages if there was a last message and it has not performed the initial poll and the last message was too long ago") { @@ -272,7 +274,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.roomMessagesRecent("testRoom"))) + expect(pollResponse?.data.keys).to(contain(.roomMessagesRecent("testRoom"))) } it("retrieves recent messages if there was a last message and it has performed an initial poll but it was not too long ago") { @@ -303,7 +305,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123))) + expect(pollResponse?.data.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123))) } it("retrieves recent messages if there was a last message and there has already been a poll this session") { @@ -334,7 +336,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123))) + expect(pollResponse?.data.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123))) } context("when unblinded") { @@ -370,8 +372,8 @@ class OpenGroupAPISpec: QuickSpec { expect(error?.localizedDescription).to(beNil()) // Validate the response data - expect(pollResponse?.keys).toNot(contain(.inbox)) - expect(pollResponse?.keys).toNot(contain(.outbox)) + expect(pollResponse?.data.keys).toNot(contain(.inbox)) + expect(pollResponse?.data.keys).toNot(contain(.outbox)) } } @@ -471,8 +473,8 @@ class OpenGroupAPISpec: QuickSpec { expect(error?.localizedDescription).to(beNil()) // Validate the response data - expect(pollResponse?.keys).to(contain(.inbox)) - expect(pollResponse?.keys).to(contain(.outbox)) + expect(pollResponse?.data.keys).to(contain(.inbox)) + expect(pollResponse?.data.keys).to(contain(.outbox)) } it("retrieves recent inbox messages if there was no last message") { @@ -498,7 +500,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.inbox)) + expect(pollResponse?.data.keys).to(contain(.inbox)) } it("retrieves inbox messages since the last message if there was one") { @@ -529,7 +531,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.inboxSince(id: 124))) + expect(pollResponse?.data.keys).to(contain(.inboxSince(id: 124))) } it("retrieves recent outbox messages if there was no last message") { @@ -555,7 +557,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.outbox)) + expect(pollResponse?.data.keys).to(contain(.outbox)) } it("retrieves outbox messages since the last message if there was one") { @@ -586,7 +588,7 @@ class OpenGroupAPISpec: QuickSpec { timeout: .milliseconds(100) ) expect(error?.localizedDescription).to(beNil()) - expect(pollResponse?.keys).to(contain(.outboxSince(id: 125))) + expect(pollResponse?.data.keys).to(contain(.outboxSince(id: 125))) } } } @@ -650,9 +652,9 @@ class OpenGroupAPISpec: QuickSpec { ) expect(error?.localizedDescription).to(beNil()) - let capabilitiesResponse: HTTP.BatchSubResponse? = (pollResponse?[.capabilities]?.1 as? HTTP.BatchSubResponse) - let pollInfoResponse: HTTP.BatchSubResponse? = (pollResponse?[.roomPollInfo("testRoom", 0)]?.1 as? HTTP.BatchSubResponse) - let messagesResponse: HTTP.BatchSubResponse<[Failable]>? = (pollResponse?[.roomMessagesRecent("testRoom")]?.1 as? HTTP.BatchSubResponse<[Failable]>) + let capabilitiesResponse: HTTP.BatchSubResponse? = (pollResponse?.data[.capabilities] as? HTTP.BatchSubResponse) + let pollInfoResponse: HTTP.BatchSubResponse? = (pollResponse?.data[.roomPollInfo("testRoom", 0)] as? HTTP.BatchSubResponse) + let messagesResponse: HTTP.BatchSubResponse<[Failable]>? = (pollResponse?.data[.roomMessagesRecent("testRoom")] as? HTTP.BatchSubResponse<[Failable]>) expect(capabilitiesResponse?.failedToParseBody).to(beFalse()) expect(pollInfoResponse?.failedToParseBody).to(beTrue()) expect(messagesResponse?.failedToParseBody).to(beTrue()) diff --git a/SessionMessagingKitTests/Sending & Receiving/MessageSenderEncryptionSpec.swift b/SessionMessagingKitTests/Sending & Receiving/MessageSenderEncryptionSpec.swift index aa3de4a23..514bb6af9 100644 --- a/SessionMessagingKitTests/Sending & Receiving/MessageSenderEncryptionSpec.swift +++ b/SessionMessagingKitTests/Sending & Receiving/MessageSenderEncryptionSpec.swift @@ -56,11 +56,14 @@ class MessageSenderEncryptionSpec: QuickSpec { } it("can encrypt correctly") { - let result = try? MessageSender.encryptWithSessionProtocol( - "TestMessage".data(using: .utf8)!, - for: "05\(TestConstants.publicKey)", - using: SMKDependencies(storage: mockStorage) - ) + let result = mockStorage.write { db in + try? MessageSender.encryptWithSessionProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "05\(TestConstants.publicKey)", + using: SMKDependencies(storage: mockStorage) + ) + } // Note: A Nonce is used for this so we can't compare the exact value when not mocked expect(result).toNot(beNil()) @@ -68,11 +71,14 @@ class MessageSenderEncryptionSpec: QuickSpec { } it("returns the correct value when mocked") { - let result = try? MessageSender.encryptWithSessionProtocol( - "TestMessage".data(using: .utf8)!, - for: "05\(TestConstants.publicKey)", - using: dependencies - ) + let result = mockStorage.write { db in + try? MessageSender.encryptWithSessionProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "05\(TestConstants.publicKey)", + using: dependencies + ) + } expect(result?.bytes).to(equal([1, 2, 3])) } @@ -83,51 +89,63 @@ class MessageSenderEncryptionSpec: QuickSpec { _ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db) } - expect { - try MessageSender.encryptWithSessionProtocol( - "TestMessage".data(using: .utf8)!, - for: "05\(TestConstants.publicKey)", - using: dependencies - ) + mockStorage.write { db in + expect { + try MessageSender.encryptWithSessionProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "05\(TestConstants.publicKey)", + using: dependencies + ) + } + .to(throwError(MessageSenderError.noUserED25519KeyPair)) } - .to(throwError(MessageSenderError.noUserED25519KeyPair)) } it("throws an error if the signature generation fails") { mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn(nil) - expect { - try MessageSender.encryptWithSessionProtocol( - "TestMessage".data(using: .utf8)!, - for: "05\(TestConstants.publicKey)", - using: dependencies - ) + mockStorage.write { db in + expect { + try MessageSender.encryptWithSessionProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "05\(TestConstants.publicKey)", + using: dependencies + ) + } + .to(throwError(MessageSenderError.signingFailed)) } - .to(throwError(MessageSenderError.signingFailed)) } it("throws an error if the encryption fails") { mockBox.when { $0.seal(message: anyArray(), recipientPublicKey: anyArray()) }.thenReturn(nil) - expect { - try MessageSender.encryptWithSessionProtocol( - "TestMessage".data(using: .utf8)!, - for: "05\(TestConstants.publicKey)", - using: dependencies - ) + mockStorage.write { db in + expect { + try MessageSender.encryptWithSessionProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "05\(TestConstants.publicKey)", + using: dependencies + ) + } + .to(throwError(MessageSenderError.encryptionFailed)) } - .to(throwError(MessageSenderError.encryptionFailed)) } } context("when encrypting with the blinded session protocol") { it("successfully encrypts") { - let result = try? MessageSender.encryptWithSessionBlindingProtocol( - "TestMessage".data(using: .utf8)!, - for: "15\(TestConstants.blindedPublicKey)", - openGroupPublicKey: TestConstants.serverPublicKey, - using: dependencies - ) + let result = mockStorage.write { db in + try? MessageSender.encryptWithSessionBlindingProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "15\(TestConstants.blindedPublicKey)", + openGroupPublicKey: TestConstants.serverPublicKey, + using: dependencies + ) + } expect(result?.toHexString()) .to(equal( @@ -138,23 +156,29 @@ class MessageSenderEncryptionSpec: QuickSpec { } it("includes a version at the start of the encrypted value") { - let result = try? MessageSender.encryptWithSessionBlindingProtocol( - "TestMessage".data(using: .utf8)!, - for: "15\(TestConstants.blindedPublicKey)", - openGroupPublicKey: TestConstants.serverPublicKey, - using: dependencies - ) + let result = mockStorage.write { db in + try? MessageSender.encryptWithSessionBlindingProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "15\(TestConstants.blindedPublicKey)", + openGroupPublicKey: TestConstants.serverPublicKey, + using: dependencies + ) + } expect(result?.toHexString().prefix(2)).to(equal("00")) } it("includes the nonce at the end of the encrypted value") { - let maybeResult = try? MessageSender.encryptWithSessionBlindingProtocol( - "TestMessage".data(using: .utf8)!, - for: "15\(TestConstants.blindedPublicKey)", - openGroupPublicKey: TestConstants.serverPublicKey, - using: dependencies - ) + let maybeResult = mockStorage.write { db in + try? MessageSender.encryptWithSessionBlindingProtocol( + db, + plaintext: "TestMessage".data(using: .utf8)!, + for: "15\(TestConstants.blindedPublicKey)", + openGroupPublicKey: TestConstants.serverPublicKey, + using: dependencies + ) + } let result: [UInt8] = (maybeResult?.bytes ?? []) let nonceBytes: [UInt8] = Array(result[max(0, (result.count - 24))..)?.body) + expect((result?.responses[0] as? HTTP.BatchSubResponse)?.body) .to(equal(testType)) - expect((result?[1].1 as? HTTP.BatchSubResponse)?.body) + expect((result?.responses[1] as? HTTP.BatchSubResponse)?.body) .to(equal(testType2)) }