import SessionUtilitiesKit internal enum MessageReceiver { internal enum Error : LocalizedError { case invalidMessage case unknownMessage case unknownEnvelopeType case noUserPublicKey case noData // Shared sender keys case invalidGroupPublicKey case noGroupPrivateKey case sharedSecretGenerationFailed case selfSend internal var errorDescription: String? { switch self { case .invalidMessage: return "Invalid message." case .unknownMessage: return "Unknown message type." case .unknownEnvelopeType: return "Unknown envelope type." case .noUserPublicKey: return "Couldn't find user key pair." case .noData: return "Received an empty envelope." // Shared sender keys case .invalidGroupPublicKey: return "Invalid group public key." case .noGroupPrivateKey: return "Missing group private key." case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret." case .selfSend: return "Message addressed at self." } } } internal static func parse(_ data: Data, using transaction: Any) throws -> Message { // Parse the envelope let envelope = try MessageWrapper.unwrap(data: data) // Decrypt the contents let plaintext: Data switch envelope.type { case .unidentifiedSender: (plaintext, _) = try decryptWithSignalProtocol(envelope: envelope, using: transaction) case .closedGroupCiphertext: (plaintext, _) = try decryptWithSharedSenderKeys(envelope: envelope, using: transaction) default: throw Error.unknownEnvelopeType } // Parse the proto let proto: SNProtoContent do { proto = try SNProtoContent.parseData((plaintext as NSData).removePadding()) } catch { SNLog("Couldn't parse proto due to error: \(error).") throw error } // Parse the message let message: Message? = { if let readReceipt = ReadReceipt.fromProto(proto) { return readReceipt } if let sessionRequest = SessionRequest.fromProto(proto) { return sessionRequest } if let typingIndicator = TypingIndicator.fromProto(proto) { return typingIndicator } if let closedGroupUpdate = ClosedGroupUpdate.fromProto(proto) { return closedGroupUpdate } if let expirationTimerUpdate = ExpirationTimerUpdate.fromProto(proto) { return expirationTimerUpdate } if let visibleMessage = VisibleMessage.fromProto(proto) { return visibleMessage } return nil }() if let message = message { guard message.isValid else { throw Error.invalidMessage } return message } else { throw Error.unknownMessage } } }