From 17596d8bd3b421a888d7881a12312829312622e2 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 10:48:26 +1000 Subject: [PATCH 01/14] Added PublicChatInfo field to DataMessage. --- SignalServiceKit/protobuf/SignalService.proto | 6 + .../src/Protos/Generated/SSKProto.swift | 112 +++++++++++++++++- .../Protos/Generated/SignalService.pb.swift | 69 +++++++++++ 3 files changed, 185 insertions(+), 2 deletions(-) diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index 7c92ad5f7..cd1e9c816 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -253,6 +253,7 @@ message DataMessage { repeated Contact contact = 9; repeated Preview preview = 10; optional LokiProfile profile = 101; // Loki: The current user's profile + optional PublicChatInfo publicChatInfo = 900; // Loki: Internal public chat info } message NullMessage { @@ -421,3 +422,8 @@ message GroupDetails { optional string color = 7; optional bool blocked = 8; } + +// Internal type - DO NOT SEND +message PublicChatInfo { + optional uint64 serverId = 1; +} diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index c1e3c4657..a54585ac9 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -3308,6 +3308,9 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { if let _value = profile { builder.setProfile(_value) } + if let _value = publicChatInfo { + builder.setPublicChatInfo(_value) + } return builder } @@ -3379,6 +3382,10 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { proto.profile = valueParam.proto } + @objc public func setPublicChatInfo(_ valueParam: SSKProtoPublicChatInfo) { + proto.publicChatInfo = valueParam.proto + } + @objc public func build() throws -> SSKProtoDataMessage { return try SSKProtoDataMessage.parseProto(proto) } @@ -3402,6 +3409,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { @objc public let profile: SSKProtoDataMessageLokiProfile? + @objc public let publicChatInfo: SSKProtoPublicChatInfo? + @objc public var body: String? { guard proto.hasBody else { return nil @@ -3449,7 +3458,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { quote: SSKProtoDataMessageQuote?, contact: [SSKProtoDataMessageContact], preview: [SSKProtoDataMessagePreview], - profile: SSKProtoDataMessageLokiProfile?) { + profile: SSKProtoDataMessageLokiProfile?, + publicChatInfo: SSKProtoPublicChatInfo?) { self.proto = proto self.attachments = attachments self.group = group @@ -3457,6 +3467,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { self.contact = contact self.preview = preview self.profile = profile + self.publicChatInfo = publicChatInfo } @objc @@ -3494,6 +3505,11 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { profile = try SSKProtoDataMessageLokiProfile.parseProto(proto.profile) } + var publicChatInfo: SSKProtoPublicChatInfo? = nil + if proto.hasPublicChatInfo { + publicChatInfo = try SSKProtoPublicChatInfo.parseProto(proto.publicChatInfo) + } + // MARK: - Begin Validation Logic for SSKProtoDataMessage - // MARK: - End Validation Logic for SSKProtoDataMessage - @@ -3504,7 +3520,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { quote: quote, contact: contact, preview: preview, - profile: profile) + profile: profile, + publicChatInfo: publicChatInfo) return result } @@ -6215,3 +6232,94 @@ extension SSKProtoGroupDetails.SSKProtoGroupDetailsBuilder { } #endif + +// MARK: - SSKProtoPublicChatInfo + +@objc public class SSKProtoPublicChatInfo: NSObject { + + // MARK: - SSKProtoPublicChatInfoBuilder + + @objc public class func builder() -> SSKProtoPublicChatInfoBuilder { + return SSKProtoPublicChatInfoBuilder() + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SSKProtoPublicChatInfoBuilder { + let builder = SSKProtoPublicChatInfoBuilder() + if hasServerID { + builder.setServerID(serverID) + } + return builder + } + + @objc public class SSKProtoPublicChatInfoBuilder: NSObject { + + private var proto = SignalServiceProtos_PublicChatInfo() + + @objc fileprivate override init() {} + + @objc public func setServerID(_ valueParam: UInt64) { + proto.serverID = valueParam + } + + @objc public func build() throws -> SSKProtoPublicChatInfo { + return try SSKProtoPublicChatInfo.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SSKProtoPublicChatInfo.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SignalServiceProtos_PublicChatInfo + + @objc public var serverID: UInt64 { + return proto.serverID + } + @objc public var hasServerID: Bool { + return proto.hasServerID + } + + private init(proto: SignalServiceProtos_PublicChatInfo) { + self.proto = proto + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SSKProtoPublicChatInfo { + let proto = try SignalServiceProtos_PublicChatInfo(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SignalServiceProtos_PublicChatInfo) throws -> SSKProtoPublicChatInfo { + // MARK: - Begin Validation Logic for SSKProtoPublicChatInfo - + + // MARK: - End Validation Logic for SSKProtoPublicChatInfo - + + let result = SSKProtoPublicChatInfo(proto: proto) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SSKProtoPublicChatInfo { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SSKProtoPublicChatInfo.SSKProtoPublicChatInfoBuilder { + @objc public func buildIgnoringErrors() -> SSKProtoPublicChatInfo? { + return try! self.build() + } +} + +#endif diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 8f2176d04..091118e4e 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -802,6 +802,16 @@ struct SignalServiceProtos_DataMessage { /// Clears the value of `profile`. Subsequent reads from it will return its default value. mutating func clearProfile() {_uniqueStorage()._profile = nil} + /// Loki: Internal public chat info + var publicChatInfo: SignalServiceProtos_PublicChatInfo { + get {return _storage._publicChatInfo ?? SignalServiceProtos_PublicChatInfo()} + set {_uniqueStorage()._publicChatInfo = newValue} + } + /// Returns true if `publicChatInfo` has been explicitly set. + var hasPublicChatInfo: Bool {return _storage._publicChatInfo != nil} + /// Clears the value of `publicChatInfo`. Subsequent reads from it will return its default value. + mutating func clearPublicChatInfo() {_uniqueStorage()._publicChatInfo = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() enum Flags: SwiftProtobuf.Enum { @@ -2492,6 +2502,28 @@ struct SignalServiceProtos_GroupDetails { fileprivate var _storage = _StorageClass.defaultInstance } +/// Internal type - DO NOT SEND +struct SignalServiceProtos_PublicChatInfo { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var serverID: UInt64 { + get {return _serverID ?? 0} + set {_serverID = newValue} + } + /// Returns true if `serverID` has been explicitly set. + var hasServerID: Bool {return self._serverID != nil} + /// Clears the value of `serverID`. Subsequent reads from it will return its default value. + mutating func clearServerID() {self._serverID = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _serverID: UInt64? = nil +} + // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "SignalServiceProtos" @@ -3146,6 +3178,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. 9: .same(proto: "contact"), 10: .same(proto: "preview"), 101: .same(proto: "profile"), + 900: .same(proto: "publicChatInfo"), ] fileprivate class _StorageClass { @@ -3160,6 +3193,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. var _contact: [SignalServiceProtos_DataMessage.Contact] = [] var _preview: [SignalServiceProtos_DataMessage.Preview] = [] var _profile: SignalServiceProtos_DataMessage.LokiProfile? = nil + var _publicChatInfo: SignalServiceProtos_PublicChatInfo? = nil static let defaultInstance = _StorageClass() @@ -3177,6 +3211,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. _contact = source._contact _preview = source._preview _profile = source._profile + _publicChatInfo = source._publicChatInfo } } @@ -3203,6 +3238,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. case 9: try decoder.decodeRepeatedMessageField(value: &_storage._contact) case 10: try decoder.decodeRepeatedMessageField(value: &_storage._preview) case 101: try decoder.decodeSingularMessageField(value: &_storage._profile) + case 900: try decoder.decodeSingularMessageField(value: &_storage._publicChatInfo) default: break } } @@ -3244,6 +3280,9 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. if let v = _storage._profile { try visitor.visitSingularMessageField(value: v, fieldNumber: 101) } + if let v = _storage._publicChatInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 900) + } } try unknownFields.traverse(visitor: &visitor) } @@ -3264,6 +3303,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. if _storage._contact != rhs_storage._contact {return false} if _storage._preview != rhs_storage._preview {return false} if _storage._profile != rhs_storage._profile {return false} + if _storage._publicChatInfo != rhs_storage._publicChatInfo {return false} return true } if !storagesAreEqual {return false} @@ -5113,3 +5153,32 @@ extension SignalServiceProtos_GroupDetails.Avatar: SwiftProtobuf.Message, SwiftP return true } } + +extension SignalServiceProtos_PublicChatInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".PublicChatInfo" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "serverId"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularUInt64Field(value: &self._serverID) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._serverID { + try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SignalServiceProtos_PublicChatInfo, rhs: SignalServiceProtos_PublicChatInfo) -> Bool { + if lhs._serverID != rhs._serverID {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} From 2392fed21a16645f78837f4b594e95c56522dc5b Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 11:09:28 +1000 Subject: [PATCH 02/14] Set the server id on public chat messages. --- Signal/src/Loki/LokiGroupChatPoller.swift | 8 ++++++++ .../src/Messages/Interactions/TSMessage.h | 10 ++++++++++ .../src/Messages/Interactions/TSMessage.m | 18 ++++++++++++++++++ .../src/Messages/OWSMessageManager.m | 3 ++- .../src/Messages/OWSMessageSender.m | 1 + 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 080644cde..d18f53480 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -44,6 +44,14 @@ public final class LokiGroupChatPoller : NSObject { x2.setTimestamp(message.timestamp) x2.setGroup(try! x1.build()) x2.setBody(message.body) + + // Pass down the message server id + if let messageServerID = message.serverID { + let publicChatInfo = SSKProtoPublicChatInfo.builder() + publicChatInfo.setServerID(messageServerID) + x2.setPublicChatInfo(try! publicChatInfo.build()) + } + let x3 = SSKProtoContent.builder() x3.setDataMessage(try! x2.build()) let x4 = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: message.timestamp) diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index 14c38e934..f00c4a159 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -48,6 +48,12 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) { @property (nonatomic) BOOL isP2P; // ======== +// Public Chat +// ======== +@property (nonatomic) uint64_t publicChatMessageID; +@property (nonatomic, readonly) BOOL isPublicChatMessage; +// ======== + - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; - (instancetype)initMessageWithTimestamp:(uint64_t)timestamp @@ -92,6 +98,10 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) { - (void)saveFriendRequestStatus:(LKMessageFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - (void)saveFriendRequestExpiresAt:(u_int64_t)expiresAt withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; +#pragma mark - Public Chat + +- (void)savePublicChatMessageID:(uint64_t)serverMessageId withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 440e64b58..f49e63098 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -84,6 +84,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _linkPreview = linkPreview; _friendRequestStatus = LKMessageFriendRequestStatusNone; _friendRequestExpiresAt = 0; + _publicChatMessageID = -1; return self; } @@ -490,6 +491,23 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return self.isFriendRequest && self.friendRequestStatus != LKMessageFriendRequestStatusSendingOrFailed; } +#pragma mark - Public chat handling + +- (BOOL) isPublicChatMessage { + return self.publicChatMessageID > 0; +} + +- (void)savePublicChatMessageID:(uint64_t)serverMessageId withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction { + self.publicChatMessageID = serverMessageId; + if (transaction == nil) { + [self save]; + [self.dbReadWriteConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{}]; + } else { + [self saveWithTransaction:transaction]; + [transaction.connection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{}]; + } +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 394535ec9..d8f3a4e9c 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1360,7 +1360,7 @@ NS_ASSUME_NONNULL_BEGIN envelopeAddress(envelope), groupId, (unsigned long)timestamp); - + // Legit usage of senderTimestamp when creating an incoming group message record TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp @@ -1377,6 +1377,7 @@ NS_ASSUME_NONNULL_BEGIN wasReceivedByUD:wasReceivedByUD]; if (envelope.isPtpMessage) { incomingMessage.isP2P = YES; } + if (dataMessage.publicChatInfo && dataMessage.publicChatInfo.hasServerID) { incomingMessage.publicChatMessageID = dataMessage.publicChatInfo.serverID; } NSArray *attachmentPointers = [TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 369b089ad..a45cf6c6b 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1117,6 +1117,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [[LKGroupChatAPI sendMessage:groupMessage toGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer] .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [message savePublicChatMessageID:groupMessage.serverID withTransaction:transaction]; [OWSPrimaryStorage.sharedManager setIDForMessageWithServerID:groupMessage.serverID to:message.uniqueId in:transaction]; }]; [self messageSendDidSucceed:messageSend deviceMessages:deviceMessages wasSentByUD:false wasSentByWebsocket:false]; From 64c2e36c8afe93c87a0f42642533c2b4581b4919 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 13:27:11 +1000 Subject: [PATCH 03/14] Handle messages sent by us in the public chat. --- Signal/src/Loki/LokiGroupChatPoller.swift | 97 +++++++++++++------ .../src/Loki/API/LokiGroupChatAPI.swift | 1 - .../src/Loki/Crypto/OWSPrimaryStorage+Loki.h | 2 +- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.m | 2 +- 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index d18f53480..d180a365d 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -9,6 +9,9 @@ public final class LokiGroupChatPoller : NSObject { private let pollForNewMessagesInterval: TimeInterval = 4 private let pollForDeletedMessagesInterval: TimeInterval = 32 * 60 + private let storage = OWSPrimaryStorage.shared() + private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + @objc(initForGroup:) public init(for group: LokiGroupChat) { self.group = group @@ -31,40 +34,80 @@ public final class LokiGroupChatPoller : NSObject { private func pollForNewMessages() { let group = self.group - let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done { messages in + let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done { [weak self] messages in + guard let self = self else { return } messages.reversed().forEach { message in let senderHexEncodedPublicKey = message.hexEncodedPublicKey - let endIndex = senderHexEncodedPublicKey.endIndex - let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) - let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. (lastMessageServerID ?? 0) { setLastMessageServerID(for: group, on: server, to: serverID) } diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h index 53a9a1ba4..061732d35 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h @@ -96,7 +96,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setLastMessageHashForServiceNode:(NSString *)serviceNode hash:(NSString *)hash expiresAt:(u_int64_t)expiresAt transaction:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setLastMessageHash(forServiceNode:hash:expiresAt:transaction:)); - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; -- (NSString *)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; +- (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; @end diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m index 17f778a6c..a70240f01 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m @@ -168,7 +168,7 @@ [transaction setObject:messageID forKey:key inCollection:LKMessageIDCollection]; } -- (NSString *)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction { +- (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction { NSString *key = [NSString stringWithFormat:@"%@", @(serverID)]; return [transaction objectForKey:key inCollection:LKMessageIDCollection]; } From d3978f5cfb5d83499c39a1f3bab0e3859acb76e9 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 13:27:11 +1000 Subject: [PATCH 04/14] Correctly show our old messages in the public chat. --- Signal/src/Loki/LokiGroupChatPoller.swift | 80 ++++++++++++++----- .../src/Loki/API/LokiGroupChatAPI.swift | 1 - .../src/Loki/Crypto/OWSPrimaryStorage+Loki.h | 2 +- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.m | 2 +- 4 files changed, 63 insertions(+), 22 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 080644cde..4cbd1d19f 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -9,6 +9,9 @@ public final class LokiGroupChatPoller : NSObject { private let pollForNewMessagesInterval: TimeInterval = 4 private let pollForDeletedMessagesInterval: TimeInterval = 32 * 60 + private let storage = OWSPrimaryStorage.shared() + private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + @objc(initForGroup:) public init(for group: LokiGroupChat) { self.group = group @@ -31,32 +34,71 @@ public final class LokiGroupChatPoller : NSObject { private func pollForNewMessages() { let group = self.group - let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done { messages in + let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done { [weak self] messages in + guard let self = self else { return } messages.reversed().forEach { message in let senderHexEncodedPublicKey = message.hexEncodedPublicKey - let endIndex = senderHexEncodedPublicKey.endIndex - let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) - let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. (lastMessageServerID ?? 0) { setLastMessageServerID(for: group, on: server, to: serverID) } diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h index 53a9a1ba4..061732d35 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h @@ -96,7 +96,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setLastMessageHashForServiceNode:(NSString *)serviceNode hash:(NSString *)hash expiresAt:(u_int64_t)expiresAt transaction:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setLastMessageHash(forServiceNode:hash:expiresAt:transaction:)); - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; -- (NSString *)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; +- (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; @end diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m index 17f778a6c..a70240f01 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m @@ -168,7 +168,7 @@ [transaction setObject:messageID forKey:key inCollection:LKMessageIDCollection]; } -- (NSString *)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction { +- (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction { NSString *key = [NSString stringWithFormat:@"%@", @(serverID)]; return [transaction objectForKey:key inCollection:LKMessageIDCollection]; } From e68de54a8762c96a89b2ad8e19aea207fbd3a932 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 15:18:42 +1000 Subject: [PATCH 05/14] Show the delete action in public chats. --- Signal/src/Models/MessageActions.swift | 2 +- .../ColorPickerViewController.swift | 2 + .../ConversationView/ConversationViewItem.h | 1 + .../ConversationView/ConversationViewItem.m | 45 +++++++++++++++++++ .../src/Contacts/Threads/TSGroupThread.h | 1 + .../src/Contacts/Threads/TSGroupThread.m | 6 +++ .../src/Loki/API/LokiRSSFeed.swift | 2 +- 7 files changed, 57 insertions(+), 2 deletions(-) diff --git a/Signal/src/Models/MessageActions.swift b/Signal/src/Models/MessageActions.swift index ede7198e5..9b2c6c7da 100644 --- a/Signal/src/Models/MessageActions.swift +++ b/Signal/src/Models/MessageActions.swift @@ -86,7 +86,7 @@ class ConversationViewItemActions: NSObject { actions.append(copyTextAction) } - if !isGroup { + if !isGroup || conversationViewItem.canDeleteGroupMessage { let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(deleteAction) } diff --git a/Signal/src/ViewControllers/ColorPickerViewController.swift b/Signal/src/ViewControllers/ColorPickerViewController.swift index df687f60f..7fb02acc2 100644 --- a/Signal/src/ViewControllers/ColorPickerViewController.swift +++ b/Signal/src/ViewControllers/ColorPickerViewController.swift @@ -305,6 +305,8 @@ class ColorPickerView: UIView, ColorViewDelegate { @objc private class MockConversationViewItem: NSObject, ConversationViewItem { + var canDeleteGroupMessage: Bool = false + var interaction: TSInteraction = TSMessage() var interactionType: OWSInteractionType = OWSInteractionType.unknown var quotedReply: OWSQuotedReplyModel? diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 22bcf932c..ad60957c4 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -67,6 +67,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly, nullable) OWSQuotedReplyModel *quotedReply; @property (nonatomic, readonly) BOOL isGroupThread; +@property (nonatomic, readonly) BOOL canDeleteGroupMessage; @property (nonatomic, readonly) BOOL hasBodyText; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 9c7c25297..259c87173 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1161,6 +1161,30 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (void)deleteAction { + // TODO: Handle deletion differently for public chats + if (self.isGroupThread) { + TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; + + // If it's RSS then just proceed normally + if (groupThread.isRSS) { + [self.interaction remove]; + return; + }; + + // Only allow deletion on incoming and outgoing messages + OWSInteractionType interationType = self.interaction.interactionType; + if (interationType != OWSInteractionType_OutgoingMessage && interationType != OWSInteractionType_IncomingMessage) return; + + // Check that we have the server id for the message + TSMessage *message = (TSMessage *)self.interaction; + if (!message.isPublicChatMessage) return; + + // TODO: Call the group api here to delete the message + + // Just return and don't delete for now + return; + } + [self.interaction remove]; } @@ -1204,6 +1228,27 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return NO; } +- (BOOL)canDeleteGroupMessage +{ + if (!self.isGroupThread) return false; + + // Make sure it's a public chat and not an rss feed + TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; + if (groupThread.isRSS) return false; + + // Only allow deletion on incoming and outgoing messages + OWSInteractionType interationType = self.interaction.interactionType; + if (interationType != OWSInteractionType_OutgoingMessage && interationType != OWSInteractionType_IncomingMessage) return false; + + // Make sure it's a public chat message + TSMessage *message = (TSMessage *)self.interaction; + if (!message.isPublicChatMessage) return false; + + // TODO: Disable deletion of incoming messages if we're not moderators + + return true; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h index bc214f93b..8fb376da2 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h +++ b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h @@ -16,6 +16,7 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; @interface TSGroupThread : TSThread @property (nonatomic, strong) TSGroupModel *groupModel; +@property (nonatomic, readonly) BOOL isRSS; + (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel; + (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel diff --git a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m index af20285bb..7110d7921 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m @@ -266,6 +266,12 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific return [self.class stableColorNameForNewConversationWithString:[self threadIdFromGroupId:groupId]]; } +- (BOOL)isRSS +{ + NSString *groupID = [[NSString alloc] initWithData:self.groupModel.groupId encoding:NSUTF8StringEncoding]; + return groupID != nil && [groupID containsString:@"rss:"]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift b/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift index 44a4807a8..96daef891 100644 --- a/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift +++ b/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift @@ -7,7 +7,7 @@ public final class LokiRSSFeed : NSObject { @objc public let isDeletable: Bool @objc public init(id: String, server: String, displayName: String, isDeletable: Bool) { - self.id = id + self.id = "rss://\(id)" self.server = server self.displayName = displayName self.isDeletable = isDeletable From d795b0a9ad3dd2ac564354e9848d6fdc0f169252 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 15:49:16 +1000 Subject: [PATCH 06/14] Don't allow input on rss groups. --- .../ConversationView/ConversationViewController.m | 12 +++++++++++- .../src/Contacts/Threads/TSGroupThread.h | 1 + .../src/Contacts/Threads/TSGroupThread.m | 6 ++++++ SignalServiceKit/src/Loki/API/LokiRSSFeed.swift | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 8947a9239..58eb2626c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -564,6 +564,16 @@ typedef enum : NSUInteger { return !groupThread.isLocalUserInGroup; } +- (BOOL)isRSSGroup +{ + if (![_thread isKindOfClass:[TSGroupThread class]]) { + return NO; + } + + TSGroupThread *groupThread = (TSGroupThread *)self.thread; + return groupThread.isRSS; +} + - (void)hideInputIfNeeded { if (_peek) { @@ -572,7 +582,7 @@ typedef enum : NSUInteger { return; } - if (self.userLeftGroup) { + if (self.userLeftGroup || self.isRSSGroup) { self.inputToolbar.hidden = YES; // user has requested they leave the group. further sends disallowed [self dismissKeyBoard]; } else { diff --git a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h index bc214f93b..8fb376da2 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h +++ b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h @@ -16,6 +16,7 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; @interface TSGroupThread : TSThread @property (nonatomic, strong) TSGroupModel *groupModel; +@property (nonatomic, readonly) BOOL isRSS; + (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel; + (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel diff --git a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m index af20285bb..7110d7921 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m @@ -266,6 +266,12 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific return [self.class stableColorNameForNewConversationWithString:[self threadIdFromGroupId:groupId]]; } +- (BOOL)isRSS +{ + NSString *groupID = [[NSString alloc] initWithData:self.groupModel.groupId encoding:NSUTF8StringEncoding]; + return groupID != nil && [groupID containsString:@"rss:"]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift b/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift index 44a4807a8..96daef891 100644 --- a/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift +++ b/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift @@ -7,7 +7,7 @@ public final class LokiRSSFeed : NSObject { @objc public let isDeletable: Bool @objc public init(id: String, server: String, displayName: String, isDeletable: Bool) { - self.id = id + self.id = "rss://\(id)" self.server = server self.displayName = displayName self.isDeletable = isDeletable From bf09a2014ba6747c8e20715bc76474fe8ef413c1 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 28 Aug 2019 16:07:14 +1000 Subject: [PATCH 07/14] Hookup api. --- Signal/src/AppDelegate.m | 2 +- .../ConversationView/ConversationViewItem.m | 24 +++++++------- .../src/Loki/API/LokiGroupChatAPI.swift | 32 ++++++++++++++++++- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 91670eb03..d7ab77bf4 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1489,7 +1489,7 @@ static NSTimeInterval launchStartedAt; - (LKGroupChat *)lokiPublicChat { - return [[LKGroupChat alloc] initWithServerID:@(LKGroupChatAPI.publicChatServerID).unsignedIntegerValue server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true]; + return [[LKGroupChat alloc] initWithServerID:LKGroupChatAPI.publicChatServerID server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true]; } - (LKRSSFeed *)lokiNewsFeed diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 259c87173..76db80e6c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -9,6 +9,7 @@ #import "OWSMessageHeaderView.h" #import "OWSSystemMessageCell.h" #import "Session-Swift.h" +#import "AnyPromise.h" #import #import #import @@ -1161,15 +1162,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (void)deleteAction { - // TODO: Handle deletion differently for public chats + // Delete it optimistically + [self.interaction remove]; + if (self.isGroupThread) { + // If it's RSS then skip TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; - - // If it's RSS then just proceed normally - if (groupThread.isRSS) { - [self.interaction remove]; - return; - }; + if (groupThread.isRSS) return; // Only allow deletion on incoming and outgoing messages OWSInteractionType interationType = self.interaction.interactionType; @@ -1179,13 +1178,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) TSMessage *message = (TSMessage *)self.interaction; if (!message.isPublicChatMessage) return; - // TODO: Call the group api here to delete the message - - // Just return and don't delete for now - return; + // Delete the message + [[LKGroupChatAPI deleteMessageWithServerID:message.publicChatMessageID forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer isOurOwnMessage:interationType == OWSInteractionType_OutgoingMessage].catch(^(NSError *error) { + // If we fail then add the interaction back in + [self.interaction save]; + }) retainUntilComplete]; } - - [self.interaction remove]; } - (BOOL)hasBodyTextActionContent diff --git a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift index ea701bb3b..f1f02b7b0 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift @@ -11,7 +11,7 @@ public final class LokiGroupChatAPI : NSObject { // MARK: Public Chat @objc public static let publicChatServer = "https://chat.lokinet.org" @objc public static let publicChatMessageType = "network.loki.messenger.publicChat" - @objc public static let publicChatServerID = 1 + @objc public static let publicChatServerID: UInt = 1 // MARK: Convenience private static var userDisplayName: String { @@ -205,6 +205,31 @@ public final class LokiGroupChatAPI : NSObject { } } + public static func deleteMessageWithServerID(_ messageServerID: UInt, for group: UInt64, on server: String, isOurOwnMessage: Bool = true) -> Promise { + return getAuthToken(for: server).then { token -> Promise in + let modTag = isOurOwnMessage ? "" : "[Mod]" + print("[Loki]\(modTag) Deleting message with server ID: \(messageServerID) for group chat with ID: \(group) on server: \(server).") + + let endpoint = isOurOwnMessage ? "\(server)/channels/\(group)/messages/\(messageServerID)" : "\(server)/loki/v1/moderation/message/\(messageServerID)" + let url = URL(string: endpoint)! + let request = TSRequest(url: url, method: "DELETE", parameters: [:]) + request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] + return TSNetworkManager.shared().makePromise(request: request).map { result -> Void in + print("[Loki]\(modTag) Deleted message \(messageServerID) on server \(server).") + }.recover { error in + // If we got 404 or 410 then message doesn't exist on the server + if let error = error as? NetworkManagerError, error.statusCode == 404 || error.statusCode == 410 { + print("[Loki]\(modTag) Message \(messageServerID) was already deleted on the server.") + return + } + + print("[Loki]\(modTag) Failed to delete message \(messageServerID) on server \(server).") + throw error + } + } + + } + // MARK: Public API (Obj-C) @objc(getMessagesForGroup:onServer:) public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise { @@ -215,4 +240,9 @@ public final class LokiGroupChatAPI : NSObject { public static func objc_sendMessage(_ message: LokiGroupMessage, to group: UInt64, on server: String) -> AnyPromise { return AnyPromise.from(sendMessage(message, to: group, on: server)) } + + @objc (deleteMessageWithServerID:forGroup:onServer:isOurOwnMessage:) + public static func objc_deleteMessageWithServerID(_ messageServerID: UInt, for group: UInt64, on server: String, ourMessage: Bool = true) -> AnyPromise { + return AnyPromise.from(deleteMessageWithServerID(messageServerID, for: group, on: server, isOurOwnMessage: ourMessage)) + } } From 679b2f7742b8045abdee8c017a12cdf0488b6ebd Mon Sep 17 00:00:00 2001 From: Mikunj Date: Thu, 29 Aug 2019 12:49:06 +1000 Subject: [PATCH 08/14] Added mod polling --- Signal.xcodeproj/project.pbxproj | 12 ++++-- Signal/src/AppDelegate.m | 3 ++ Signal/src/Loki/LokiGroupChatModPoller.swift | 40 +++++++++++++++++++ .../ConversationView/ConversationViewItem.m | 18 ++++++++- .../src/Loki/API/LokiGroupChatAPI.swift | 19 ++++++++- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.h | 7 ++++ .../src/Loki/Crypto/OWSPrimaryStorage+Loki.m | 9 +++++ 7 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 Signal/src/Loki/LokiGroupChatModPoller.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 95173afe4..2f8e7de9b 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 24684784231772CD00600430 /* LokiGroupChatModPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24684783231772CD00600430 /* LokiGroupChatModPoller.swift */; }; 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */; }; 2AE2882E4C2B96BFFF9EE27C /* Pods_SignalShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */; }; 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; }; @@ -660,6 +661,7 @@ 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; + 24684783231772CD00600430 /* LokiGroupChatModPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiGroupChatModPoller.swift; sourceTree = ""; }; 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiP2PServer.swift; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = ""; }; @@ -2615,17 +2617,18 @@ B8439518228510E9000563FE /* Loki */ = { isa = PBXGroup; children = ( + B821F2F72272CED3002C88C0 /* AccountDetailsViewController.swift */, B8162F0222891AD600D46544 /* FriendRequestView.swift */, B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, - B89841E222B7579F00B1BDC6 /* NewConversationViewController.swift */, - B821F2F72272CED3002C88C0 /* AccountDetailsViewController.swift */, - B821F2F92272CEEE002C88C0 /* SeedViewController.swift */, - 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, + 24684783231772CD00600430 /* LokiGroupChatModPoller.swift */, B845B4D3230CD09000D759F0 /* LokiGroupChatPoller.swift */, + 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */, + B89841E222B7579F00B1BDC6 /* NewConversationViewController.swift */, B825848A230F94FE001B41CB /* QRCodeViewController.swift */, B8258491230FA5DA001B41CB /* ScanQRCodeViewController.h */, B8258492230FA5E9001B41CB /* ScanQRCodeViewController.m */, + B821F2F92272CEEE002C88C0 /* SeedViewController.swift */, ); path = Loki; sourceTree = ""; @@ -3627,6 +3630,7 @@ 341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */, 340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */, 34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */, + 24684784231772CD00600430 /* LokiGroupChatModPoller.swift in Sources */, 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */, 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */, 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index d7ab77bf4..048dfe836 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -67,6 +67,7 @@ static NSTimeInterval launchStartedAt; @property (nonatomic) LKGroupChatPoller *lokiPublicChatPoller; @property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller; @property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller; +@property (nonatomic) LKGroupChatModPoller *lokiModPoller; @end @@ -1555,6 +1556,7 @@ static NSTimeInterval launchStartedAt; - (void)createGroupChatPollersIfNeeded { if (self.lokiPublicChatPoller == nil) { self.lokiPublicChatPoller = [[LKGroupChatPoller alloc] initForGroup:self.lokiPublicChat]; } + if (self.lokiModPoller == nil) { self.lokiModPoller = [[LKGroupChatModPoller alloc] initForGroup:self.lokiPublicChat]; } } - (void)createRSSFeedPollersIfNeeded @@ -1567,6 +1569,7 @@ static NSTimeInterval launchStartedAt; { [self createGroupChatPollersIfNeeded]; [self.lokiPublicChatPoller startIfNeeded]; + [self.lokiModPoller startIfNeeded]; } - (void)startRSSFeedPollersIfNeeded diff --git a/Signal/src/Loki/LokiGroupChatModPoller.swift b/Signal/src/Loki/LokiGroupChatModPoller.swift new file mode 100644 index 000000000..0cf7408ad --- /dev/null +++ b/Signal/src/Loki/LokiGroupChatModPoller.swift @@ -0,0 +1,40 @@ + +@objc(LKGroupChatModPoller) +public final class LokiGroupChatModPoller : NSObject { + private let group: LokiGroupChat + private var timer: Timer? = nil + private var hasStarted = false + + private let interval: TimeInterval = 10 * 60 + + private let storage = OWSPrimaryStorage.shared() + private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + + @objc(initForGroup:) + public init(for group: LokiGroupChat) { + self.group = group + super.init() + } + + @objc public func startIfNeeded() { + if hasStarted { return } + timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in self?.pollForModeratorStatus() } + pollForModeratorStatus() // Perform initial update + hasStarted = true + } + + @objc public func stop() { + timer?.invalidate() + hasStarted = false + } + + private func pollForModeratorStatus() { + let group = self.group + let _ = LokiGroupChatAPI.isCurrentUserMod(on: group.server).done { [weak self] isModerator in + guard let self = self else { return } + self.storage.dbReadWriteConnection.readWrite { transaction in + self.storage.setIsModerator(isModerator, for: group.server, transaction: transaction) + } + } + } +} diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 76db80e6c..c6842aa42 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -15,6 +15,8 @@ #import #import #import +#import +#import NS_ASSUME_NONNULL_BEGIN @@ -199,6 +201,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } } +- (OWSPrimaryStorage *)primaryStorage +{ + OWSAssertDebug(SSKEnvironment.shared.primaryStorage); + + return SSKEnvironment.shared.primaryStorage; +} + - (NSString *)itemId { return self.interaction.uniqueId; @@ -1242,7 +1251,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) TSMessage *message = (TSMessage *)self.interaction; if (!message.isPublicChatMessage) return false; - // TODO: Disable deletion of incoming messages if we're not moderators + // Don't allow deletion if we're not mods on incoming messages + if (interationType == OWSInteractionType_IncomingMessage) { + __block BOOL isModerator; + [[self primaryStorage].dbReadWriteConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + isModerator = [[self primaryStorage] getIsModeratorForServer:LKGroupChatAPI.publicChatServer transaction:transaction]; + }]; + if (!isModerator) return false; + } return true; } diff --git a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift index f1f02b7b0..51b884777 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift @@ -28,7 +28,7 @@ public final class LokiGroupChatAPI : NSObject { // MARK: Error public enum Error : Swift.Error { - case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed + case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed, jsonParsingFailed } // MARK: Database @@ -230,6 +230,23 @@ public final class LokiGroupChatAPI : NSObject { } + public static func isCurrentUserMod(on server: String) -> Promise { + return getAuthToken(for: server).then { token -> Promise in + let url = URL(string: "\(server)/loki/v1/user_info")! + let request = TSRequest(url: url) + request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] + return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in + guard let json = rawResponse as? JSON, let data = json["data"] as? JSON else { + print("[Loki] Couldn't parse json for user info.") + throw Error.jsonParsingFailed + } + + // moderator_status is not set for users that are not mods + return data["moderator_status"] as? Bool ?? false + } + } + } + // MARK: Public API (Obj-C) @objc(getMessagesForGroup:onServer:) public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise { diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h index 061732d35..aaa4bf2af 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h @@ -95,9 +95,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setLastMessageHashForServiceNode:(NSString *)serviceNode hash:(NSString *)hash expiresAt:(u_int64_t)expiresAt transaction:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setLastMessageHash(forServiceNode:hash:expiresAt:transaction:)); +# pragma mark - Public chat + - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; - (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; +- (void)setIsModerator:(BOOL)isModerator forServer:(NSString *)server transaction:(YapDatabaseReadWriteTransaction *)transaction + NS_SWIFT_NAME(setIsModerator(_:for:transaction:)); +- (BOOL)getIsModeratorForServer:(NSString *)server transaction:(YapDatabaseReadTransaction *)transaction + NS_SWIFT_NAME(getIsModerator(forServer:transaction:)); + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m index a70240f01..eafd3a2f8 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m @@ -19,6 +19,7 @@ #define LKReceivedMessageHashesKey @"LKReceivedMessageHashesKey" #define LKReceivedMessageHashesCollection @"LKReceivedMessageHashesCollection" #define LKMessageIDCollection @"LKMessageIDCollection" +#define LKModeratorCollection @"LKModerationCollection" @implementation OWSPrimaryStorage (Loki) @@ -173,4 +174,12 @@ return [transaction objectForKey:key inCollection:LKMessageIDCollection]; } +- (void)setIsModerator:(BOOL)isModerator forServer:(NSString *)server transaction:(YapDatabaseReadWriteTransaction *)transaction { + [transaction setBool:isModerator forKey:server inCollection:LKModeratorCollection]; +} + +- (BOOL)getIsModeratorForServer:(NSString *)server transaction:(YapDatabaseReadTransaction *)transaction { + return [transaction boolForKey:server inCollection:LKModeratorCollection defaultValue:false]; +} + @end From 7bc37be34db1356dddb51c05913a7b505f075cc4 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Thu, 29 Aug 2019 15:17:00 +1000 Subject: [PATCH 09/14] Minor fix --- .../ViewControllers/ConversationView/ConversationViewItem.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index c6842aa42..1750ec2a4 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1177,7 +1177,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) if (self.isGroupThread) { // If it's RSS then skip TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; - if (groupThread.isRSS) return; + if (groupThread.isRSSFeed) return; // Only allow deletion on incoming and outgoing messages OWSInteractionType interationType = self.interaction.interactionType; @@ -1241,7 +1241,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) // Make sure it's a public chat and not an rss feed TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; - if (groupThread.isRSS) return false; + if (groupThread.isRSSFeed) return false; // Only allow deletion on incoming and outgoing messages OWSInteractionType interationType = self.interaction.interactionType; From a1c0db61ec47e1bf5df0d19f68be2b21ab21f421 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 2 Sep 2019 12:35:28 +1000 Subject: [PATCH 10/14] Make polling for moderation permission part of LokiGroupChatPoller --- Signal.xcodeproj/project.pbxproj | 4 -- Signal/src/Loki/LokiGroupChatModPoller.swift | 40 -------------------- Signal/src/Loki/LokiGroupChatPoller.swift | 14 +++++++ 3 files changed, 14 insertions(+), 44 deletions(-) delete mode 100644 Signal/src/Loki/LokiGroupChatModPoller.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 2f8e7de9b..f10fe677d 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 24684784231772CD00600430 /* LokiGroupChatModPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24684783231772CD00600430 /* LokiGroupChatModPoller.swift */; }; 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */; }; 2AE2882E4C2B96BFFF9EE27C /* Pods_SignalShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */; }; 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; }; @@ -661,7 +660,6 @@ 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; - 24684783231772CD00600430 /* LokiGroupChatModPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiGroupChatModPoller.swift; sourceTree = ""; }; 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiP2PServer.swift; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = ""; }; @@ -2620,7 +2618,6 @@ B821F2F72272CED3002C88C0 /* AccountDetailsViewController.swift */, B8162F0222891AD600D46544 /* FriendRequestView.swift */, B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, - 24684783231772CD00600430 /* LokiGroupChatModPoller.swift */, B845B4D3230CD09000D759F0 /* LokiGroupChatPoller.swift */, 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */, @@ -3630,7 +3627,6 @@ 341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */, 340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */, 34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */, - 24684784231772CD00600430 /* LokiGroupChatModPoller.swift in Sources */, 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */, 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */, 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */, diff --git a/Signal/src/Loki/LokiGroupChatModPoller.swift b/Signal/src/Loki/LokiGroupChatModPoller.swift deleted file mode 100644 index 0cf7408ad..000000000 --- a/Signal/src/Loki/LokiGroupChatModPoller.swift +++ /dev/null @@ -1,40 +0,0 @@ - -@objc(LKGroupChatModPoller) -public final class LokiGroupChatModPoller : NSObject { - private let group: LokiGroupChat - private var timer: Timer? = nil - private var hasStarted = false - - private let interval: TimeInterval = 10 * 60 - - private let storage = OWSPrimaryStorage.shared() - private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey - - @objc(initForGroup:) - public init(for group: LokiGroupChat) { - self.group = group - super.init() - } - - @objc public func startIfNeeded() { - if hasStarted { return } - timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in self?.pollForModeratorStatus() } - pollForModeratorStatus() // Perform initial update - hasStarted = true - } - - @objc public func stop() { - timer?.invalidate() - hasStarted = false - } - - private func pollForModeratorStatus() { - let group = self.group - let _ = LokiGroupChatAPI.isCurrentUserMod(on: group.server).done { [weak self] isModerator in - guard let self = self else { return } - self.storage.dbReadWriteConnection.readWrite { transaction in - self.storage.setIsModerator(isModerator, for: group.server, transaction: transaction) - } - } - } -} diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 1320769a8..ae8061f28 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -4,10 +4,12 @@ public final class LokiGroupChatPoller : NSObject { private let group: LokiGroupChat private var pollForNewMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil + private var pollForModerationPermissionTimer: Timer? = nil private var hasStarted = false private let pollForNewMessagesInterval: TimeInterval = 4 private let pollForDeletedMessagesInterval: TimeInterval = 20 + private let pollForModerationPermissionInterval: TimeInterval = 10 * 60 private let storage = OWSPrimaryStorage.shared() private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey @@ -23,12 +25,14 @@ public final class LokiGroupChatPoller : NSObject { pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForNewMessagesInterval, repeats: true) { [weak self] _ in self?.pollForNewMessages() } pollForNewMessages() // Perform initial update pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForDeletedMessagesInterval, repeats: true) { [weak self] _ in self?.pollForDeletedMessages() } + pollForModerationPermissionTimer = Timer.scheduledTimer(withTimeInterval: pollForModerationPermissionInterval, repeats: true) { [weak self] _ in self?.pollForModerationPermission() } hasStarted = true } @objc public func stop() { pollForNewMessagesTimer?.invalidate() pollForDeletedMessagesTimer?.invalidate() + pollForModerationPermissionTimer?.invalidate() hasStarted = false } @@ -120,4 +124,14 @@ public final class LokiGroupChatPoller : NSObject { } } } + + private func pollForModerationPermission() { + let group = self.group + let _ = LokiGroupChatAPI.isCurrentUserMod(on: group.server).done { [weak self] isModerator in + guard let self = self else { return } + self.storage.dbReadWriteConnection.readWrite { transaction in + self.storage.setIsModerator(isModerator, for: group.server, transaction: transaction) + } + } + } } From 928301298837fcf2de4786165483ece70b365a68 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 2 Sep 2019 12:56:42 +1000 Subject: [PATCH 11/14] Make moderation permission both group and server specific Also fix dependence on self and app delegate issue --- Signal/src/AppDelegate.m | 3 --- Signal/src/Loki/LokiGroupChatPoller.swift | 7 +++---- .../ConversationView/ConversationViewItem.m | 2 +- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.h | 7 ++----- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.m | 12 +++++++----- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 3eeec4b4c..1d0c684cb 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -70,7 +70,6 @@ static NSTimeInterval launchStartedAt; @property (nonatomic) LKGroupChatPoller *lokiPublicChatPoller; @property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller; @property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller; -@property (nonatomic) LKGroupChatModPoller *lokiModPoller; @end @@ -1571,7 +1570,6 @@ static NSTimeInterval launchStartedAt; - (void)createGroupChatPollersIfNeeded { if (self.lokiPublicChatPoller == nil) { self.lokiPublicChatPoller = [[LKGroupChatPoller alloc] initForGroup:self.lokiPublicChat]; } - if (self.lokiModPoller == nil) { self.lokiModPoller = [[LKGroupChatModPoller alloc] initForGroup:self.lokiPublicChat]; } } - (void)createRSSFeedPollersIfNeeded @@ -1584,7 +1582,6 @@ static NSTimeInterval launchStartedAt; { [self createGroupChatPollersIfNeeded]; [self.lokiPublicChatPoller startIfNeeded]; - [self.lokiModPoller startIfNeeded]; } - (void)startRSSFeedPollersIfNeeded diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index ae8061f28..65bd710c2 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -127,10 +127,9 @@ public final class LokiGroupChatPoller : NSObject { private func pollForModerationPermission() { let group = self.group - let _ = LokiGroupChatAPI.isCurrentUserMod(on: group.server).done { [weak self] isModerator in - guard let self = self else { return } - self.storage.dbReadWriteConnection.readWrite { transaction in - self.storage.setIsModerator(isModerator, for: group.server, transaction: transaction) + let _ = LokiGroupChatAPI.isCurrentUserMod(on: group.server).done { [storage] isModerator in + storage.dbReadWriteConnection.readWrite { transaction in + storage.setIsModerator(isModerator, for: UInt(group.serverID), on: group.server, in: transaction) } } } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 1750ec2a4..f6da3dd37 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1255,7 +1255,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) if (interationType == OWSInteractionType_IncomingMessage) { __block BOOL isModerator; [[self primaryStorage].dbReadWriteConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - isModerator = [[self primaryStorage] getIsModeratorForServer:LKGroupChatAPI.publicChatServer transaction:transaction]; + isModerator = [[self primaryStorage] isModeratorForGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer in:transaction]; }]; if (!isModerator) return false; } diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h index aaa4bf2af..ed6e781b3 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h @@ -99,11 +99,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; - (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; - -- (void)setIsModerator:(BOOL)isModerator forServer:(NSString *)server transaction:(YapDatabaseReadWriteTransaction *)transaction - NS_SWIFT_NAME(setIsModerator(_:for:transaction:)); -- (BOOL)getIsModeratorForServer:(NSString *)server transaction:(YapDatabaseReadTransaction *)transaction - NS_SWIFT_NAME(getIsModerator(forServer:transaction:)); +- (void)setIsModerator:(BOOL)isModerator forGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setIsModerator(_:for:on:in:)); +- (BOOL)isModeratorForGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(isModerator(for:on:in:)); @end diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m index eafd3a2f8..42b870e06 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m @@ -19,7 +19,7 @@ #define LKReceivedMessageHashesKey @"LKReceivedMessageHashesKey" #define LKReceivedMessageHashesCollection @"LKReceivedMessageHashesCollection" #define LKMessageIDCollection @"LKMessageIDCollection" -#define LKModeratorCollection @"LKModerationCollection" +#define LKModerationPermissionCollection @"LKModerationPermissionCollection" @implementation OWSPrimaryStorage (Loki) @@ -174,12 +174,14 @@ return [transaction objectForKey:key inCollection:LKMessageIDCollection]; } -- (void)setIsModerator:(BOOL)isModerator forServer:(NSString *)server transaction:(YapDatabaseReadWriteTransaction *)transaction { - [transaction setBool:isModerator forKey:server inCollection:LKModeratorCollection]; +- (void)setIsModerator:(BOOL)isModerator forGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadWriteTransaction *)transaction { + NSString *key = [NSString stringWithFormat:@"%@.%@", server, @(group)]; + [transaction setBool:isModerator forKey:key inCollection:LKModerationPermissionCollection]; } -- (BOOL)getIsModeratorForServer:(NSString *)server transaction:(YapDatabaseReadTransaction *)transaction { - return [transaction boolForKey:server inCollection:LKModeratorCollection defaultValue:false]; +- (BOOL)isModeratorForGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(isModerator(for:on:in:)) { + NSString *key = [NSString stringWithFormat:@"%@.%@", server, @(group)]; + return [transaction boolForKey:key inCollection:LKModerationPermissionCollection defaultValue:false]; } @end From 3a1d07e5a8eb7a61dcfc3a2c45918c210165df00 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 2 Sep 2019 13:27:12 +1000 Subject: [PATCH 12/14] Fix promise usage & clean --- Signal/src/Loki/LokiGroupChatPoller.swift | 2 +- Signal/src/Models/MessageActions.swift | 2 +- .../ColorPickerViewController.swift | 3 +- .../ConversationView/ConversationViewItem.h | 2 +- .../ConversationView/ConversationViewItem.m | 20 ++++--- .../src/Loki/API/LokiGroupChatAPI.swift | 53 +++++++------------ .../src/Loki/Crypto/OWSPrimaryStorage+Loki.h | 4 +- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.m | 2 + 8 files changed, 37 insertions(+), 51 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 65bd710c2..ccf9ed5ce 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -127,7 +127,7 @@ public final class LokiGroupChatPoller : NSObject { private func pollForModerationPermission() { let group = self.group - let _ = LokiGroupChatAPI.isCurrentUserMod(on: group.server).done { [storage] isModerator in + let _ = LokiGroupChatAPI.userHasModerationPermission(for: group.serverID, on: group.server).done { [storage] isModerator in storage.dbReadWriteConnection.readWrite { transaction in storage.setIsModerator(isModerator, for: UInt(group.serverID), on: group.server, in: transaction) } diff --git a/Signal/src/Models/MessageActions.swift b/Signal/src/Models/MessageActions.swift index 9b2c6c7da..f65defe0e 100644 --- a/Signal/src/Models/MessageActions.swift +++ b/Signal/src/Models/MessageActions.swift @@ -86,7 +86,7 @@ class ConversationViewItemActions: NSObject { actions.append(copyTextAction) } - if !isGroup || conversationViewItem.canDeleteGroupMessage { + if !isGroup || conversationViewItem.userCanDeleteGroupMessage { let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(deleteAction) } diff --git a/Signal/src/ViewControllers/ColorPickerViewController.swift b/Signal/src/ViewControllers/ColorPickerViewController.swift index 7fb02acc2..f82137efd 100644 --- a/Signal/src/ViewControllers/ColorPickerViewController.swift +++ b/Signal/src/ViewControllers/ColorPickerViewController.swift @@ -305,8 +305,7 @@ class ColorPickerView: UIView, ColorViewDelegate { @objc private class MockConversationViewItem: NSObject, ConversationViewItem { - var canDeleteGroupMessage: Bool = false - + var userCanDeleteGroupMessage: Bool = false var interaction: TSInteraction = TSMessage() var interactionType: OWSInteractionType = OWSInteractionType.unknown var quotedReply: OWSQuotedReplyModel? diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index ad60957c4..6504d0ef9 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -67,7 +67,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly, nullable) OWSQuotedReplyModel *quotedReply; @property (nonatomic, readonly) BOOL isGroupThread; -@property (nonatomic, readonly) BOOL canDeleteGroupMessage; +@property (nonatomic, readonly) BOOL userCanDeleteGroupMessage; @property (nonatomic, readonly) BOOL hasBodyText; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index f6da3dd37..7d3fa49f0 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -203,8 +203,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (OWSPrimaryStorage *)primaryStorage { - OWSAssertDebug(SSKEnvironment.shared.primaryStorage); - return SSKEnvironment.shared.primaryStorage; } @@ -1171,25 +1169,25 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (void)deleteAction { - // Delete it optimistically [self.interaction remove]; if (self.isGroupThread) { - // If it's RSS then skip + // Skip if the thread is an RSS feed TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; if (groupThread.isRSSFeed) return; // Only allow deletion on incoming and outgoing messages OWSInteractionType interationType = self.interaction.interactionType; - if (interationType != OWSInteractionType_OutgoingMessage && interationType != OWSInteractionType_IncomingMessage) return; + if (interationType != OWSInteractionType_IncomingMessage && interationType != OWSInteractionType_OutgoingMessage) return; - // Check that we have the server id for the message + // Make sure it's a public chat message TSMessage *message = (TSMessage *)self.interaction; if (!message.isPublicChatMessage) return; // Delete the message - [[LKGroupChatAPI deleteMessageWithServerID:message.publicChatMessageID forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer isOurOwnMessage:interationType == OWSInteractionType_OutgoingMessage].catch(^(NSError *error) { - // If we fail then add the interaction back in + BOOL isSentByUser = (interationType == OWSInteractionType_OutgoingMessage); + [[LKGroupChatAPI deleteMessageWithID:message.publicChatMessageID forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer isSentByUser:isSentByUser].catch(^(NSError *error) { + // Roll back [self.interaction save]; }) retainUntilComplete]; } @@ -1235,11 +1233,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return NO; } -- (BOOL)canDeleteGroupMessage +- (BOOL)userCanDeleteGroupMessage { if (!self.isGroupThread) return false; - // Make sure it's a public chat and not an rss feed + // Ensure the thread is a public chat and not an RSS feed TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; if (groupThread.isRSSFeed) return false; @@ -1251,7 +1249,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) TSMessage *message = (TSMessage *)self.interaction; if (!message.isPublicChatMessage) return false; - // Don't allow deletion if we're not mods on incoming messages + // Only allow deletion on incoming messages if the user has moderation permission if (interationType == OWSInteractionType_IncomingMessage) { __block BOOL isModerator; [[self primaryStorage].dbReadWriteConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { diff --git a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift index ca612748c..059d8c91d 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift @@ -11,7 +11,7 @@ public final class LokiGroupChatAPI : NSObject { // MARK: Public Chat @objc public static let publicChatServer = "https://chat.lokinet.org" @objc public static let publicChatMessageType = "network.loki.messenger.publicChat" - @objc public static let publicChatServerID: UInt = 1 + @objc public static let publicChatServerID: UInt64 = 1 // MARK: Convenience private static var userDisplayName: String { @@ -28,7 +28,7 @@ public final class LokiGroupChatAPI : NSObject { // MARK: Error public enum Error : Swift.Error { - case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed, deletionParsingFailed, jsonParsingFailed + case parsingFailed, decryptionFailed } // MARK: Database @@ -87,7 +87,7 @@ public final class LokiGroupChatAPI : NSObject { return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let base64EncodedChallenge = json["cipherText64"] as? String, let base64EncodedServerPublicKey = json["serverPubKey64"] as? String, let challenge = Data(base64Encoded: base64EncodedChallenge), var serverPublicKey = Data(base64Encoded: base64EncodedServerPublicKey) else { - throw Error.tokenParsingFailed + throw Error.parsingFailed } // Discard the "05" prefix if needed if (serverPublicKey.count == 33) { @@ -97,7 +97,7 @@ public final class LokiGroupChatAPI : NSObject { // The challenge is prefixed by the 16 bit IV guard let tokenAsData = try? DiffieHellman.decrypt(challenge, publicKey: serverPublicKey, privateKey: userKeyPair.privateKey), let token = String(bytes: tokenAsData, encoding: .utf8) else { - throw Error.tokenDecryptionFailed + throw Error.decryptionFailed } return token } @@ -136,7 +136,7 @@ public final class LokiGroupChatAPI : NSObject { return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let rawMessages = json["data"] as? [JSON] else { print("[Loki] Couldn't parse messages for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") - throw Error.messageParsingFailed + throw Error.parsingFailed } return rawMessages.flatMap { message in guard let annotations = message["annotations"] as? [JSON], let annotation = annotations.first, let value = annotation["value"] as? JSON, @@ -167,7 +167,7 @@ public final class LokiGroupChatAPI : NSObject { guard let json = rawResponse as? JSON, let messageAsJSON = json["data"] as? JSON, let serverID = messageAsJSON["id"] as? UInt64, let body = messageAsJSON["text"] as? String, let dateAsString = messageAsJSON["created_at"] as? String, let date = dateFormatter.date(from: dateAsString) else { print("[Loki] Couldn't parse message for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") - throw Error.messageParsingFailed + throw Error.parsingFailed } let timestamp = UInt64(date.timeIntervalSince1970) * 1000 return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: userHexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp) @@ -194,7 +194,7 @@ public final class LokiGroupChatAPI : NSObject { return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let deletions = json["data"] as? [JSON] else { print("[Loki] Couldn't parse deleted messages for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") - throw Error.deletionParsingFailed + throw Error.parsingFailed } return deletions.flatMap { deletion in guard let serverID = deletion["id"] as? UInt64, let messageServerID = deletion["message_id"] as? UInt64 else { @@ -208,43 +208,30 @@ public final class LokiGroupChatAPI : NSObject { } } - public static func deleteMessageWithServerID(_ messageServerID: UInt, for group: UInt64, on server: String, isOurOwnMessage: Bool = true) -> Promise { + public static func deleteMessage(with messageID: UInt, for group: UInt64, on server: String, isSentByUser: Bool) -> Promise { return getAuthToken(for: server).then { token -> Promise in - let modTag = isOurOwnMessage ? "" : "[Mod]" - print("[Loki]\(modTag) Deleting message with server ID: \(messageServerID) for group chat with ID: \(group) on server: \(server).") - - let endpoint = isOurOwnMessage ? "\(server)/channels/\(group)/messages/\(messageServerID)" : "\(server)/loki/v1/moderation/message/\(messageServerID)" - let url = URL(string: endpoint)! + let isModerationRequest = !isSentByUser + print("[Loki] Deleting message with ID: \(messageID) for group chat with ID: \(group) on server: \(server) (isModerationRequest = \(isModerationRequest)).") + let urlAsString = isSentByUser ? "\(server)/channels/\(group)/messages/\(messageID)" : "\(server)/loki/v1/moderation/message/\(messageID)" + let url = URL(string: urlAsString)! let request = TSRequest(url: url, method: "DELETE", parameters: [:]) request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] - return TSNetworkManager.shared().makePromise(request: request).map { result -> Void in - print("[Loki]\(modTag) Deleted message \(messageServerID) on server \(server).") - }.recover { error in - // If we got 404 or 410 then message doesn't exist on the server - if let error = error as? NetworkManagerError, error.statusCode == 404 || error.statusCode == 410 { - print("[Loki]\(modTag) Message \(messageServerID) was already deleted on the server.") - return - } - - print("[Loki]\(modTag) Failed to delete message \(messageServerID) on server \(server).") - throw error + return TSNetworkManager.shared().makePromise(request: request).done { result -> Void in + print("[Loki] Deleted message with ID: \(messageID) on server: \(server).") } } - } - public static func isCurrentUserMod(on server: String) -> Promise { + public static func userHasModerationPermission(for group: UInt64, on server: String) -> Promise { return getAuthToken(for: server).then { token -> Promise in let url = URL(string: "\(server)/loki/v1/user_info")! let request = TSRequest(url: url) request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let data = json["data"] as? JSON else { - print("[Loki] Couldn't parse json for user info.") - throw Error.jsonParsingFailed + print("[Loki] Couldn't parse moderation permission for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") + throw Error.parsingFailed } - - // moderator_status is not set for users that are not mods return data["moderator_status"] as? Bool ?? false } } @@ -261,8 +248,8 @@ public final class LokiGroupChatAPI : NSObject { return AnyPromise.from(sendMessage(message, to: group, on: server)) } - @objc (deleteMessageWithServerID:forGroup:onServer:isOurOwnMessage:) - public static func objc_deleteMessageWithServerID(_ messageServerID: UInt, for group: UInt64, on server: String, ourMessage: Bool = true) -> AnyPromise { - return AnyPromise.from(deleteMessageWithServerID(messageServerID, for: group, on: server, isOurOwnMessage: ourMessage)) + @objc (deleteMessageWithID:forGroup:onServer:isSentByUser:) + public static func objc_deleteMessage(with messageID: UInt, for group: UInt64, on server: String, isSentByUser: Bool) -> AnyPromise { + return AnyPromise.from(deleteMessage(with: messageID, for: group, on: server, isSentByUser: isSentByUser)) } } diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h index ed6e781b3..528b70213 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h @@ -72,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)removePreKeyBundleForContact:(NSString *)pubKey transaction:(YapDatabaseReadWriteTransaction *)transaction; -# pragma mark - Last Hash Handling +# pragma mark - Last Hash /** Get the last message hash for the given service node. @@ -95,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setLastMessageHashForServiceNode:(NSString *)serviceNode hash:(NSString *)hash expiresAt:(u_int64_t)expiresAt transaction:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setLastMessageHash(forServiceNode:hash:expiresAt:transaction:)); -# pragma mark - Public chat +# pragma mark - Group Chat - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; - (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m index 42b870e06..fa89c1386 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m @@ -164,6 +164,8 @@ [transaction removeObjectForKey:serviceNode inCollection:LKLastMessageHashCollection]; } +# pragma mark - Group Chat + - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction { NSString *key = [NSString stringWithFormat:@"%@", @(serverID)]; [transaction setObject:messageID forKey:key inCollection:LKMessageIDCollection]; From 1e7949a029102dfb3d7100c45a10921d21cac79d Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 2 Sep 2019 13:44:18 +1000 Subject: [PATCH 13/14] Clean --- Signal/src/Loki/LokiGroupChatPoller.swift | 2 +- .../ConversationView/ConversationViewItem.m | 6 +++--- .../src/Messages/Interactions/TSMessage.h | 15 +++++---------- .../src/Messages/Interactions/TSMessage.m | 12 ++++++------ SignalServiceKit/src/Messages/OWSMessageManager.m | 2 +- SignalServiceKit/src/Messages/OWSMessageSender.m | 2 +- 6 files changed, 17 insertions(+), 22 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index ccf9ed5ce..225faffdf 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -71,7 +71,7 @@ public final class LokiGroupChatPoller : NSObject { let message = TSOutgoingMessage(outgoingMessageWithTimestamp: message.timestamp, in: groupThread, messageBody: message.body, attachmentIds: [], expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false, groupMetaMessage: .deliver, quotedMessage: nil, contactShare: nil, linkPreview: nil) storage.newDatabaseConnection().readWrite { transaction in message.update(withSentRecipient: group.server, wasSentByUD: false, transaction: transaction) - message.savePublicChatMessageID(messageServerID, with: transaction) + message.saveGroupChatMessageID(messageServerID, in: transaction) guard let messageID = message.uniqueId else { owsFailDebug("[Loki] Outgoing public chat message should have a unique id set") diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 7d3fa49f0..329d38dc3 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1182,11 +1182,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) // Make sure it's a public chat message TSMessage *message = (TSMessage *)self.interaction; - if (!message.isPublicChatMessage) return; + if (!message.isGroupChatMessage) return; // Delete the message BOOL isSentByUser = (interationType == OWSInteractionType_OutgoingMessage); - [[LKGroupChatAPI deleteMessageWithID:message.publicChatMessageID forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer isSentByUser:isSentByUser].catch(^(NSError *error) { + [[LKGroupChatAPI deleteMessageWithID:message.isGroupChatMessage forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer isSentByUser:isSentByUser].catch(^(NSError *error) { // Roll back [self.interaction save]; }) retainUntilComplete]; @@ -1247,7 +1247,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) // Make sure it's a public chat message TSMessage *message = (TSMessage *)self.interaction; - if (!message.isPublicChatMessage) return false; + if (!message.isGroupChatMessage) return false; // Only allow deletion on incoming messages if the user has moderation permission if (interationType == OWSInteractionType_IncomingMessage) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index f00c4a159..04e0d50a0 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -39,20 +39,15 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) { @property (nonatomic, readonly, nullable) OWSContact *contactShare; @property (nonatomic, readonly, nullable) OWSLinkPreview *linkPreview; // Loki friend request handling -// ======== @property (nonatomic) LKMessageFriendRequestStatus friendRequestStatus; @property (nonatomic, readonly) NSString *friendRequestStatusDescription; @property (nonatomic) uint64_t friendRequestExpiresAt; @property (nonatomic, readonly) BOOL isFriendRequest; @property (nonatomic, readonly) BOOL hasFriendRequestStatusMessage; @property (nonatomic) BOOL isP2P; -// ======== - -// Public Chat -// ======== -@property (nonatomic) uint64_t publicChatMessageID; -@property (nonatomic, readonly) BOOL isPublicChatMessage; -// ======== +// Group chat +@property (nonatomic) uint64_t groupChatMessageID; +@property (nonatomic, readonly) BOOL isGroupChatMessage; - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; @@ -98,9 +93,9 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) { - (void)saveFriendRequestStatus:(LKMessageFriendRequestStatus)friendRequestStatus withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - (void)saveFriendRequestExpiresAt:(u_int64_t)expiresAt withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; -#pragma mark - Public Chat +#pragma mark - Group chat -- (void)savePublicChatMessageID:(uint64_t)serverMessageId withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; +- (void)saveGroupChatMessageID:(uint64_t)serverMessageID in:(YapDatabaseReadWriteTransaction *_Nullable)transaction; @end diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index f49e63098..85336cbe6 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -84,7 +84,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _linkPreview = linkPreview; _friendRequestStatus = LKMessageFriendRequestStatusNone; _friendRequestExpiresAt = 0; - _publicChatMessageID = -1; + _groupChatMessageID = -1; return self; } @@ -491,14 +491,14 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return self.isFriendRequest && self.friendRequestStatus != LKMessageFriendRequestStatusSendingOrFailed; } -#pragma mark - Public chat handling +#pragma mark - Group chat -- (BOOL) isPublicChatMessage { - return self.publicChatMessageID > 0; +- (BOOL) isGroupChatMessage { + return self.groupChatMessageID > 0; } -- (void)savePublicChatMessageID:(uint64_t)serverMessageId withTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction { - self.publicChatMessageID = serverMessageId; +- (void)saveGroupChatMessageID:(uint64_t)serverMessageID in:(YapDatabaseReadWriteTransaction *_Nullable)transaction { + self.groupChatMessageID = serverMessageID; if (transaction == nil) { [self save]; [self.dbReadWriteConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{}]; diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 61289b259..fe0c5af2b 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1377,7 +1377,7 @@ NS_ASSUME_NONNULL_BEGIN wasReceivedByUD:wasReceivedByUD]; if (envelope.isPtpMessage) { incomingMessage.isP2P = YES; } - if (dataMessage.publicChatInfo && dataMessage.publicChatInfo.hasServerID) { incomingMessage.publicChatMessageID = dataMessage.publicChatInfo.serverID; } + if (dataMessage.publicChatInfo && dataMessage.publicChatInfo.hasServerID) { incomingMessage.groupChatMessageID = dataMessage.publicChatInfo.serverID; } NSArray *attachmentPointers = [TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index a45cf6c6b..875e0b743 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1117,7 +1117,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [[LKGroupChatAPI sendMessage:groupMessage toGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer] .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [message savePublicChatMessageID:groupMessage.serverID withTransaction:transaction]; + [message saveGroupChatMessageID:groupMessage.serverID in:transaction]; [OWSPrimaryStorage.sharedManager setIDForMessageWithServerID:groupMessage.serverID to:message.uniqueId in:transaction]; }]; [self messageSendDidSucceed:messageSend deviceMessages:deviceMessages wasSentByUD:false wasSentByWebsocket:false]; From 4b8011ad7e7cc81ca0be98c05b4dcf7fece9e6e5 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 2 Sep 2019 14:06:20 +1000 Subject: [PATCH 14/14] Fix dependence on self --- Signal/src/Loki/LokiGroupChatPoller.swift | 133 +++++++++--------- .../src/Messages/Interactions/TSMessage.h | 2 +- .../src/Messages/Interactions/TSMessage.m | 2 +- 3 files changed, 65 insertions(+), 72 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 225faffdf..ace8f9122 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -6,14 +6,14 @@ public final class LokiGroupChatPoller : NSObject { private var pollForDeletedMessagesTimer: Timer? = nil private var pollForModerationPermissionTimer: Timer? = nil private var hasStarted = false + private let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + // MARK: Settings private let pollForNewMessagesInterval: TimeInterval = 4 private let pollForDeletedMessagesInterval: TimeInterval = 20 private let pollForModerationPermissionInterval: TimeInterval = 10 * 60 - private let storage = OWSPrimaryStorage.shared() - private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey - + // MARK: Lifecycle @objc(initForGroup:) public init(for group: LokiGroupChat) { self.group = group @@ -36,82 +36,74 @@ public final class LokiGroupChatPoller : NSObject { hasStarted = false } + // MARK: Polling private func pollForNewMessages() { + // Prepare let group = self.group - let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done { [weak self] messages in - guard let self = self else { return } + let userHexEncodedPublicKey = self.userHexEncodedPublicKey + // Processing logic for incoming messages + func processIncomingMessage(_ message: LokiGroupMessage) { + let senderHexEncodedPublicKey = message.hexEncodedPublicKey + let endIndex = senderHexEncodedPublicKey.endIndex + let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) + let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. 0;