diff --git a/LibSession-Util b/LibSession-Util index d0b03ecf7..ea1b1dfcf 160000 --- a/LibSession-Util +++ b/LibSession-Util @@ -1 +1 @@ -Subproject commit d0b03ecf7d17e365edb077d888e926c88ef5d593 +Subproject commit ea1b1dfcf6b0a6ad6e2359df3c296935e695afc8 diff --git a/Scripts/LintLocalizableStrings.swift b/Scripts/LintLocalizableStrings.swift index b957d354a..0416b0ac8 100755 --- a/Scripts/LintLocalizableStrings.swift +++ b/Scripts/LintLocalizableStrings.swift @@ -44,6 +44,7 @@ extension ProjectState { .contains("Log.warn(", caseSensitive: false), .contains("Log.error(", caseSensitive: false), .contains("Log.critical(", caseSensitive: false), + .contains("logMessage:", caseSensitive: false), .contains("owsFailDebug(", caseSensitive: false), .contains("#imageLiteral(resourceName:", caseSensitive: false), .contains("UIImage(named:", caseSensitive: false), diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 086545ac3..6df0c96e2 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -586,8 +586,6 @@ FD2B4AFF2946C93200AB4848 /* ConfigurationSyncJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD2B4AFE2946C93200AB4848 /* ConfigurationSyncJob.swift */; }; FD2B4B042949887A00AB4848 /* QueryInterfaceRequest+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD2B4B032949887A00AB4848 /* QueryInterfaceRequest+Utilities.swift */; }; FD3003662A25D5B300B5A5FB /* ConfigMessageReceiveJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3003652A25D5B300B5A5FB /* ConfigMessageReceiveJob.swift */; }; - FD30036A2A3ADEC100B5A5FB /* CExceptionHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = FD3003692A3ADD6000B5A5FB /* CExceptionHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FD30036E2A3AE26000B5A5FB /* CExceptionHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = FD30036D2A3AE26000B5A5FB /* CExceptionHelper.mm */; }; FD368A6829DE8F9C000DBF1E /* _012_AddFTSIfNeeded.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD368A6729DE8F9B000DBF1E /* _012_AddFTSIfNeeded.swift */; }; FD368A6A29DE9E30000DBF1E /* UIContextualAction+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD368A6929DE9E30000DBF1E /* UIContextualAction+Utilities.swift */; }; FD37E9C328A1C6F3003AE748 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9C228A1C6F3003AE748 /* ThemeManager.swift */; }; @@ -1756,8 +1754,6 @@ FD2B4AFE2946C93200AB4848 /* ConfigurationSyncJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationSyncJob.swift; sourceTree = ""; }; FD2B4B032949887A00AB4848 /* QueryInterfaceRequest+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueryInterfaceRequest+Utilities.swift"; sourceTree = ""; }; FD3003652A25D5B300B5A5FB /* ConfigMessageReceiveJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigMessageReceiveJob.swift; sourceTree = ""; }; - FD3003692A3ADD6000B5A5FB /* CExceptionHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CExceptionHelper.h; sourceTree = ""; }; - FD30036D2A3AE26000B5A5FB /* CExceptionHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CExceptionHelper.mm; sourceTree = ""; }; FD368A6729DE8F9B000DBF1E /* _012_AddFTSIfNeeded.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _012_AddFTSIfNeeded.swift; sourceTree = ""; }; FD368A6929DE9E30000DBF1E /* UIContextualAction+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIContextualAction+Utilities.swift"; sourceTree = ""; }; FD37E9C228A1C6F3003AE748 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; @@ -3681,8 +3677,6 @@ FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */, FD97B23F2A3FEB050027DD57 /* ARC4RandomNumberGenerator.swift */, FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */, - FD3003692A3ADD6000B5A5FB /* CExceptionHelper.h */, - FD30036D2A3AE26000B5A5FB /* CExceptionHelper.mm */, FD23CE1E2A65269C0000B97C /* Crypto.swift */, FD12A84A2AD6458800EEBA0D /* DifferenceKit+Utilities.swift */, FD559DF42A7368CB00C7C62A /* DispatchQueue+Utilities.swift */, @@ -4639,7 +4633,6 @@ C3D9E4E3256778720040E4F3 /* UIImage+OWS.h in Headers */, B8856E1A256F1700001CE70E /* OWSMath.h in Headers */, C352A3772557864000338F3E /* NSTimer+Proxying.h in Headers */, - FD30036A2A3ADEC100B5A5FB /* CExceptionHelper.h in Headers */, C3C2A67D255388CC00C340D1 /* SessionUtilitiesKit.h in Headers */, C32C6018256E07F9003C73A2 /* NSUserDefaults+OWS.h in Headers */, B8856D8D256F1502001CE70E /* UIView+OWS.h in Headers */, @@ -5996,7 +5989,6 @@ C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Utilities.swift in Sources */, FD97B2402A3FEB050027DD57 /* ARC4RandomNumberGenerator.swift in Sources */, FD37EA1128AB34B3003AE748 /* TypedTableAlteration.swift in Sources */, - FD30036E2A3AE26000B5A5FB /* CExceptionHelper.mm in Sources */, FD1936412ACA7BD8004BCF0F /* Result+Utilities.swift in Sources */, C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */, FD12A84B2AD6458800EEBA0D /* DifferenceKit+Utilities.swift in Sources */, diff --git a/SessionMessagingKit/Jobs/Types/ConfigurationSyncJob.swift b/SessionMessagingKit/Jobs/Types/ConfigurationSyncJob.swift index c0d92dd78..688f8d600 100644 --- a/SessionMessagingKit/Jobs/Types/ConfigurationSyncJob.swift +++ b/SessionMessagingKit/Jobs/Types/ConfigurationSyncJob.swift @@ -33,7 +33,7 @@ public enum ConfigurationSyncJob: JobExecutor { .jobInfoFor(state: .running, variant: .configurationSync) .filter({ key, info in key != job.id && // Exclude this job - info.threadId == job.threadId // Exclude jobs for different ids + info.threadId == job.threadId // Exclude jobs for different config stores }) .isEmpty else { diff --git a/SessionMessagingKit/LibSession/Config Handling/LibSession+Contacts.swift b/SessionMessagingKit/LibSession/Config Handling/LibSession+Contacts.swift index 70a1441c3..6392b886e 100644 --- a/SessionMessagingKit/LibSession/Config Handling/LibSession+Contacts.swift +++ b/SessionMessagingKit/LibSession/Config Handling/LibSession+Contacts.swift @@ -290,8 +290,11 @@ internal extension LibSession { else { /// It looks like there are some situations where this object might not get created correctly (and /// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead - SNLog("Unable to upsert contact to LibSession: \(LibSession.lastError(conf))") - throw LibSessionError.getOrConstructFailedUnexpectedly + throw LibSessionError( + conf, + fallbackError: .getOrConstructFailedUnexpectedly, + logMessage: "Unable to upsert contact to LibSession" + ) } // Assign all properties to match the updated contact (if there is one) @@ -308,6 +311,7 @@ internal extension LibSession { // Store the updated contact (needs to happen before variables go out of scope) contacts_set(conf, &contact) + try LibSessionError.throwIfNeeded(conf) } // Update the profile data (if there is one - users we have sent a message request to may @@ -338,6 +342,7 @@ internal extension LibSession { // Store the updated contact (needs to happen before variables go out of scope) contacts_set(conf, &contact) + try LibSessionError.throwIfNeeded(conf) } // Assign all properties to match the updated disappearing messages configuration (if there is one) @@ -352,6 +357,7 @@ internal extension LibSession { // Store the updated contact (can't be sure if we made any changes above) contact.priority = (info.priority ?? contact.priority) contacts_set(conf, &contact) + try LibSessionError.throwIfNeeded(conf) } } } @@ -390,7 +396,10 @@ internal extension LibSession { var cContactId: [CChar] = contactData.id.cString(using: .utf8), contacts_get(conf, &contact, &cContactId), String(libSessionVal: contact.name, nullIfEmpty: true) != nil - else { return contactData.id } + else { + LibSessionError.clear(conf) + return contactData.id + } return nil } diff --git a/SessionMessagingKit/LibSession/Config Handling/LibSession+ConvoInfoVolatile.swift b/SessionMessagingKit/LibSession/Config Handling/LibSession+ConvoInfoVolatile.swift index 6573afaf5..abd693b73 100644 --- a/SessionMessagingKit/LibSession/Config Handling/LibSession+ConvoInfoVolatile.swift +++ b/SessionMessagingKit/LibSession/Config Handling/LibSession+ConvoInfoVolatile.swift @@ -144,8 +144,11 @@ internal extension LibSession { guard convo_info_volatile_get_or_construct_1to1(conf, &oneToOne, &cThreadId) else { /// It looks like there are some situations where this object might not get created correctly (and /// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead - SNLog("Unable to upsert contact volatile info to LibSession: \(LibSession.lastError(conf))") - throw LibSessionError.getOrConstructFailedUnexpectedly + throw LibSessionError( + conf, + fallbackError: .getOrConstructFailedUnexpectedly, + logMessage: "Unable to upsert contact volatile info to LibSession" + ) } threadInfo.changes.forEach { change in @@ -165,8 +168,11 @@ internal extension LibSession { guard convo_info_volatile_get_or_construct_legacy_group(conf, &legacyGroup, &cThreadId) else { /// It looks like there are some situations where this object might not get created correctly (and /// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead - SNLog("Unable to upsert legacy group volatile info to LibSession: \(LibSession.lastError(conf))") - throw LibSessionError.getOrConstructFailedUnexpectedly + throw LibSessionError( + conf, + fallbackError: .getOrConstructFailedUnexpectedly, + logMessage: "Unable to upsert legacy group volatile info to LibSession" + ) } threadInfo.changes.forEach { change in @@ -195,8 +201,11 @@ internal extension LibSession { guard convo_info_volatile_get_or_construct_community(conf, &community, &cBaseUrl, &cRoomToken, &cPubkey) else { /// It looks like there are some situations where this object might not get created correctly (and /// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead - SNLog("Unable to upsert community volatile info to LibSession: \(LibSession.lastError(conf))") - throw LibSessionError.getOrConstructFailedUnexpectedly + throw LibSessionError( + conf, + fallbackError: .getOrConstructFailedUnexpectedly, + logMessage: "Unable to upsert community volatile info to LibSession" + ) } threadInfo.changes.forEach { change in @@ -339,7 +348,10 @@ public extension LibSession { guard var cThreadId: [CChar] = threadId.cString(using: .utf8), convo_info_volatile_get_1to1(conf, &oneToOne, &cThreadId) - else { return false } + else { + LibSessionError.clear(conf) + return false + } return (oneToOne.last_read >= timestampMs) @@ -349,7 +361,10 @@ public extension LibSession { guard var cThreadId: [CChar] = threadId.cString(using: .utf8), convo_info_volatile_get_legacy_group(conf, &legacyGroup, &cThreadId) - else { return false } + else { + LibSessionError.clear(conf) + return false + } return (legacyGroup.last_read >= timestampMs) @@ -362,7 +377,10 @@ public extension LibSession { var cBaseUrl: [CChar] = openGroup.server.cString(using: .utf8), var cRoomToken: [CChar] = openGroup.roomToken.cString(using: .utf8), convo_info_volatile_get_community(conf, &convoCommunity, &cBaseUrl, &cRoomToken) - else { return false } + else { + LibSessionError.clear(conf) + return false + } return (convoCommunity.last_read >= timestampMs) diff --git a/SessionMessagingKit/LibSession/Config Handling/LibSession+Shared.swift b/SessionMessagingKit/LibSession/Config Handling/LibSession+Shared.swift index 6ef3c0493..498496242 100644 --- a/SessionMessagingKit/LibSession/Config Handling/LibSession+Shared.swift +++ b/SessionMessagingKit/LibSession/Config Handling/LibSession+Shared.swift @@ -419,7 +419,10 @@ public extension LibSession { var contact: contacts_contact = contacts_contact() - guard contacts_get(conf, &contact, &cThreadId) else { return false } + guard contacts_get(conf, &contact, &cThreadId) else { + LibSessionError.clear(conf) + return false + } /// If the user opens a conversation with an existing contact but doesn't send them a message /// then the one-to-one conversation should remain hidden so we want to delete the `SessionThread` @@ -440,10 +443,14 @@ public extension LibSession { var community: ugroups_community_info = ugroups_community_info() /// Not handling the `hidden` behaviour for communities so just indicate the existence - return user_groups_get_community(conf, &community, &cBaseUrl, &cRoom) + let result: Bool = user_groups_get_community(conf, &community, &cBaseUrl, &cRoom) + LibSessionError.clear(conf) + + return result case .legacyGroup: let groupInfo: UnsafeMutablePointer? = user_groups_get_legacy_group(conf, &cThreadId) + LibSessionError.clear(conf) /// Not handling the `hidden` behaviour for legacy groups so just indicate the existence if groupInfo != nil { diff --git a/SessionMessagingKit/LibSession/Config Handling/LibSession+UserGroups.swift b/SessionMessagingKit/LibSession/Config Handling/LibSession+UserGroups.swift index c65d3be8e..84e61ee89 100644 --- a/SessionMessagingKit/LibSession/Config Handling/LibSession+UserGroups.swift +++ b/SessionMessagingKit/LibSession/Config Handling/LibSession+UserGroups.swift @@ -409,8 +409,11 @@ internal extension LibSession { guard let userGroup: UnsafeMutablePointer = user_groups_get_or_construct_legacy_group(conf, &cGroupId) else { /// It looks like there are some situations where this object might not get created correctly (and /// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead - SNLog("Unable to upsert legacy group conversation to LibSession: \(LibSession.lastError(conf))") - throw LibSessionError.getOrConstructFailedUnexpectedly + throw LibSessionError( + conf, + fallbackError: .getOrConstructFailedUnexpectedly, + logMessage: "Unable to upsert legacy group conversation to LibSession" + ) } // Assign all properties to match the updated group (if there is one) @@ -419,6 +422,7 @@ internal extension LibSession { // Store the updated group (needs to happen before variables go out of scope) user_groups_set_legacy_group(conf, userGroup) + try LibSessionError.throwIfNeeded(conf) { ugroups_legacy_group_free(userGroup) } } if let lastKeyPair: ClosedGroupKeyPair = legacyGroup.lastKeyPair { @@ -428,6 +432,7 @@ internal extension LibSession { // Store the updated group (needs to happen before variables go out of scope) user_groups_set_legacy_group(conf, userGroup) + try LibSessionError.throwIfNeeded(conf) { ugroups_legacy_group_free(userGroup) } } // Assign all properties to match the updated disappearing messages config (if there is one) @@ -437,6 +442,7 @@ internal extension LibSession { ) user_groups_set_legacy_group(conf, userGroup) + try LibSessionError.throwIfNeeded(conf) { ugroups_legacy_group_free(userGroup) } } // Add/Remove the group members and admins @@ -500,6 +506,7 @@ internal extension LibSession { // Note: Need to free the legacy group pointer user_groups_set_free_legacy_group(conf, userGroup) + try LibSessionError.throwIfNeeded(conf) } } @@ -526,8 +533,11 @@ internal extension LibSession { guard user_groups_get_or_construct_community(conf, &userCommunity, &cBaseUrl, &cRoom, &cPubkey) else { /// It looks like there are some situations where this object might not get created correctly (and /// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead - SNLog("Unable to upsert community conversation to LibSession: \(LibSession.lastError(conf))") - throw LibSessionError.getOrConstructFailedUnexpectedly + throw LibSessionError( + conf, + fallbackError: .getOrConstructFailedUnexpectedly, + logMessage: "Unable to upsert community conversation to LibSession" + ) } userCommunity.priority = (community.priority ?? userCommunity.priority) @@ -624,6 +634,7 @@ public extension LibSession { // coming in from the legacy group swarm) guard userGroup == nil else { ugroups_legacy_group_free(userGroup) + try LibSessionError.throwIfNeeded(conf) return } diff --git a/SessionMessagingKit/LibSession/Config Handling/LibSession+UserProfile.swift b/SessionMessagingKit/LibSession/Config Handling/LibSession+UserProfile.swift index 469b6340e..fcdd8084f 100644 --- a/SessionMessagingKit/LibSession/Config Handling/LibSession+UserProfile.swift +++ b/SessionMessagingKit/LibSession/Config Handling/LibSession+UserProfile.swift @@ -180,12 +180,14 @@ internal extension LibSession { // Update the name var updatedName: [CChar] = try profile.name.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }() user_profile_set_name(conf, &updatedName) + try LibSessionError.throwIfNeeded(conf) // Either assign the updated profile pic, or sent a blank profile pic (to remove the current one) var profilePic: user_profile_pic = user_profile_pic() profilePic.url = profile.profilePictureUrl.toLibSession() profilePic.key = profile.profileEncryptionKey.toLibSession() user_profile_set_pic(conf, profilePic) + try LibSessionError.throwIfNeeded(conf) } static func updateNoteToSelf( diff --git a/SessionMessagingKit/LibSession/LibSession+SessionMessagingKit.swift b/SessionMessagingKit/LibSession/LibSession+SessionMessagingKit.swift index 153db836d..2c55fa8f8 100644 --- a/SessionMessagingKit/LibSession/LibSession+SessionMessagingKit.swift +++ b/SessionMessagingKit/LibSession/LibSession+SessionMessagingKit.swift @@ -82,9 +82,6 @@ public extension LibSession { static var libSessionVersion: String { String(cString: LIBSESSION_UTIL_VERSION_STR) } - internal static func lastError(_ conf: UnsafeMutablePointer?) -> String { - return (conf?.pointee.last_error.map { String(cString: $0) } ?? "Unknown") // stringlint:disable - } // MARK: - Loading @@ -209,9 +206,10 @@ public extension LibSession { var dumpResult: UnsafeMutablePointer? = nil var dumpResultLen: Int = 0 - try CExceptionHelper.performSafely { - config_dump(conf, &dumpResult, &dumpResultLen) - } + config_dump(conf, &dumpResult, &dumpResultLen) + + // If we got an error then throw it + try LibSessionError.throwIfNeeded(conf) guard let dumpResult: UnsafeMutablePointer = dumpResult else { return nil } @@ -247,14 +245,17 @@ public extension LibSession { ConfigDump.Variant.userVariants.forEach { existingDumpVariants.insert($0) } } - // Ensure we always check the required user config types for changes even if there is no dump - // data yet (to deal with first launch cases) + /// Ensure we always check the required user config types for changes even if there is no dump data yet (to deal with first launch cases) + /// + /// **Note:** We `mutate` when retrieving the pending changes here because we want to ensure no other threads can modify the + /// config while we are reading (which could result in crashes) return try existingDumpVariants .reduce(into: PendingChanges()) { result, variant in try LibSession .config(for: variant, publicKey: publicKey) - .wrappedValue - .map { conf in + .mutate { conf in + guard conf != nil else { return } + // Check if the config needs to be pushed guard config_needs_push(conf) else { // If not then try retrieve any obsolete hashes to be removed @@ -275,31 +276,22 @@ public extension LibSession { return } - var cPushData: UnsafeMutablePointer! - let configCountInfo: String = { - var result: String = "Invalid" // stringlint:disable - - try? CExceptionHelper.performSafely { + guard let cPushData: UnsafeMutablePointer = config_push(conf) else { + let configCountInfo: String = { switch variant { - case .userProfile: result = "1 profile" - case .contacts: result = "\(contacts_size(conf)) contacts" - case .userGroups: result = "\(user_groups_size(conf)) group conversations" - case .convoInfoVolatile: result = "\(convo_info_volatile_size(conf)) volatile conversations" - case .invalid: break + case .userProfile: return "1 profile" // stringlint:disable + case .contacts: return "\(contacts_size(conf)) contacts" // stringlint:disable + case .userGroups: return "\(user_groups_size(conf)) group conversations" // stringlint:disable + case .convoInfoVolatile: return "\(convo_info_volatile_size(conf)) volatile conversations" // stringlint:disable + case .invalid: return "Invalid" // stringlint:disable } - } + }() - return result - }() - - do { - try CExceptionHelper.performSafely { - cPushData = config_push(conf) - } - } - catch { - SNLog("[LibSession] Failed to generate push data for \(variant) config data, size: \(configCountInfo), error: \(error)") - throw error + throw LibSessionError( + conf, + fallbackError: .unableToGeneratePushData, + logMessage: "[LibSession] Failed to generate push data for \(variant) config data, size: \(configCountInfo), error" + ) } let pushData: Data = Data( @@ -431,16 +423,16 @@ public extension LibSession { else { return SNLog("[LibSession] Failed to correctly allocate merge data") } var mergeSize: [size_t] = value.map { size_t($0.data.count) } - var mergedHashesPtr: UnsafeMutablePointer? - try CExceptionHelper.performSafely { - mergedHashesPtr = config_merge( - conf, - &mergeHashes, - &mergeData, - &mergeSize, - value.count - ) - } + let mergedHashesPtr: UnsafeMutablePointer? = config_merge( + conf, + &mergeHashes, + &mergeData, + &mergeSize, + value.count + ) + + // If we got an error then throw it + try LibSessionError.throwIfNeeded(conf) // Get the list of hashes from the config (to determine which were successful) let mergedHashes: [String] = mergedHashesPtr diff --git a/SessionMessagingKitTests/LibSession/LibSessionUtilSpec.swift b/SessionMessagingKitTests/LibSession/LibSessionUtilSpec.swift index 6e192174e..448726223 100644 --- a/SessionMessagingKitTests/LibSession/LibSessionUtilSpec.swift +++ b/SessionMessagingKitTests/LibSession/LibSessionUtilSpec.swift @@ -154,9 +154,10 @@ fileprivate extension LibSessionUtilSpec { expect(config_needs_dump(conf)).to(beTrue()) expect { - try CExceptionHelper.performSafely { config_push(conf).deallocate() } + config_push(conf)?.deallocate() + try LibSessionError.throwIfNeeded(conf) } - .to(throwError(NSError(domain: "cpp_exception", code: -2, userInfo: ["NSLocalizedDescription": "Config data is too large"]))) + .to(throwError(LibSessionError.libSessionError("Config data is too large."))) } } @@ -196,7 +197,10 @@ fileprivate extension LibSessionUtilSpec { ) contacts_set(conf, &contact) - do { try CExceptionHelper.performSafely { config_push(conf).deallocate() } } + do { + config_push(conf)?.deallocate() + try LibSessionError.throwIfNeeded(conf) + } catch { break } // We successfully inserted a contact and didn't hit the limit so increment the counter @@ -220,7 +224,10 @@ fileprivate extension LibSessionUtilSpec { ) contacts_set(conf, &contact) - do { try CExceptionHelper.performSafely { config_push(conf).deallocate() } } + do { + config_push(conf)?.deallocate() + try LibSessionError.throwIfNeeded(conf) + } catch { break } // We successfully inserted a contact and didn't hit the limit so increment the counter @@ -244,7 +251,10 @@ fileprivate extension LibSessionUtilSpec { ) contacts_set(conf, &contact) - do { try CExceptionHelper.performSafely { config_push(conf).deallocate() } } + do { + config_push(conf)?.deallocate() + try LibSessionError.throwIfNeeded(conf) + } catch { break } // We successfully inserted a contact and didn't hit the limit so increment the counter @@ -252,7 +262,7 @@ fileprivate extension LibSessionUtilSpec { } // Check that the record count matches the maximum when we last checked - expect(numRecords).to(equal(270)) + expect(numRecords).to(equal(274)) } // MARK: ---- has not changed the max filled records @@ -268,7 +278,10 @@ fileprivate extension LibSessionUtilSpec { ) contacts_set(conf, &contact) - do { try CExceptionHelper.performSafely { config_push(conf).deallocate() } } + do { + config_push(conf)?.deallocate() + try LibSessionError.throwIfNeeded(conf) + } catch { break } // We successfully inserted a contact and didn't hit the limit so increment the counter @@ -277,7 +290,7 @@ fileprivate extension LibSessionUtilSpec { // Check that the record count matches the maximum when we last checked (seems to swap between // these two on different test runs for some reason) - expect(numRecords).to(satisfyAnyOf(equal(219), equal(220))) + expect(numRecords).to(equal(223)) } } diff --git a/SessionMessagingKitTests/LibSession/Utilities/LibSessionTypeConversionUtilitiesSpec.swift b/SessionMessagingKitTests/LibSession/Utilities/LibSessionTypeConversionUtilitiesSpec.swift index c1624604e..4a6302786 100644 --- a/SessionMessagingKitTests/LibSession/Utilities/LibSessionTypeConversionUtilitiesSpec.swift +++ b/SessionMessagingKitTests/LibSession/Utilities/LibSessionTypeConversionUtilitiesSpec.swift @@ -143,12 +143,13 @@ class LibSessionTypeConversionUtilitiesSpec: QuickSpec { // MARK: ---- truncates when too long it("truncates when too long") { - let result: (CChar, CChar, CChar, CChar, CChar) = "TestTest".toLibSession() + let result: (CChar, CChar, CChar, CChar, CChar, CChar) = "TestTest".toLibSession() expect(result.0).to(equal(84)) expect(result.1).to(equal(101)) expect(result.2).to(equal(115)) expect(result.3).to(equal(116)) expect(result.4).to(equal(84)) + expect(result.5).to(equal(0)) // Last character will always be a null termination } // MARK: ---- when optional diff --git a/SessionUtilitiesKit/LibSession/LibSessionError.swift b/SessionUtilitiesKit/LibSession/LibSessionError.swift index 44fb7e771..bf263c0c4 100644 --- a/SessionUtilitiesKit/LibSession/LibSessionError.swift +++ b/SessionUtilitiesKit/LibSession/LibSessionError.swift @@ -5,13 +5,14 @@ import Foundation import SessionUtil -public enum LibSessionError: LocalizedError { +public enum LibSessionError: Error, CustomStringConvertible { case unableToCreateConfigObject case nilConfigObject case userDoesNotExist case getOrConstructFailedUnexpectedly case processingLoopLimitReached case invalidCConversion + case unableToGeneratePushData case libSessionError(String) case unknown @@ -26,17 +27,52 @@ public enum LibSessionError: LocalizedError { } } - public var errorDescription: String? { + public init?(_ conf: UnsafeMutablePointer?) { + guard let lastErrorPtr: UnsafePointer = conf?.pointee.last_error else { return nil } + + let errorString = String(cString: lastErrorPtr) + conf?.pointee.last_error = nil // Clear the last error so subsequent calls don't get confused + self = LibSessionError.libSessionError(errorString) + } + + public init( + _ conf: UnsafeMutablePointer?, + fallbackError: LibSessionError, + logMessage: String? = nil + ) { + self = (LibSessionError(conf) ?? fallbackError) + + if let logMessage: String = logMessage { + Log.error("\(logMessage): \(self)") + } + } + + public static func throwIfNeeded( + _ conf: UnsafeMutablePointer?, + beforeThrow: (() -> ())? = nil + ) throws { + guard let error: LibSessionError = LibSessionError(conf) else { return } + + beforeThrow?() + throw error + } + + public static func clear(_ conf: UnsafeMutablePointer?) { + conf?.pointee.last_error = nil + } + + public var description: String { switch self { - case .unableToCreateConfigObject: return "Unable to create config object." - case .nilConfigObject: return "Null config object." - case .userDoesNotExist: return "User does not exist." - case .getOrConstructFailedUnexpectedly: return "'getOrConstruct' failed unexpectedly." - case .processingLoopLimitReached: return "Processing loop limit reached." - case .invalidCConversion: return "Invalid conversation to C type." + case .unableToCreateConfigObject: return "Unable to create config object (LibSessionError.unableToCreateConfigObject)." + case .nilConfigObject: return "Null config object (LibSessionError.nilConfigObject)." + case .userDoesNotExist: return "User does not exist (LibSessionError.userDoesNotExist)." + case .getOrConstructFailedUnexpectedly: return "'getOrConstruct' failed unexpectedly (LibSessionError.getOrConstructFailedUnexpectedly)." + case .processingLoopLimitReached: return "Processing loop limit reached (LibSessionError.processingLoopLimitReached)." + case .invalidCConversion: return "Invalid conversation to C type (LibSessionError.invalidCConversion)." + case .unableToGeneratePushData: return "Unable to generate push data (LibSessionError.unableToGeneratePushData)." case .libSessionError(let error): return "\(error)\(error.hasSuffix(".") ? "" : ".")" - case .unknown: return "An unknown error occurred." + case .unknown: return "An unknown error occurred (LibSessionError.unknown)." } } } diff --git a/SessionUtilitiesKit/LibSession/Utilities/TypeConversion+Utilities.swift b/SessionUtilitiesKit/LibSession/Utilities/TypeConversion+Utilities.swift index f3a5baab8..20fa32f4e 100644 --- a/SessionUtilitiesKit/LibSession/Utilities/TypeConversion+Utilities.swift +++ b/SessionUtilitiesKit/LibSession/Utilities/TypeConversion+Utilities.swift @@ -59,10 +59,14 @@ public extension String { func toLibSession() -> T { let targetSize: Int = MemoryLayout.stride + + // Limit the string to be the destination size - 1 (for the null terminated character), this will + // mean instead of crashing by trying to set a value that is too large, we truncate the value) + let sizeLimitedString: String = String(self.substring(to: min(count, targetSize - 1))) var dataMatchingDestinationSize: [CChar] = [CChar](repeating: 0, count: targetSize) dataMatchingDestinationSize.replaceSubrange( - 0.. #import #import -#import - diff --git a/SessionUtilitiesKit/Utilities/CExceptionHelper.h b/SessionUtilitiesKit/Utilities/CExceptionHelper.h deleted file mode 100644 index 6d5cd8adc..000000000 --- a/SessionUtilitiesKit/Utilities/CExceptionHelper.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. - -#ifndef __CExceptionHelper_h__ -#define __CExceptionHelper_h__ - -#import - -#define noEscape __attribute__((noescape)) - -@interface CExceptionHelper: NSObject - -+ (BOOL)performSafely:(noEscape void(^)(void))tryBlock error:(__autoreleasing NSError **)error; - -@end - -#endif diff --git a/SessionUtilitiesKit/Utilities/CExceptionHelper.mm b/SessionUtilitiesKit/Utilities/CExceptionHelper.mm deleted file mode 100644 index fac2e007e..000000000 --- a/SessionUtilitiesKit/Utilities/CExceptionHelper.mm +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. -// -// This logic is not foolproof and may result in memory-leaks, when possible we should look to remove this -// and use the native C++ <-> Swift interoperability coming with Swift 5.9 -// -// This solution was sourced from the following link, for more information please refer to this thread: -// https://forums.swift.org/t/pitch-a-swift-representation-for-thrown-and-caught-exceptions/54583 - -#import "CExceptionHelper.h" -#include - -@implementation CExceptionHelper - -+ (BOOL)performSafely:(noEscape void(^)(void))tryBlock error:(__autoreleasing NSError **)error { - try { - tryBlock(); - return YES; - } - catch(NSException* e) { - *error = [[NSError alloc] initWithDomain:e.name code:-1 userInfo:e.userInfo]; - return NO; - } - catch (std::exception& e) { - NSString* what = [NSString stringWithUTF8String: e.what()]; - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : what}; - *error = [[NSError alloc] initWithDomain:@"cpp_exception" code:-2 userInfo:userInfo]; - return NO; - } - catch(...) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey:@"Other C++ exception"}; - *error = [[NSError alloc] initWithDomain:@"cpp_exception" code:-3 userInfo:userInfo]; - return NO; - } -} - -@end