Merge branch 'dev' into p2p

pull/20/head
Mikunj 6 years ago
commit 482721a2c2

@ -1 +1 @@
Subproject commit 3343906944c5db1e40599f4d36c35fd6dc75da20 Subproject commit c8f9b28f577a03f9f03a0b2e38e125811785ee29

@ -321,7 +321,11 @@ static NSTimeInterval launchStartedAt;
BOOL isStarted = [self.lokiP2PServer startOnPort:port.unsignedIntegerValue]; BOOL isStarted = [self.lokiP2PServer startOnPort:port.unsignedIntegerValue];
if (isStarted) { if (isStarted) {
[LokiP2PManager setOurP2PAddressWithUrl:self.lokiP2PServer.serverURL]; [LokiP2PManager setOurP2PAddressWithUrl:self.lokiP2PServer.serverURL];
OWSLogInfo(@"[Loki] Started server at %@.", self.lokiP2PServer.serverURL); NSString *serverURL = self.lokiP2PServer.serverURL.absoluteString;
if ([serverURL hasSuffix:@"/"]) {
serverURL = [serverURL substringToIndex:serverURL.length - 1];
}
OWSLogInfo(@"[Loki] Started server at %@.", serverURL);
break; break;
} }
} }

@ -12,6 +12,12 @@
enum Kind : String { case incoming, outgoing } enum Kind : String { case incoming, outgoing }
// MARK: Components // MARK: Components
private lazy var topSpacer: UIView = {
let result = UIView()
result.autoSetDimension(.height, toSize: 12)
return result
}()
private lazy var label: UILabel = { private lazy var label: UILabel = {
let result = UILabel() let result = UILabel()
result.textColor = Theme.secondaryColor result.textColor = Theme.secondaryColor
@ -47,6 +53,7 @@
let mainStackView = UIStackView() let mainStackView = UIStackView()
mainStackView.axis = .vertical mainStackView.axis = .vertical
mainStackView.distribution = .fill mainStackView.distribution = .fill
mainStackView.addArrangedSubview(topSpacer)
mainStackView.addArrangedSubview(label) mainStackView.addArrangedSubview(label)
switch kind { switch kind {
case .incoming: case .incoming:
@ -85,23 +92,33 @@
buttonStackView.isHidden = message.friendRequestStatus != .pending buttonStackView.isHidden = message.friendRequestStatus != .pending
let format: String = { let format: String = {
switch (message.friendRequestStatus) { switch (message.friendRequestStatus) {
case .none, .sendingOrFailed: preconditionFailure()
case .pending: return NSLocalizedString("%@ sent you a friend request", comment: "")
case .accepted: return NSLocalizedString("You've accepted %@'s friend request", comment: "") case .accepted: return NSLocalizedString("You've accepted %@'s friend request", comment: "")
case .declined: return NSLocalizedString("You've declined %@'s friend request", comment: "") case .declined: return NSLocalizedString("You've declined %@'s friend request", comment: "")
case .expired: return NSLocalizedString("%@'s friend request has expired", comment: "") case .expired: return NSLocalizedString("%@'s friend request has expired", comment: "")
default: return NSLocalizedString("%@ sent you a friend request", comment: "") default: preconditionFailure()
} }
}() }()
label.text = String(format: format, message.authorId) label.text = String(format: format, message.authorId)
case .outgoing: case .outgoing:
guard let message = message as? TSOutgoingMessage else { preconditionFailure() } guard let message = message as? TSOutgoingMessage else { preconditionFailure() }
let format: String = { let format: String? = {
switch (message.friendRequestStatus) { switch (message.friendRequestStatus) {
case .none: preconditionFailure()
case .sendingOrFailed: return nil
case .pending: return NSLocalizedString("You've sent %@ a friend request", comment: "")
case .accepted: return NSLocalizedString("%@ accepted your friend request", comment: "") case .accepted: return NSLocalizedString("%@ accepted your friend request", comment: "")
case .declined: preconditionFailure()
case .expired: return NSLocalizedString("Your friend request to %@ has expired", comment: "") case .expired: return NSLocalizedString("Your friend request to %@ has expired", comment: "")
default: return NSLocalizedString("You've sent %@ a friend request", comment: "") default: preconditionFailure()
} }
}() }()
label.text = String(format: format, message.thread.contactIdentifier()!) if let format = format {
label.text = String(format: format, message.thread.contactIdentifier()!)
}
label.isHidden = (format == nil)
topSpacer.isHidden = (label.isHidden)
} }
} }
@ -121,9 +138,11 @@
// MARK: Measuring // MARK: Measuring
@objc static func calculateHeight(message: TSMessage, conversationStyle: ConversationStyle) -> CGFloat { @objc static func calculateHeight(message: TSMessage, conversationStyle: ConversationStyle) -> CGFloat {
let width = conversationStyle.contentWidth let width = conversationStyle.contentWidth
let topSpacing: CGFloat = 12
let dummyFriendRequestView = FriendRequestView(message: message) let dummyFriendRequestView = FriendRequestView(message: message)
let labelHeight = dummyFriendRequestView.label.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height let hasTopSpacer = !dummyFriendRequestView.topSpacer.isHidden
let topSpacing: CGFloat = hasTopSpacer ? 12 : 0
let hasLabel = !dummyFriendRequestView.label.isHidden
let labelHeight = hasLabel ? dummyFriendRequestView.label.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height : 0
let hasButtonStackView = dummyFriendRequestView.buttonStackView.superview != nil && !dummyFriendRequestView.buttonStackView.isHidden let hasButtonStackView = dummyFriendRequestView.buttonStackView.superview != nil && !dummyFriendRequestView.buttonStackView.isHidden
let buttonHeight = hasButtonStackView ? dummyFriendRequestView.buttonHeight : 0 let buttonHeight = hasButtonStackView ? dummyFriendRequestView.buttonHeight : 0
let totalHeight = topSpacing + labelHeight + buttonHeight let totalHeight = topSpacing + labelHeight + buttonHeight

@ -214,7 +214,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.viewConstraints addObjectsFromArray:@[ [self.viewConstraints addObjectsFromArray:@[
[self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:self.conversationStyle.gutterLeading], [self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:self.conversationStyle.gutterLeading],
[self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:self.conversationStyle.gutterTrailing], [self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:self.conversationStyle.gutterTrailing],
[self.friendRequestView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.messageBubbleView withOffset:12.f], [self.friendRequestView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.messageBubbleView],
[self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeBottom] [self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeBottom]
]]; ]];
} }

@ -4315,7 +4315,7 @@ typedef enum : NSUInteger {
- (void)acceptFriendRequest:(TSIncomingMessage *)friendRequest - (void)acceptFriendRequest:(TSIncomingMessage *)friendRequest
{ {
// Update the thread's friend request status // Update the thread's friend request status
[self.thread saveFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:nil]; [self.thread saveFriendRequestStatus:LKThreadFriendRequestStatusFriends withTransaction:nil];
// Send a friend request accepted message // Send a friend request accepted message
[ThreadUtil enqueueAcceptFriendRequestMessageInThread:self.thread]; [ThreadUtil enqueueAcceptFriendRequestMessageInThread:self.thread];
} }
@ -4323,7 +4323,7 @@ typedef enum : NSUInteger {
- (void)declineFriendRequest:(TSIncomingMessage *)friendRequest - (void)declineFriendRequest:(TSIncomingMessage *)friendRequest
{ {
// Reset the thread's friend request status // Reset the thread's friend request status
[self.thread saveFriendRequestStatus:TSThreadFriendRequestStatusNone withTransaction:nil]; [self.thread saveFriendRequestStatus:LKThreadFriendRequestStatusNone withTransaction:nil];
// Delete prekeys // Delete prekeys
NSString *contactID = friendRequest.authorId; NSString *contactID = friendRequest.authorId;
OWSPrimaryStorage *primaryStorage = SSKEnvironment.shared.primaryStorage; OWSPrimaryStorage *primaryStorage = SSKEnvironment.shared.primaryStorage;

@ -407,7 +407,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
} }
} else if (self.interaction.interactionType == OWSInteractionType_OutgoingMessage } else if (self.interaction.interactionType == OWSInteractionType_OutgoingMessage
&& previousLayoutItem.interaction.interactionType == OWSInteractionType_OutgoingMessage && previousLayoutItem.interaction.interactionType == OWSInteractionType_OutgoingMessage
&& !([previousLayoutItem.interaction isKindOfClass:TSOutgoingMessage.class] && ((TSOutgoingMessage *)previousLayoutItem.interaction).isFriendRequest)) { && !((TSOutgoingMessage *)previousLayoutItem.interaction).hasFriendRequestStatusMessage) {
return 2.f; return 2.f;
} }

@ -29,20 +29,19 @@ extern ConversationColorName const ConversationColorNameSteel;
extern ConversationColorName const kConversationColorName_Default; extern ConversationColorName const kConversationColorName_Default;
// Loki: Friend request status typedef NS_ENUM(NSInteger, LKThreadFriendRequestStatus) {
typedef NS_ENUM(NSInteger, TSThreadFriendRequestStatus) { /// New conversation; no messages sent or received.
/// New conversation, no messages sent or received. LKThreadFriendRequestStatusNone,
TSThreadFriendRequestStatusNone,
/// This state is used to lock the input early while sending. /// This state is used to lock the input early while sending.
TSThreadFriendRequestStatusRequestSending, LKThreadFriendRequestStatusRequestSending,
/// Friend request sent, awaiting response. /// Friend request sent; awaiting response.
TSThreadFriendRequestStatusRequestSent, LKThreadFriendRequestStatusRequestSent,
/// Friend request received, awaiting user input. /// Friend request received; awaiting user input.
TSThreadFriendRequestStatusRequestReceived, LKThreadFriendRequestStatusRequestReceived,
/// We are friends with the other user in this thread. /// We are friends with the other user in this thread.
TSThreadFriendRequestStatusFriends, LKThreadFriendRequestStatusFriends,
/// Friend request sent but it timed out (i.e. the other user didn't accept within the allocated time). /// A friend request was sent, but it timed out (i.e. the other user didn't accept within the allocated time).
TSThreadFriendRequestStatusRequestExpired LKThreadFriendRequestStatusRequestExpired
}; };
/** /**
@ -56,9 +55,10 @@ typedef NS_ENUM(NSInteger, TSThreadFriendRequestStatus) {
@property (nonatomic, readonly) TSInteraction *lastInteraction; @property (nonatomic, readonly) TSInteraction *lastInteraction;
// Loki friend request handling // Loki friend request handling
// ======== // ========
@property (nonatomic) TSThreadFriendRequestStatus friendRequestStatus; @property (nonatomic) LKThreadFriendRequestStatus friendRequestStatus;
/// Shorthand for checking that `friendRequestStatus` is `TSThreadFriendRequestStatusRequestSending`, `TSThreadFriendRequestStatusRequestSent` @property (nonatomic, readonly) NSString *friendRequestStatusDescription;
/// or `TSThreadFriendRequestStatusRequestReceived`. /// Shorthand for checking that `friendRequestStatus` is `LKThreadFriendRequestStatusRequestSending`, `LKThreadFriendRequestStatusRequestSent`
/// or `LKThreadFriendRequestStatusRequestReceived`.
@property (nonatomic, readonly) BOOL hasPendingFriendRequest; @property (nonatomic, readonly) BOOL hasPendingFriendRequest;
@property (nonatomic, readonly) BOOL isContactFriend; @property (nonatomic, readonly) BOOL isContactFriend;
@property (nonatomic, readonly) BOOL hasCurrentUserSentFriendRequest; @property (nonatomic, readonly) BOOL hasCurrentUserSentFriendRequest;
@ -199,17 +199,17 @@ typedef NS_ENUM(NSInteger, TSThreadFriendRequestStatus) {
#pragma mark - Loki Friend Request Handling #pragma mark - Loki Friend Request Handling
- (void)saveFriendRequestStatus:(TSThreadFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - (void)saveFriendRequestStatus:(LKThreadFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction;
/** /**
Remove any outgoing friend request message which failed to send Remove any outgoing friend request message which failed to send
*/ */
- (void)removeOldOutgoingFriendRequestMessagesWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)removeOldOutgoingFriendRequestMessagesIfNeededWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/** /**
Remove any old incoming friend request message that is still pending Remove any old incoming friend request message that is still pending
*/ */
- (void)removeOldIncomingFriendRequestMessagesWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)removeOldIncomingFriendRequestMessagesIfNeededWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
@end @end

@ -93,7 +93,7 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
_conversationColorName = [self.class stableColorNameForNewConversationWithString:self.uniqueId]; _conversationColorName = [self.class stableColorNameForNewConversationWithString:self.uniqueId];
} }
_friendRequestStatus = TSThreadFriendRequestStatusNone; _friendRequestStatus = LKThreadFriendRequestStatusNone;
} }
return self; return self;
@ -708,20 +708,20 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
#pragma mark - Loki Friend Request Handling #pragma mark - Loki Friend Request Handling
- (void)removeOldOutgoingFriendRequestMessagesWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)removeOldOutgoingFriendRequestMessagesIfNeededWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
[self removeOldFriendRequestMessages:OWSInteractionType_OutgoingMessage withTransaction:transaction]; [self removeOldFriendRequestMessagesIfNeeded:OWSInteractionType_OutgoingMessage withTransaction:transaction];
} }
- (void)removeOldIncomingFriendRequestMessagesWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)removeOldIncomingFriendRequestMessagesIfNeededWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
[self removeOldFriendRequestMessages:OWSInteractionType_IncomingMessage withTransaction:transaction]; [self removeOldFriendRequestMessagesIfNeeded:OWSInteractionType_IncomingMessage withTransaction:transaction];
} }
- (void)removeOldFriendRequestMessages:(OWSInteractionType)interactionType withTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)removeOldFriendRequestMessagesIfNeeded:(OWSInteractionType)interactionType withTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
// If we're friends with the person then we don't need to remove any friend request messages // If we're friends with the person then we don't need to remove any friend request messages
if (self.friendRequestStatus == TSThreadFriendRequestStatusFriends) { return; } if (self.friendRequestStatus == LKThreadFriendRequestStatusFriends) { return; }
NSMutableArray<NSString *> *idsToRemove = [NSMutableArray new]; NSMutableArray<NSString *> *idsToRemove = [NSMutableArray new];
__block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it __block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it
@ -739,11 +739,11 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
// We want to remove any old incoming friend request messages which are pending // We want to remove any old incoming friend request messages which are pending
if (interactionType == OWSInteractionType_IncomingMessage) { if (interactionType == OWSInteractionType_IncomingMessage) {
removeMessage = message.friendRequestStatus == TSMessageFriendRequestStatusPending; removeMessage = message.friendRequestStatus == LKMessageFriendRequestStatusPending;
} else { } else {
// Or if we're sending then remove any failed friend request messages // Or if we're sending then remove any failed friend request messages
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message; TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message;
removeMessage = outgoingMessage.isFriendRequest && outgoingMessage.messageState == TSOutgoingMessageStateFailed; removeMessage = outgoingMessage.friendRequestStatus == LKMessageFriendRequestStatusSendingOrFailed;
} }
if (removeMessage) { if (removeMessage) {
@ -765,9 +765,10 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
} }
} }
- (void)saveFriendRequestStatus:(TSThreadFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction - (void)saveFriendRequestStatus:(LKThreadFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
{ {
self.friendRequestStatus = friendRequestStatus; self.friendRequestStatus = friendRequestStatus;
OWSLogInfo(@"[Loki] Setting thread friend request status to %@.", self.friendRequestStatusDescription);
void (^postNotification)() = ^() { void (^postNotification)() = ^() {
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.threadFriendRequestStatusChanged object:self.uniqueId]; [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.threadFriendRequestStatusChanged object:self.uniqueId];
}; };
@ -780,25 +781,37 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
} }
} }
- (NSString *)friendRequestStatusDescription
{
switch (self.friendRequestStatus) {
case LKThreadFriendRequestStatusNone: return @"none";
case LKThreadFriendRequestStatusRequestSending: return @"sending";
case LKThreadFriendRequestStatusRequestSent: return @"sent";
case LKThreadFriendRequestStatusRequestReceived: return @"received";
case LKThreadFriendRequestStatusFriends: return @"friends";
case LKThreadFriendRequestStatusRequestExpired: return @"expired";
}
}
- (BOOL)hasPendingFriendRequest - (BOOL)hasPendingFriendRequest
{ {
return self.friendRequestStatus == TSThreadFriendRequestStatusRequestSending || self.friendRequestStatus == TSThreadFriendRequestStatusRequestSent return self.friendRequestStatus == LKThreadFriendRequestStatusRequestSending || self.friendRequestStatus == LKThreadFriendRequestStatusRequestSent
|| self.friendRequestStatus == TSThreadFriendRequestStatusRequestReceived; || self.friendRequestStatus == LKThreadFriendRequestStatusRequestReceived;
} }
- (BOOL)isContactFriend - (BOOL)isContactFriend
{ {
return self.friendRequestStatus == TSThreadFriendRequestStatusFriends; return self.friendRequestStatus == LKThreadFriendRequestStatusFriends;
} }
- (BOOL)hasCurrentUserSentFriendRequest - (BOOL)hasCurrentUserSentFriendRequest
{ {
return self.friendRequestStatus == TSThreadFriendRequestStatusRequestSent; return self.friendRequestStatus == LKThreadFriendRequestStatusRequestSent;
} }
- (BOOL)hasCurrentUserReceivedFriendRequest - (BOOL)hasCurrentUserReceivedFriendRequest
{ {
return self.friendRequestStatus == TSThreadFriendRequestStatusRequestReceived; return self.friendRequestStatus == LKThreadFriendRequestStatusRequestReceived;
} }
@end @end

@ -35,11 +35,14 @@ internal extension LokiAPI {
} }
} }
internal extension AnyPromise { internal extension Promise {
internal static func from<T : Any>(_ promise: Promise<T>) -> AnyPromise { internal func recoveringNetworkErrorsIfNeeded() -> Promise<T> {
let result = AnyPromise(promise) return recover() { error -> Promise<T> in
result.retainUntilComplete() switch error {
return result case NetworkManagerError.taskError(_, let underlyingError): throw underlyingError
default: throw error
}
}
} }
} }

@ -31,7 +31,7 @@ public extension LokiAPI {
// MARK: Internal API // MARK: Internal API
private static func getRandomSnode() -> Promise<Target> { private static func getRandomSnode() -> Promise<Target> {
return Promise<Target> { seal in return Promise<Target> { seal in
seal.fulfill(Target(address: "http://13.236.173.190", port: 8080)) // TODO: For debugging purposes seal.fulfill(Target(address: "http://13.236.173.190", port: defaultSnodePort)) // TODO: For debugging purposes
} }
} }
@ -73,9 +73,10 @@ internal extension Promise {
switch error.statusCode { switch error.statusCode {
case 0: case 0:
// The snode is unreachable; usually a problem with LokiNet // The snode is unreachable; usually a problem with LokiNet
Logger.warn("[Loki] There appears to be a problem with LokiNet.") Logger.warn("[Loki] Couldn't reach snode at: \(target.address):\(target.port).")
case 421: case 421:
// The snode isn't associated with the given public key anymore // The snode isn't associated with the given public key anymore
Logger.warn("[Loki] Invalidating swarm for: \(hexEncodedPublicKey).")
let swarm = LokiAPI.swarmCache[hexEncodedPublicKey] let swarm = LokiAPI.swarmCache[hexEncodedPublicKey]
if var swarm = swarm, let index = swarm.firstIndex(of: target) { if var swarm = swarm, let index = swarm.firstIndex(of: target) {
swarm.remove(at: index) swarm.remove(at: index)

@ -6,6 +6,7 @@ import PromiseKit
// MARK: Settings // MARK: Settings
private static let version = "v1" private static let version = "v1"
public static let defaultMessageTTL: UInt64 = 1 * 24 * 60 * 60 * 1000 public static let defaultMessageTTL: UInt64 = 1 * 24 * 60 * 60 * 1000
private static let maxRetryCount: UInt = 3
private static let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey private static let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
@ -34,7 +35,8 @@ import PromiseKit
internal static func invoke(_ method: Target.Method, on target: Target, associatedWith hexEncodedPublicKey: String, parameters: [String:Any] = [:]) -> Promise<RawResponse> { internal static func invoke(_ method: Target.Method, on target: Target, associatedWith hexEncodedPublicKey: String, parameters: [String:Any] = [:]) -> Promise<RawResponse> {
let url = URL(string: "\(target.address):\(target.port)/\(version)/storage_rpc")! let url = URL(string: "\(target.address):\(target.port)/\(version)/storage_rpc")!
let request = TSRequest(url: url, method: "POST", parameters: [ "method" : method.rawValue, "params" : parameters ]) let request = TSRequest(url: url, method: "POST", parameters: [ "method" : method.rawValue, "params" : parameters ])
return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.handlingSwarmSpecificErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }
.handlingSwarmSpecificErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey).recoveringNetworkErrorsIfNeeded()
} }
// MARK: Public API // MARK: Public API
@ -48,7 +50,7 @@ import PromiseKit
let newRawMessages = removeDuplicates(from: rawMessages) let newRawMessages = removeDuplicates(from: rawMessages)
return parseProtoEnvelopes(from: newRawMessages) return parseProtoEnvelopes(from: newRawMessages)
} }
}.map { Set($0) } }.retryingIfNeeded(maxRetryCount: maxRetryCount).map { Set($0) }
} }
// MARK: Public API (Obj-C) // MARK: Public API (Obj-C)

@ -52,7 +52,7 @@ public enum LokiMessageWrapper {
/// Wrap a `SignalMessage` in an `SSKProtoEnvelope`. /// Wrap a `SignalMessage` in an `SSKProtoEnvelope`.
private static func createEnvelope(around signalMessage: SignalMessage, timestamp: UInt64) throws -> SSKProtoEnvelope { private static func createEnvelope(around signalMessage: SignalMessage, timestamp: UInt64) throws -> SSKProtoEnvelope {
guard let keyPair = SSKEnvironment.shared.identityManager.identityKeyPair() else { guard let keyPair = SSKEnvironment.shared.identityManager.identityKeyPair() else {
owsFailDebug("[Loki] Failed to wrap message in envelope: identityManager.identityKeyPair() is invalid.") owsFailDebug("[Loki] Failed to wrap message in envelope: SSKEnvironment.shared.identityManager.identityKeyPair() is invalid.")
throw WrappingError.failedToWrapMessageInEnvelope throw WrappingError.failedToWrapMessageInEnvelope
} }
do { do {

@ -103,8 +103,8 @@ public enum ProofOfWork {
// Do all the calculations // Do all the calculations
let totalLength = UInt64(payloadLength + nonceLength) let totalLength = UInt64(payloadLength + nonceLength)
let ttlSeconds = ttl / 1000 let ttlInSeconds = ttl / 1000
let ttlMult = ttlSeconds * totalLength let ttlMult = ttlInSeconds * totalLength
// UInt64 values // UInt64 values
let innerFrac = ttlMult / two16 let innerFrac = ttlMult / two16

@ -140,7 +140,7 @@ public final class FriendRequestExpirationJob : NSObject {
private func timerDidFire(isMainTimer: Bool) { private func timerDidFire(isMainTimer: Bool) {
guard CurrentAppContext().isMainAppAndActive else { guard CurrentAppContext().isMainAppAndActive else {
let infoString = isMainTimer ? "Main timer fired while main app is inactive." : "Ignoring fallback timer for app which is not main and active." let infoString = isMainTimer ? "Main timer fired while main app is inactive." : "Ignoring fallback timer for app which is not main and active."
Logger.info("[Loki] Friend request expiration job: \(infoString)") Logger.info("[Loki] Friend request expiration job running: \(infoString).")
return return
} }

@ -6,8 +6,6 @@
@implementation LKFriendRequestMessage @implementation LKFriendRequestMessage
- (BOOL)isFriendRequest { return YES; }
- (uint)ttl { return 4 * kDayInMs; } // Friend requests should stay for the longest on the storage server - (uint)ttl { return 4 * kDayInMs; } // Friend requests should stay for the longest on the storage server
- (SSKProtoContentBuilder *)contentBuilder:(SignalRecipient *)recipient { - (SSKProtoContentBuilder *)contentBuilder:(SignalRecipient *)recipient {

@ -0,0 +1,10 @@
import PromiseKit
internal extension AnyPromise {
internal static func from<T : Any>(_ promise: Promise<T>) -> AnyPromise {
let result = AnyPromise(promise)
result.retainUntilComplete()
return result
}
}

@ -0,0 +1,16 @@
import PromiseKit
internal extension Promise {
internal func retryingIfNeeded(maxRetryCount: UInt) -> Promise<T> {
var retryCount = 0
func retryIfNeeded() -> Promise<T> {
return recover { error -> Promise<T> in
guard retryCount != maxRetryCount else { throw error }
retryCount += 1
return retryIfNeeded()
}
}
return retryIfNeeded()
}
}

@ -17,18 +17,14 @@ NS_ASSUME_NONNULL_BEGIN
@class TSQuotedMessage; @class TSQuotedMessage;
@class YapDatabaseReadWriteTransaction; @class YapDatabaseReadWriteTransaction;
// Loki: Friend request status typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) {
typedef NS_ENUM(NSInteger, TSMessageFriendRequestStatus) { LKMessageFriendRequestStatusNone,
/// Not a friend request message. LKMessageFriendRequestStatusSendingOrFailed,
TSMessageFriendRequestStatusNone, /// Either sent or received.
/// A pending friend request. LKMessageFriendRequestStatusPending,
TSMessageFriendRequestStatusPending, LKMessageFriendRequestStatusAccepted,
/// A friend request that has been accepted. LKMessageFriendRequestStatusDeclined,
TSMessageFriendRequestStatusAccepted, LKMessageFriendRequestStatusExpired
/// A friend request that has been declined.
TSMessageFriendRequestStatusDeclined,
/// A friend request that has expired.
TSMessageFriendRequestStatusExpired
}; };
@interface TSMessage : TSInteraction <OWSPreviewText> @interface TSMessage : TSInteraction <OWSPreviewText>
@ -44,9 +40,11 @@ typedef NS_ENUM(NSInteger, TSMessageFriendRequestStatus) {
@property (nonatomic, readonly, nullable) OWSLinkPreview *linkPreview; @property (nonatomic, readonly, nullable) OWSLinkPreview *linkPreview;
// Loki friend request handling // Loki friend request handling
// ======== // ========
@property (nonatomic) TSMessageFriendRequestStatus friendRequestStatus; @property (nonatomic) LKMessageFriendRequestStatus friendRequestStatus;
@property (nonatomic, readonly) NSString *friendRequestStatusDescription;
@property (nonatomic) uint64_t friendRequestExpiresAt; @property (nonatomic) uint64_t friendRequestExpiresAt;
@property (nonatomic, readonly) BOOL isFriendRequest; @property (nonatomic, readonly) BOOL isFriendRequest;
@property (nonatomic, readonly) BOOL hasFriendRequestStatusMessage;
// ======== // ========
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE;
@ -90,7 +88,7 @@ typedef NS_ENUM(NSInteger, TSMessageFriendRequestStatus) {
#pragma mark - Loki Friend Request Handling #pragma mark - Loki Friend Request Handling
- (void)saveFriendRequestStatus:(TSMessageFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - (void)saveFriendRequestStatus:(LKMessageFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction;
- (void)saveFriendRequestExpiresAt:(u_int64_t)expiresAt withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - (void)saveFriendRequestExpiresAt:(u_int64_t)expiresAt withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction;
@end @end

@ -82,6 +82,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
_quotedMessage = quotedMessage; _quotedMessage = quotedMessage;
_contactShare = contactShare; _contactShare = contactShare;
_linkPreview = linkPreview; _linkPreview = linkPreview;
_friendRequestStatus = LKMessageFriendRequestStatusNone;
_friendRequestExpiresAt = 0; _friendRequestExpiresAt = 0;
return self; return self;
@ -441,9 +442,10 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
#pragma mark - Loki Friend Request Handling #pragma mark - Loki Friend Request Handling
- (void)saveFriendRequestStatus:(TSMessageFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction - (void)saveFriendRequestStatus:(LKMessageFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
{ {
self.friendRequestStatus = friendRequestStatus; self.friendRequestStatus = friendRequestStatus;
OWSLogInfo(@"[Loki] Setting message friend request status to %@.", self.friendRequestStatusDescription);
void (^postNotification)() = ^() { void (^postNotification)() = ^() {
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.messageFriendRequestStatusChanged object:self.uniqueId]; [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.messageFriendRequestStatusChanged object:self.uniqueId];
}; };
@ -456,6 +458,18 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
} }
} }
- (NSString *)friendRequestStatusDescription
{
switch (self.friendRequestStatus) {
case LKMessageFriendRequestStatusNone: return @"none";
case LKMessageFriendRequestStatusSendingOrFailed: return @"sending or failed";
case LKMessageFriendRequestStatusPending: return @"pending";
case LKMessageFriendRequestStatusAccepted: return @"accepted";
case LKMessageFriendRequestStatusDeclined: return @"declined";
case LKMessageFriendRequestStatusExpired: return @"expired";
}
}
- (void)saveFriendRequestExpiresAt:(u_int64_t)expiresAt withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction - (void)saveFriendRequestExpiresAt:(u_int64_t)expiresAt withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
{ {
self.friendRequestExpiresAt = expiresAt; self.friendRequestExpiresAt = expiresAt;
@ -468,7 +482,12 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
- (BOOL)isFriendRequest - (BOOL)isFriendRequest
{ {
return self.friendRequestStatus != TSMessageFriendRequestStatusNone; return self.friendRequestStatus != LKMessageFriendRequestStatusNone;
}
- (BOOL)hasFriendRequestStatusMessage
{
return self.isFriendRequest && self.friendRequestStatus != LKMessageFriendRequestStatusSendingOrFailed;
} }
@end @end

@ -1486,16 +1486,16 @@ NS_ASSUME_NONNULL_BEGIN
// mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request // mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request
// and send a friend request accepted message back to Bob. We don't check that sending the // and send a friend request accepted message back to Bob. We don't check that sending the
// friend request accepted message succeeded. Even if it doesn't, the thread's current friend // friend request accepted message succeeded. Even if it doesn't, the thread's current friend
// request status will be set to TSThreadFriendRequestStatusFriends for Alice making it possible // request status will be set to LKThreadFriendRequestStatusFriends for Alice making it possible
// for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status // for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status
// will then be set to TSThreadFriendRequestStatusFriends. If we do check for a successful send // will then be set to LKThreadFriendRequestStatusFriends. If we do check for a successful send
// before updating Alice's thread's friend request status to TSThreadFriendRequestStatusFriends, // before updating Alice's thread's friend request status to LKThreadFriendRequestStatusFriends,
// we can end up in a deadlock where both users' threads' friend request statuses are // we can end up in a deadlock where both users' threads' friend request statuses are
// TSThreadFriendRequestStatusRequestSent. // LKThreadFriendRequestStatusRequestSent.
[thread saveFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:transaction]; [thread saveFriendRequestStatus:LKThreadFriendRequestStatusFriends withTransaction:transaction];
TSOutgoingMessage *existingFriendRequestMessage = (TSOutgoingMessage *)[thread.lastInteraction as:TSOutgoingMessage.class]; TSOutgoingMessage *existingFriendRequestMessage = (TSOutgoingMessage *)[thread.lastInteraction as:TSOutgoingMessage.class];
if (existingFriendRequestMessage != nil && existingFriendRequestMessage.isFriendRequest) { if (existingFriendRequestMessage != nil && existingFriendRequestMessage.isFriendRequest) {
[existingFriendRequestMessage saveFriendRequestStatus:TSMessageFriendRequestStatusAccepted withTransaction:transaction]; [existingFriendRequestMessage saveFriendRequestStatus:LKMessageFriendRequestStatusAccepted withTransaction:transaction];
} }
// The two lines below are equivalent to calling [ThreadUtil enqueueAcceptFriendRequestMessageInThread:thread] // The two lines below are equivalent to calling [ThreadUtil enqueueAcceptFriendRequestMessageInThread:thread]
LKEphemeralMessage *emptyMessage = [LKEphemeralMessage createEmptyOutgoingMessageInThread:thread]; LKEphemeralMessage *emptyMessage = [LKEphemeralMessage createEmptyOutgoingMessageInThread:thread];
@ -1503,19 +1503,19 @@ NS_ASSUME_NONNULL_BEGIN
} else if (!thread.isContactFriend) { } else if (!thread.isContactFriend) {
// Checking that the sender of the message isn't already a friend is necessary because otherwise // Checking that the sender of the message isn't already a friend is necessary because otherwise
// the following situation can occur: Alice and Bob are friends. Bob loses his database and his // the following situation can occur: Alice and Bob are friends. Bob loses his database and his
// friend request status is reset to TSThreadFriendRequestStatusNone. Bob now sends Alice a friend // friend request status is reset to LKThreadFriendRequestStatusNone. Bob now sends Alice a friend
// request. Alice's thread's friend request status is reset to // request. Alice's thread's friend request status is reset to
// TSThreadFriendRequestStatusRequestReceived. // LKThreadFriendRequestStatusRequestReceived.
[thread saveFriendRequestStatus:TSThreadFriendRequestStatusRequestReceived withTransaction:transaction]; [thread saveFriendRequestStatus:LKThreadFriendRequestStatusRequestReceived withTransaction:transaction];
message.friendRequestStatus = TSMessageFriendRequestStatusPending; // Don't save yet. This is done in finalizeIncomingMessage:thread:envelope:transaction. message.friendRequestStatus = LKMessageFriendRequestStatusPending; // Don't save yet. This is done in finalizeIncomingMessage:thread:envelope:transaction.
} }
} else if (!thread.isContactFriend) { } else if (!thread.isContactFriend) {
// If the thread's friend request status is not TSThreadFriendRequestStatusFriends, but we're receiving a message, // If the thread's friend request status is not LKThreadFriendRequestStatusFriends, but we're receiving a message,
// it must be a friend request accepted message. Declining a friend request doesn't send a message. // it must be a friend request accepted message. Declining a friend request doesn't send a message.
[thread saveFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:transaction]; [thread saveFriendRequestStatus:LKThreadFriendRequestStatusFriends withTransaction:transaction];
TSOutgoingMessage *existingFriendRequestMessage = (TSOutgoingMessage *)[thread.lastInteraction as:TSOutgoingMessage.class]; TSOutgoingMessage *existingFriendRequestMessage = (TSOutgoingMessage *)[thread.lastInteraction as:TSOutgoingMessage.class];
if (existingFriendRequestMessage != nil && existingFriendRequestMessage.isFriendRequest) { if (existingFriendRequestMessage != nil && existingFriendRequestMessage.isFriendRequest) {
[existingFriendRequestMessage saveFriendRequestStatus:TSMessageFriendRequestStatusAccepted withTransaction:transaction]; [existingFriendRequestMessage saveFriendRequestStatus:LKMessageFriendRequestStatusAccepted withTransaction:transaction];
} }
// Send our p2p details to the other user // Send our p2p details to the other user
@ -1552,7 +1552,7 @@ NS_ASSUME_NONNULL_BEGIN
// Remove any old incoming messages // Remove any old incoming messages
if (incomingMessage.isFriendRequest) { if (incomingMessage.isFriendRequest) {
[thread removeOldIncomingFriendRequestMessagesWithTransaction:transaction]; [thread removeOldIncomingFriendRequestMessagesIfNeededWithTransaction:transaction];
} }
// Any messages sent from the current user - from this device or another - should be automatically marked as read. // Any messages sent from the current user - from this device or another - should be automatically marked as read.

@ -21,7 +21,7 @@ public class OWSMessageSend: NSObject {
@objc @objc
public let recipient: SignalRecipient public let recipient: SignalRecipient
private static let kMaxRetriesPerRecipient: Int = 3 private static let kMaxRetriesPerRecipient: Int = 1
@objc @objc
public var remainingAttempts = OWSMessageSend.kMaxRetriesPerRecipient public var remainingAttempts = OWSMessageSend.kMaxRetriesPerRecipient

@ -1067,10 +1067,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
hasValidMessageType = [validMessageTypes containsObject:messageType]; hasValidMessageType = [validMessageTypes containsObject:messageType];
/* Loki: Original code /* Loki: Original code
hasValidMessageType = ([messageType isEqualToNumber:@(TSEncryptedWhisperMessageType)] || * ========
[messageType isEqualToNumber:@(TSPreKeyWhisperMessageType)]); hasValidMessageType = ([messageType isEqualToNumber:@(TSEncryptedWhisperMessageType)] || [messageType isEqualToNumber:@(TSPreKeyWhisperMessageType)]);
* ========
*/ */
} }
if (!hasValidMessageType) { if (!hasValidMessageType) {
@ -1104,16 +1104,46 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return messageSend.failure(error); return messageSend.failure(error);
} }
// Update the state to show that the proof of work is being calculated // Get the message parameters and type
[self saveIsCalculatingProofOfWork:YES forMessage:messageSend];
// Convert the message to a Loki message and send it using the Loki messaging API
NSDictionary *signalMessage = deviceMessages.firstObject; NSDictionary *signalMessage = deviceMessages.firstObject;
// Update the message and thread if needed TSWhisperMessageType messageType = ((NSNumber *)signalMessage[@"type"]).integerValue;
NSInteger *messageType = ((NSNumber *)signalMessage[@"type"]).integerValue; [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if (messageType == TSFriendRequestMessageType) { // Update the PoW calculation status
[message.thread saveFriendRequestStatus:TSThreadFriendRequestStatusRequestSending withTransaction:nil]; [message saveIsCalculatingProofOfWork:YES withTransaction:transaction];
[message saveFriendRequestStatus:TSMessageFriendRequestStatusPending withTransaction:nil]; // Update the message and thread if needed
} if (messageType == TSFriendRequestMessageType) {
[message.thread saveFriendRequestStatus:LKThreadFriendRequestStatusRequestSending withTransaction:transaction];
[message saveFriendRequestStatus:LKMessageFriendRequestStatusSendingOrFailed withTransaction:transaction];
}
}];
// Convenience
void (^handleError)(NSError *error) = ^(NSError *error) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// Update the thread if needed
if (messageType == TSFriendRequestMessageType) {
[message.thread saveFriendRequestStatus:LKThreadFriendRequestStatusNone withTransaction:transaction];
[message saveFriendRequestStatus:LKMessageFriendRequestStatusSendingOrFailed withTransaction:transaction];
}
// Update the PoW calculation status
[message saveIsCalculatingProofOfWork:NO withTransaction:transaction];
}];
// Handle the error
NSUInteger statusCode = 0;
NSData *_Nullable responseData = nil;
if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) {
statusCode = error.code;
NSError *_Nullable underlyingError = error.userInfo[NSUnderlyingErrorKey];
if (underlyingError) {
responseData = underlyingError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
} else {
OWSFailDebug(@"Missing underlying error: %@.", error);
}
} else {
OWSFailDebug(@"Unexpected error: %@.", error);
}
[self messageSendDidFail:messageSend deviceMessages:deviceMessages statusCode:statusCode error:error responseData:responseData];
};
// Convert the message to a Loki message and send it using the Loki API
[[LokiAPI objc_sendSignalMessage:signalMessage to:recipient.recipientId with:message.timestamp] [[LokiAPI objc_sendSignalMessage:signalMessage to:recipient.recipientId with:message.timestamp]
.thenOn(OWSDispatch.sendingQueue, ^(id result) { .thenOn(OWSDispatch.sendingQueue, ^(id result) {
NSSet<AnyPromise *> *promises = (NSSet<AnyPromise *> *)result; NSSet<AnyPromise *> *promises = (NSSet<AnyPromise *> *)result;
@ -1125,11 +1155,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
.thenOn(OWSDispatch.sendingQueue, ^(id result) { .thenOn(OWSDispatch.sendingQueue, ^(id result) {
if (isSuccess) { return; } // Succeed as soon as the first promise succeeds if (isSuccess) { return; } // Succeed as soon as the first promise succeeds
isSuccess = YES; isSuccess = YES;
// Update the message and thread if needed
if (messageType == TSFriendRequestMessageType) { if (messageType == TSFriendRequestMessageType) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message.thread saveFriendRequestStatus:TSThreadFriendRequestStatusRequestSent withTransaction:transaction]; // Update the thread
[message.thread removeOldOutgoingFriendRequestMessagesWithTransaction:transaction]; [message.thread saveFriendRequestStatus:LKThreadFriendRequestStatusRequestSent withTransaction:transaction];
[message.thread removeOldOutgoingFriendRequestMessagesIfNeededWithTransaction:transaction];
// Update the message
[message saveFriendRequestStatus:LKMessageFriendRequestStatusPending withTransaction:transaction];
NSTimeInterval expirationInterval = 72 * kHourInterval; NSTimeInterval expirationInterval = 72 * kHourInterval;
NSDate *expirationDate = [[NSDate new] dateByAddingTimeInterval:expirationInterval]; NSDate *expirationDate = [[NSDate new] dateByAddingTimeInterval:expirationInterval];
[message saveFriendRequestExpiresAt:[NSDate ows_millisecondsSince1970ForDate:expirationDate] withTransaction:transaction]; [message saveFriendRequestExpiresAt:[NSDate ows_millisecondsSince1970ForDate:expirationDate] withTransaction:transaction];
@ -1141,29 +1173,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
.catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) {
errorCount += 1; errorCount += 1;
if (errorCount != promiseCount) { return; } // Only error out if all promises failed if (errorCount != promiseCount) { return; } // Only error out if all promises failed
// Update the thread if needed handleError(error);
if (messageType == TSFriendRequestMessageType) {
[message.thread saveFriendRequestStatus:TSThreadFriendRequestStatusNone withTransaction:nil];
}
// Update the PoW calculation status
[self saveIsCalculatingProofOfWork:NO forMessage:messageSend];
// Handle the error
NSUInteger statusCode = 0;
NSData *_Nullable responseData = nil;
if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) {
statusCode = error.code;
NSError *_Nullable underlyingError = error.userInfo[NSUnderlyingErrorKey];
if (underlyingError) {
responseData = underlyingError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
} else {
OWSFailDebug(@"Missing underlying error: %@.", error);
}
} else {
OWSFailDebug(@"Unexpected error: %@.", error);
}
[self messageSendDidFail:messageSend deviceMessages:deviceMessages statusCode:statusCode error:error responseData:responseData];
}) retainUntilComplete]; }) retainUntilComplete];
} }
})
.catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { // Unreachable snode; usually a problem with LokiNet
handleError(error);
}) retainUntilComplete]; }) retainUntilComplete];
// Loki: Original code // Loki: Original code
@ -1229,16 +1244,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
*/ */
} }
- (void)saveIsCalculatingProofOfWork:(BOOL)isCalculatingPoW forMessage:(OWSMessageSend *)messageSend
{
OWSAssertDebug(messageSend);
dispatch_async(OWSDispatch.sendingQueue, ^{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[messageSend.message saveIsCalculatingProofOfWork:isCalculatingPoW withTransaction:transaction];
}];
});
}
- (void)messageSendDidSucceed:(OWSMessageSend *)messageSend - (void)messageSendDidSucceed:(OWSMessageSend *)messageSend
deviceMessages:(NSArray<NSDictionary *> *)deviceMessages deviceMessages:(NSArray<NSDictionary *> *)deviceMessages
wasSentByUD:(BOOL)wasSentByUD wasSentByUD:(BOOL)wasSentByUD
@ -1331,6 +1336,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}; };
switch (statusCode) { switch (statusCode) {
case 0: { // Loki
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:NO];
return messageSend.failure(error);
}
case 401: { case 401: {
OWSLogWarn(@"Unable to send due to invalid credentials. Did the user's client get de-authed by " OWSLogWarn(@"Unable to send due to invalid credentials. Did the user's client get de-authed by "
@"registering elsewhere?"); @"registering elsewhere?");

@ -81,7 +81,7 @@ public class MessageSenderJobQueue: NSObject, JobQueue {
public typealias DurableOperationType = MessageSenderOperation public typealias DurableOperationType = MessageSenderOperation
public static let jobRecordLabel: String = "MessageSender" public static let jobRecordLabel: String = "MessageSender"
public static let maxRetries: UInt = 30 public static let maxRetries: UInt = 1
public let requiresInternet: Bool = true public let requiresInternet: Bool = true
public var runningOperations: [MessageSenderOperation] = [] public var runningOperations: [MessageSenderOperation] = []

Loading…
Cancel
Save