Path: blob/main/SignalServiceKit/Messages/MessageSender+Errors.swift
1 views
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public import LibSignalClient
public enum MessageSenderError: Error, UserErrorDescriptionProvider {
case blockedContactRecipient
case threadMissing
public var localizedDescription: String {
switch self {
case .blockedContactRecipient:
return OWSLocalizedString(
"ERROR_DESCRIPTION_MESSAGE_SEND_FAILED_DUE_TO_BLOCK_LIST",
comment: "Error message indicating that message send failed due to block list",
)
case .threadMissing:
return OWSLocalizedString(
"MESSAGE_STATUS_SEND_FAILED",
comment: "Label indicating that a message failed to send.",
)
}
}
}
// MARK: -
extension Error {
public var isFatalError: Bool {
switch self {
case is MessageSenderNoSessionForTransientMessageError:
return true
case is UntrustedIdentityError:
return true
case is MessageDeletedBeforeSentError:
return true
default:
// Default to NOT fatal.
return false
}
}
}
// MARK: -
public class MessageSenderNoSuchSignalRecipientError: CustomNSError, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
public var errorCode: Int { OWSErrorCode.noSuchSignalRecipient.rawValue }
// NSError bridging: the error code within the given domain.
public var errorUserInfo: [String: Any] {
[NSLocalizedDescriptionKey: localizedDescription]
}
public var localizedDescription: String {
OWSLocalizedString(
"ERROR_DESCRIPTION_UNREGISTERED_RECIPIENT",
comment: "Error message when attempting to send message",
)
}
public class func isNoSuchSignalRecipientError(_ error: Error?) -> Bool {
error is MessageSenderNoSuchSignalRecipientError
}
public init() {}
}
// MARK: -
public class MessageSenderErrorNoValidRecipients: CustomNSError, UserErrorDescriptionProvider {
static var asNSError: NSError {
MessageSenderErrorNoValidRecipients() as Error as NSError
}
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
public var errorCode: Int { OWSErrorCode.messageSendNoValidRecipients.rawValue }
// NSError bridging: the error code within the given domain.
public var errorUserInfo: [String: Any] {
[NSLocalizedDescriptionKey: self.localizedDescription]
}
public var localizedDescription: String {
OWSLocalizedString(
"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS",
comment: "Error indicating that an outgoing message had no valid recipients.",
)
}
}
// MARK: -
class MessageSenderNoSessionForTransientMessageError: CustomNSError, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
var errorCode: Int { OWSErrorCode.noSessionForTransientMessage.rawValue }
var localizedDescription: String {
// These messages are never presented to the user, since these errors only
// occur to transient messages. We only specify an error to avoid an assert.
return OWSError.genericErrorDescription()
}
}
// MARK: -
public class UntrustedIdentityError: CustomNSError, UserErrorDescriptionProvider {
public let serviceId: ServiceId
init(serviceId: ServiceId) {
self.serviceId = serviceId
}
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
public static var errorCode: Int { OWSErrorCode.untrustedIdentity.rawValue }
// NSError bridging: the error code within the given domain.
public var errorUserInfo: [String: Any] {
[NSLocalizedDescriptionKey: self.localizedDescription]
}
public var localizedDescription: String {
let format = OWSLocalizedString(
"FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_KEY",
comment: "action sheet header when re-sending message which failed because of untrusted identity keys",
)
return String.nonPluralLocalizedStringWithFormat(format, SSKEnvironment.shared.databaseStorageRef.read { tx in
return SSKEnvironment.shared.contactManagerRef.displayName(for: SignalServiceAddress(serviceId), tx: tx).resolvedValue()
})
}
// NSError bridging: the error code within the given domain.
public var errorCode: Int { Self.errorCode }
}
public class InvalidKeySignatureError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
public let serviceId: ServiceId
public let isTerminalFailure: Bool
init(serviceId: ServiceId, isTerminalFailure: Bool) {
self.serviceId = serviceId
self.isTerminalFailure = isTerminalFailure
}
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
public static var errorCode: Int { OWSErrorCode.invalidKeySignature.rawValue }
// NSError bridging: the error code within the given domain.
public var errorUserInfo: [String: Any] {
[NSLocalizedDescriptionKey: self.localizedDescription]
}
public var localizedDescription: String {
let format = OWSLocalizedString(
"FAILED_SENDING_BECAUSE_INVALID_KEY_SIGNATURE",
comment: "action sheet header when re-sending message which failed because of an invalid key signature",
)
return String.nonPluralLocalizedStringWithFormat(format, SSKEnvironment.shared.databaseStorageRef.read { tx in
return SSKEnvironment.shared.contactManagerRef.displayName(for: SignalServiceAddress(serviceId), tx: tx).resolvedValue()
})
}
// NSError bridging: the error code within the given domain.
public var errorCode: Int { Self.errorCode }
/// Key will continue to be invalidly signed, so no need to retry. It'll only
/// cause us to hit the Pre-Key request rate limit.
public var isRetryableProvider: Bool {
!isTerminalFailure
}
}
// MARK: -
public class SpamChallengeRequiredError: CustomNSError, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
public var errorUserInfo: [String: Any] {
[NSLocalizedDescriptionKey: self.localizedDescription]
}
public var localizedDescription: String {
OWSLocalizedString(
"ERROR_DESCRIPTION_SUSPECTED_SPAM",
comment: "Description for errors returned from the server due to suspected spam.",
)
}
// NSError bridging: the error code within the given domain.
public var errorCode: Int { OWSErrorCode.serverRejectedSuspectedSpam.rawValue }
}
// MARK: -
class SpamChallengeResolvedError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
var errorUserInfo: [String: Any] {
[NSLocalizedDescriptionKey: self.localizedDescription]
}
var localizedDescription: String {
OWSLocalizedString(
"ERROR_DESCRIPTION_SUSPECTED_SPAM",
comment: "Description for errors returned from the server due to suspected spam.",
)
}
// NSError bridging: the error code within the given domain.
var errorCode: Int { OWSErrorCode.serverRejectedSuspectedSpam.rawValue }
var isRetryableProvider: Bool { true }
}
// MARK: -
class OWSRetryableMessageSenderError: Error, IsRetryableProvider {
static var asNSError: NSError {
OWSRetryableMessageSenderError() as Error as NSError
}
// MARK: - IsRetryableProvider
var isRetryableProvider: Bool { true }
}
// MARK: -
class MessageDeletedBeforeSentError: CustomNSError {
static var asNSError: NSError {
MessageDeletedBeforeSentError() as Error as NSError
}
// NSError bridging: the domain of the error.
static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
var errorCode: Int { OWSErrorCode.messageDeletedBeforeSent.rawValue }
}