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];
if (isStarted) {
[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;
}
}

@ -12,6 +12,12 @@
enum Kind : String { case incoming, outgoing }
// MARK: Components
private lazy var topSpacer: UIView = {
let result = UIView()
result.autoSetDimension(.height, toSize: 12)
return result
}()
private lazy var label: UILabel = {
let result = UILabel()
result.textColor = Theme.secondaryColor
@ -47,6 +53,7 @@
let mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.distribution = .fill
mainStackView.addArrangedSubview(topSpacer)
mainStackView.addArrangedSubview(label)
switch kind {
case .incoming:
@ -85,23 +92,33 @@
buttonStackView.isHidden = message.friendRequestStatus != .pending
let format: String = {
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 .declined: return NSLocalizedString("You've declined %@'s friend request", 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)
case .outgoing:
guard let message = message as? TSOutgoingMessage else { preconditionFailure() }
let format: String = {
let format: String? = {
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 .declined: preconditionFailure()
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
@objc static func calculateHeight(message: TSMessage, conversationStyle: ConversationStyle) -> CGFloat {
let width = conversationStyle.contentWidth
let topSpacing: CGFloat = 12
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 buttonHeight = hasButtonStackView ? dummyFriendRequestView.buttonHeight : 0
let totalHeight = topSpacing + labelHeight + buttonHeight

@ -214,7 +214,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.viewConstraints addObjectsFromArray:@[
[self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:self.conversationStyle.gutterLeading],
[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]
]];
}

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

@ -407,7 +407,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
} else if (self.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;
}

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

@ -93,7 +93,7 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
_conversationColorName = [self.class stableColorNameForNewConversationWithString:self.uniqueId];
}
_friendRequestStatus = TSThreadFriendRequestStatusNone;
_friendRequestStatus = LKThreadFriendRequestStatusNone;
}
return self;
@ -708,20 +708,20 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
#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 (self.friendRequestStatus == TSThreadFriendRequestStatusFriends) { return; }
if (self.friendRequestStatus == LKThreadFriendRequestStatusFriends) { return; }
NSMutableArray<NSString *> *idsToRemove = [NSMutableArray new];
__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
if (interactionType == OWSInteractionType_IncomingMessage) {
removeMessage = message.friendRequestStatus == TSMessageFriendRequestStatusPending;
removeMessage = message.friendRequestStatus == LKMessageFriendRequestStatusPending;
} else {
// Or if we're sending then remove any failed friend request messages
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message;
removeMessage = outgoingMessage.isFriendRequest && outgoingMessage.messageState == TSOutgoingMessageStateFailed;
removeMessage = outgoingMessage.friendRequestStatus == LKMessageFriendRequestStatusSendingOrFailed;
}
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;
OWSLogInfo(@"[Loki] Setting thread friend request status to %@.", self.friendRequestStatusDescription);
void (^postNotification)() = ^() {
[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
{
return self.friendRequestStatus == TSThreadFriendRequestStatusRequestSending || self.friendRequestStatus == TSThreadFriendRequestStatusRequestSent
|| self.friendRequestStatus == TSThreadFriendRequestStatusRequestReceived;
return self.friendRequestStatus == LKThreadFriendRequestStatusRequestSending || self.friendRequestStatus == LKThreadFriendRequestStatusRequestSent
|| self.friendRequestStatus == LKThreadFriendRequestStatusRequestReceived;
}
- (BOOL)isContactFriend
{
return self.friendRequestStatus == TSThreadFriendRequestStatusFriends;
return self.friendRequestStatus == LKThreadFriendRequestStatusFriends;
}
- (BOOL)hasCurrentUserSentFriendRequest
{
return self.friendRequestStatus == TSThreadFriendRequestStatusRequestSent;
return self.friendRequestStatus == LKThreadFriendRequestStatusRequestSent;
}
- (BOOL)hasCurrentUserReceivedFriendRequest
{
return self.friendRequestStatus == TSThreadFriendRequestStatusRequestReceived;
return self.friendRequestStatus == LKThreadFriendRequestStatusRequestReceived;
}
@end

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

@ -31,7 +31,7 @@ public extension LokiAPI {
// MARK: Internal API
private static func getRandomSnode() -> Promise<Target> {
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 {
case 0:
// 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:
// The snode isn't associated with the given public key anymore
Logger.warn("[Loki] Invalidating swarm for: \(hexEncodedPublicKey).")
let swarm = LokiAPI.swarmCache[hexEncodedPublicKey]
if var swarm = swarm, let index = swarm.firstIndex(of: target) {
swarm.remove(at: index)

@ -6,6 +6,7 @@ import PromiseKit
// MARK: Settings
private static let version = "v1"
public static let defaultMessageTTL: UInt64 = 1 * 24 * 60 * 60 * 1000
private static let maxRetryCount: UInt = 3
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> {
let url = URL(string: "\(target.address):\(target.port)/\(version)/storage_rpc")!
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
@ -48,7 +50,7 @@ import PromiseKit
let newRawMessages = removeDuplicates(from: rawMessages)
return parseProtoEnvelopes(from: newRawMessages)
}
}.map { Set($0) }
}.retryingIfNeeded(maxRetryCount: maxRetryCount).map { Set($0) }
}
// MARK: Public API (Obj-C)

@ -52,7 +52,7 @@ public enum LokiMessageWrapper {
/// Wrap a `SignalMessage` in an `SSKProtoEnvelope`.
private static func createEnvelope(around signalMessage: SignalMessage, timestamp: UInt64) throws -> SSKProtoEnvelope {
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
}
do {

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

@ -140,7 +140,7 @@ public final class FriendRequestExpirationJob : NSObject {
private func timerDidFire(isMainTimer: Bool) {
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."
Logger.info("[Loki] Friend request expiration job: \(infoString)")
Logger.info("[Loki] Friend request expiration job running: \(infoString).")
return
}

@ -6,8 +6,6 @@
@implementation LKFriendRequestMessage
- (BOOL)isFriendRequest { return YES; }
- (uint)ttl { return 4 * kDayInMs; } // Friend requests should stay for the longest on the storage server
- (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 YapDatabaseReadWriteTransaction;
// Loki: Friend request status
typedef NS_ENUM(NSInteger, TSMessageFriendRequestStatus) {
/// Not a friend request message.
TSMessageFriendRequestStatusNone,
/// A pending friend request.
TSMessageFriendRequestStatusPending,
/// A friend request that has been accepted.
TSMessageFriendRequestStatusAccepted,
/// A friend request that has been declined.
TSMessageFriendRequestStatusDeclined,
/// A friend request that has expired.
TSMessageFriendRequestStatusExpired
typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) {
LKMessageFriendRequestStatusNone,
LKMessageFriendRequestStatusSendingOrFailed,
/// Either sent or received.
LKMessageFriendRequestStatusPending,
LKMessageFriendRequestStatusAccepted,
LKMessageFriendRequestStatusDeclined,
LKMessageFriendRequestStatusExpired
};
@interface TSMessage : TSInteraction <OWSPreviewText>
@ -44,9 +40,11 @@ typedef NS_ENUM(NSInteger, TSMessageFriendRequestStatus) {
@property (nonatomic, readonly, nullable) OWSLinkPreview *linkPreview;
// Loki friend request handling
// ========
@property (nonatomic) TSMessageFriendRequestStatus friendRequestStatus;
@property (nonatomic) LKMessageFriendRequestStatus friendRequestStatus;
@property (nonatomic, readonly) NSString *friendRequestStatusDescription;
@property (nonatomic) uint64_t friendRequestExpiresAt;
@property (nonatomic, readonly) BOOL isFriendRequest;
@property (nonatomic, readonly) BOOL hasFriendRequestStatusMessage;
// ========
- (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
- (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;
@end

@ -82,6 +82,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
_quotedMessage = quotedMessage;
_contactShare = contactShare;
_linkPreview = linkPreview;
_friendRequestStatus = LKMessageFriendRequestStatusNone;
_friendRequestExpiresAt = 0;
return self;
@ -441,9 +442,10 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
#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;
OWSLogInfo(@"[Loki] Setting message friend request status to %@.", self.friendRequestStatusDescription);
void (^postNotification)() = ^() {
[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
{
self.friendRequestExpiresAt = expiresAt;
@ -468,7 +482,12 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
- (BOOL)isFriendRequest
{
return self.friendRequestStatus != TSMessageFriendRequestStatusNone;
return self.friendRequestStatus != LKMessageFriendRequestStatusNone;
}
- (BOOL)hasFriendRequestStatusMessage
{
return self.isFriendRequest && self.friendRequestStatus != LKMessageFriendRequestStatusSendingOrFailed;
}
@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
// 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
// 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
// will then be set to TSThreadFriendRequestStatusFriends. If we do check for a successful send
// before updating Alice's thread's friend request status to TSThreadFriendRequestStatusFriends,
// will then be set to LKThreadFriendRequestStatusFriends. If we do check for a successful send
// 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
// TSThreadFriendRequestStatusRequestSent.
[thread saveFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:transaction];
// LKThreadFriendRequestStatusRequestSent.
[thread saveFriendRequestStatus:LKThreadFriendRequestStatusFriends withTransaction:transaction];
TSOutgoingMessage *existingFriendRequestMessage = (TSOutgoingMessage *)[thread.lastInteraction as:TSOutgoingMessage.class];
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]
LKEphemeralMessage *emptyMessage = [LKEphemeralMessage createEmptyOutgoingMessageInThread:thread];
@ -1503,19 +1503,19 @@ NS_ASSUME_NONNULL_BEGIN
} else if (!thread.isContactFriend) {
// 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
// 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
// TSThreadFriendRequestStatusRequestReceived.
[thread saveFriendRequestStatus:TSThreadFriendRequestStatusRequestReceived withTransaction:transaction];
message.friendRequestStatus = TSMessageFriendRequestStatusPending; // Don't save yet. This is done in finalizeIncomingMessage:thread:envelope:transaction.
// LKThreadFriendRequestStatusRequestReceived.
[thread saveFriendRequestStatus:LKThreadFriendRequestStatusRequestReceived withTransaction:transaction];
message.friendRequestStatus = LKMessageFriendRequestStatusPending; // Don't save yet. This is done in finalizeIncomingMessage:thread:envelope:transaction.
}
} 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.
[thread saveFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:transaction];
[thread saveFriendRequestStatus:LKThreadFriendRequestStatusFriends withTransaction:transaction];
TSOutgoingMessage *existingFriendRequestMessage = (TSOutgoingMessage *)[thread.lastInteraction as:TSOutgoingMessage.class];
if (existingFriendRequestMessage != nil && existingFriendRequestMessage.isFriendRequest) {
[existingFriendRequestMessage saveFriendRequestStatus:TSMessageFriendRequestStatusAccepted withTransaction:transaction];
[existingFriendRequestMessage saveFriendRequestStatus:LKMessageFriendRequestStatusAccepted withTransaction:transaction];
}
// Send our p2p details to the other user
@ -1552,7 +1552,7 @@ NS_ASSUME_NONNULL_BEGIN
// Remove any old incoming messages
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.

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

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

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

Loading…
Cancel
Save