|
|
@ -88,10 +88,12 @@ public final class MessageSender : NSObject {
|
|
|
|
internal static func sendToSnodeDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
|
|
|
|
internal static func sendToSnodeDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
|
|
|
|
let (promise, seal) = Promise<Void>.pending()
|
|
|
|
let (promise, seal) = Promise<Void>.pending()
|
|
|
|
let storage = SNMessagingKitConfiguration.shared.storage
|
|
|
|
let storage = SNMessagingKitConfiguration.shared.storage
|
|
|
|
|
|
|
|
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
|
|
|
|
|
|
|
let userPublicKey = storage.getUserPublicKey()
|
|
|
|
|
|
|
|
// Set the timestamp, sender and recipient
|
|
|
|
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
|
|
|
|
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
|
|
|
|
message.sentTimestamp = NSDate.millisecondTimestamp()
|
|
|
|
message.sentTimestamp = NSDate.millisecondTimestamp()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let userPublicKey = storage.getUserPublicKey()
|
|
|
|
|
|
|
|
message.sender = userPublicKey
|
|
|
|
message.sender = userPublicKey
|
|
|
|
switch destination {
|
|
|
|
switch destination {
|
|
|
|
case .contact(let publicKey): message.recipient = publicKey
|
|
|
|
case .contact(let publicKey): message.recipient = publicKey
|
|
|
@ -99,17 +101,18 @@ public final class MessageSender : NSObject {
|
|
|
|
case .openGroup(_, _): preconditionFailure()
|
|
|
|
case .openGroup(_, _): preconditionFailure()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let isSelfSend = (message.recipient == userPublicKey)
|
|
|
|
let isSelfSend = (message.recipient == userPublicKey)
|
|
|
|
// Set the failure handler (for precondition failure handling)
|
|
|
|
// Set the failure handler (need it here already for precondition failure handling)
|
|
|
|
let _ = promise.catch(on: DispatchQueue.main) { error in
|
|
|
|
func handleFailure(with error: Swift.Error, using transaction: YapDatabaseReadWriteTransaction) {
|
|
|
|
storage.withAsync({ transaction in
|
|
|
|
|
|
|
|
MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
|
|
|
|
MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
|
|
|
|
}, completion: { })
|
|
|
|
|
|
|
|
if case .contact(_) = destination, message is VisibleMessage, !isSelfSend {
|
|
|
|
if case .contact(_) = destination, message is VisibleMessage, !isSelfSend {
|
|
|
|
NotificationCenter.default.post(name: .messageSendingFailed, object: NSNumber(value: message.sentTimestamp!))
|
|
|
|
NotificationCenter.default.post(name: .messageSendingFailed, object: NSNumber(value: message.sentTimestamp!))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction.addCompletionQueue(DispatchQueue.main) {
|
|
|
|
|
|
|
|
seal.reject(error)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Validate the message
|
|
|
|
// Validate the message
|
|
|
|
guard message.isValid else { seal.reject(Error.invalidMessage); return promise }
|
|
|
|
guard message.isValid else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
|
|
|
|
// Stop here if this is a self-send
|
|
|
|
// Stop here if this is a self-send
|
|
|
|
guard !isSelfSend else {
|
|
|
|
guard !isSelfSend else {
|
|
|
|
storage.withAsync({ transaction in
|
|
|
|
storage.withAsync({ transaction in
|
|
|
@ -131,18 +134,18 @@ public final class MessageSender : NSObject {
|
|
|
|
// Convert it to protobuf
|
|
|
|
// Convert it to protobuf
|
|
|
|
let protoOrNil: SNProtoContent?
|
|
|
|
let protoOrNil: SNProtoContent?
|
|
|
|
if let message = message as? VisibleMessage {
|
|
|
|
if let message = message as? VisibleMessage {
|
|
|
|
protoOrNil = message.toProto(using: transaction as! YapDatabaseReadWriteTransaction) // Needed because of how TSAttachmentStream works
|
|
|
|
protoOrNil = message.toProto(using: transaction) // Needed because of how TSAttachmentStream works
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
protoOrNil = message.toProto()
|
|
|
|
protoOrNil = message.toProto()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard let proto = protoOrNil else { seal.reject(Error.protoConversionFailed); return promise }
|
|
|
|
guard let proto = protoOrNil else { handleFailure(with: Error.protoConversionFailed, using: transaction); return promise }
|
|
|
|
// Serialize the protobuf
|
|
|
|
// Serialize the protobuf
|
|
|
|
let plaintext: Data
|
|
|
|
let plaintext: Data
|
|
|
|
do {
|
|
|
|
do {
|
|
|
|
plaintext = try proto.serializedData()
|
|
|
|
plaintext = try proto.serializedData()
|
|
|
|
} catch {
|
|
|
|
} catch {
|
|
|
|
SNLog("Couldn't serialize proto due to error: \(error).")
|
|
|
|
SNLog("Couldn't serialize proto due to error: \(error).")
|
|
|
|
seal.reject(error)
|
|
|
|
handleFailure(with: error, using: transaction)
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Encrypt the serialized protobuf
|
|
|
|
// Encrypt the serialized protobuf
|
|
|
@ -160,7 +163,7 @@ public final class MessageSender : NSObject {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
} catch {
|
|
|
|
SNLog("Couldn't encrypt message for destination: \(destination) due to error: \(error).")
|
|
|
|
SNLog("Couldn't encrypt message for destination: \(destination) due to error: \(error).")
|
|
|
|
seal.reject(error)
|
|
|
|
handleFailure(with: error, using: transaction)
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Wrap the result
|
|
|
|
// Wrap the result
|
|
|
@ -181,7 +184,7 @@ public final class MessageSender : NSObject {
|
|
|
|
senderPublicKey: senderPublicKey, base64EncodedContent: ciphertext.base64EncodedString())
|
|
|
|
senderPublicKey: senderPublicKey, base64EncodedContent: ciphertext.base64EncodedString())
|
|
|
|
} catch {
|
|
|
|
} catch {
|
|
|
|
SNLog("Couldn't wrap message due to error: \(error).")
|
|
|
|
SNLog("Couldn't wrap message due to error: \(error).")
|
|
|
|
seal.reject(error)
|
|
|
|
handleFailure(with: error, using: transaction)
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Calculate proof of work
|
|
|
|
// Calculate proof of work
|
|
|
@ -194,7 +197,7 @@ public final class MessageSender : NSObject {
|
|
|
|
let base64EncodedData = wrappedMessage.base64EncodedString()
|
|
|
|
let base64EncodedData = wrappedMessage.base64EncodedString()
|
|
|
|
guard let (timestamp, nonce) = ProofOfWork.calculate(ttl: type(of: message).ttl, publicKey: recipient, data: base64EncodedData) else {
|
|
|
|
guard let (timestamp, nonce) = ProofOfWork.calculate(ttl: type(of: message).ttl, publicKey: recipient, data: base64EncodedData) else {
|
|
|
|
SNLog("Proof of work calculation failed.")
|
|
|
|
SNLog("Proof of work calculation failed.")
|
|
|
|
seal.reject(Error.proofOfWorkCalculationFailed)
|
|
|
|
handleFailure(with: Error.proofOfWorkCalculationFailed, using: transaction)
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Send the result
|
|
|
|
// Send the result
|
|
|
@ -228,12 +231,16 @@ public final class MessageSender : NSObject {
|
|
|
|
$0.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
$0.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
errorCount += 1
|
|
|
|
errorCount += 1
|
|
|
|
guard errorCount == promiseCount else { return } // Only error out if all promises failed
|
|
|
|
guard errorCount == promiseCount else { return } // Only error out if all promises failed
|
|
|
|
seal.reject(error)
|
|
|
|
storage.withAsync({ transaction in
|
|
|
|
|
|
|
|
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
|
|
|
|
|
|
|
|
}, completion: { })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
SNLog("Couldn't send message due to error: \(error).")
|
|
|
|
SNLog("Couldn't send message due to error: \(error).")
|
|
|
|
seal.reject(error)
|
|
|
|
storage.withAsync({ transaction in
|
|
|
|
|
|
|
|
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
|
|
|
|
|
|
|
|
}, completion: { })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Return
|
|
|
|
// Return
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
@ -243,6 +250,8 @@ public final class MessageSender : NSObject {
|
|
|
|
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
|
|
|
|
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
|
|
|
|
let (promise, seal) = Promise<Void>.pending()
|
|
|
|
let (promise, seal) = Promise<Void>.pending()
|
|
|
|
let storage = SNMessagingKitConfiguration.shared.storage
|
|
|
|
let storage = SNMessagingKitConfiguration.shared.storage
|
|
|
|
|
|
|
|
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
|
|
|
|
|
|
|
// Set the timestamp, sender and recipient
|
|
|
|
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
|
|
|
|
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
|
|
|
|
message.sentTimestamp = NSDate.millisecondTimestamp()
|
|
|
|
message.sentTimestamp = NSDate.millisecondTimestamp()
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -252,22 +261,23 @@ public final class MessageSender : NSObject {
|
|
|
|
case .closedGroup(_): preconditionFailure()
|
|
|
|
case .closedGroup(_): preconditionFailure()
|
|
|
|
case .openGroup(let channel, let server): message.recipient = "\(server).\(channel)"
|
|
|
|
case .openGroup(let channel, let server): message.recipient = "\(server).\(channel)"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Set the failure handler (for precondition failure handling)
|
|
|
|
// Set the failure handler (need it here already for precondition failure handling)
|
|
|
|
let _ = promise.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
func handleFailure(with error: Swift.Error, using transaction: YapDatabaseReadWriteTransaction) {
|
|
|
|
storage.withAsync({ transaction in
|
|
|
|
|
|
|
|
MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
|
|
|
|
MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
|
|
|
|
}, completion: { })
|
|
|
|
transaction.addCompletionQueue(DispatchQueue.main) {
|
|
|
|
|
|
|
|
seal.reject(error)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Validate the message
|
|
|
|
// Validate the message
|
|
|
|
guard let message = message as? VisibleMessage else {
|
|
|
|
guard let message = message as? VisibleMessage else {
|
|
|
|
#if DEBUG
|
|
|
|
#if DEBUG
|
|
|
|
preconditionFailure()
|
|
|
|
preconditionFailure()
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
seal.reject(Error.invalidMessage)
|
|
|
|
handleFailure(with: Error.invalidMessage, using: transaction)
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard message.isValid else { seal.reject(Error.invalidMessage); return promise }
|
|
|
|
guard message.isValid else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
|
|
|
|
// Convert the message to an open group message
|
|
|
|
// Convert the message to an open group message
|
|
|
|
let (channel, server) = { () -> (UInt64, String) in
|
|
|
|
let (channel, server) = { () -> (UInt64, String) in
|
|
|
|
switch destination {
|
|
|
|
switch destination {
|
|
|
@ -275,7 +285,7 @@ public final class MessageSender : NSObject {
|
|
|
|
default: preconditionFailure()
|
|
|
|
default: preconditionFailure()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
guard let openGroupMessage = OpenGroupMessage.from(message, for: server, using: transaction as! YapDatabaseReadWriteTransaction) else { seal.reject(Error.invalidMessage); return promise }
|
|
|
|
guard let openGroupMessage = OpenGroupMessage.from(message, for: server, using: transaction) else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
|
|
|
|
// Send the result
|
|
|
|
// Send the result
|
|
|
|
OpenGroupAPI.sendMessage(openGroupMessage, to: channel, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
|
|
|
|
OpenGroupAPI.sendMessage(openGroupMessage, to: channel, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
|
|
|
|
message.openGroupServerMessageID = openGroupMessage.serverID
|
|
|
|
message.openGroupServerMessageID = openGroupMessage.serverID
|
|
|
@ -285,7 +295,9 @@ public final class MessageSender : NSObject {
|
|
|
|
seal.fulfill(())
|
|
|
|
seal.fulfill(())
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
|
|
|
seal.reject(error)
|
|
|
|
storage.withAsync({ transaction in
|
|
|
|
|
|
|
|
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
|
|
|
|
|
|
|
|
}, completion: { })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Return
|
|
|
|
// Return
|
|
|
|
return promise
|
|
|
|
return promise
|
|
|
|