Merge branch 'dev' into threading

pull/214/head
nielsandriesse 4 years ago
commit 6cc28426d6

@ -320,7 +320,7 @@ SPEC CHECKSUMS:
SessionServiceKit: c792d64fc86aaa0f7325beaa36e1a9211a887359
SQLCipher: e434ed542b24f38ea7b36468a13f9765e1b5c072
SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9
Starscream: 8aaf1a7feb805c816d0e7d3190ef23856f6665b9
Starscream: ef3ece99d765eeccb67de105bfa143f929026cf5
SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2
YapDatabase: b418a4baa6906e8028748938f9159807fd039af4
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54

@ -1 +1 @@
Subproject commit 827a67a609b8a4166220440c911796f1a18614ee
Subproject commit 6077f71ac9595fa165558a398d296a6c97b5b143

@ -1394,12 +1394,15 @@ static NSTimeInterval launchStartedAt;
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
if (userHexEncodedPublicKey == nil) { return; }
self.lokiPoller = [[LKPoller alloc] initOnMessagesReceived:^(NSArray<SSKProtoEnvelope *> *messages) {
if (messages.count != 0) {
[LKLogger print:@"[Loki] Received new messages."];
}
for (SSKProtoEnvelope *message in messages) {
NSData *data = [message serializedDataAndReturnError:nil];
if (data != nil) {
[SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:data];
} else {
NSLog(@"[Loki] Failed to deserialize envelope.");
[LKLogger print:@"[Loki] Failed to deserialize envelope."];
}
}
}];

@ -134,26 +134,29 @@ final class ConversationCell : UITableViewCell {
// MARK: Updating
private func update() {
AssertIsOnMainThread()
MentionsManager.populateUserPublicKeyCacheIfNeeded(for: threadViewModel.threadRecord.uniqueId!) // FIXME: This is a terrible place to do this
unreadMessagesIndicatorView.alpha = threadViewModel.hasUnreadMessages ? 1 : 0.0001 // Setting the alpha to exactly 0 causes an issue on iOS 12
profilePictureView.openGroupProfilePicture = nil
if threadViewModel.isGroupThread {
if threadViewModel.name == "Session Public Chat" {
if threadViewModel.name == "Loki Public Chat"
|| threadViewModel.name == "Session Public Chat" { // Override the profile picture for the Loki Public Chat and the Session Public Chat
profilePictureView.hexEncodedPublicKey = ""
profilePictureView.isRSSFeed = true
} else {
} else if let openGroupProfilePicture = (threadViewModel.threadRecord as! TSGroupThread).groupModel.groupImage { // An open group with a profile picture
profilePictureView.openGroupProfilePicture = openGroupProfilePicture
} else if (threadViewModel.threadRecord as! TSGroupThread).groupModel.groupType == .openGroup
|| (threadViewModel.threadRecord as! TSGroupThread).groupModel.groupType == .rssFeed { // An open group without a profile picture or an RSS feed
profilePictureView.hexEncodedPublicKey = ""
profilePictureView.isRSSFeed = true
} else { // A closed group
var users = MentionsManager.userPublicKeyCache[threadViewModel.threadRecord.uniqueId!] ?? []
users.remove(getUserHexEncodedPublicKey())
let randomUsers = users.sorted().prefix(2) // Sort to provide a level of stability
if !randomUsers.isEmpty {
profilePictureView.hexEncodedPublicKey = randomUsers[0]
profilePictureView.additionalHexEncodedPublicKey = randomUsers.count >= 2 ? randomUsers[1] : ""
} else {
profilePictureView.hexEncodedPublicKey = ""
profilePictureView.additionalHexEncodedPublicKey = ""
}
profilePictureView.isRSSFeed = (threadViewModel.threadRecord as? TSGroupThread)?.isRSSFeed ?? false
profilePictureView.hexEncodedPublicKey = randomUsers.count >= 1 ? randomUsers[0] : ""
profilePictureView.additionalHexEncodedPublicKey = randomUsers.count >= 2 ? randomUsers[1] : ""
}
} else {
} else { // A one-on-one chat
profilePictureView.hexEncodedPublicKey = threadViewModel.contactIdentifier!
profilePictureView.additionalHexEncodedPublicKey = nil
profilePictureView.isRSSFeed = false

@ -133,8 +133,11 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
isJoining = true
let channelID: UInt64 = 1
let urlAsString = url.absoluteString
let displayName = OWSProfileManager.shared().localProfileName()
// TODO: Profile picture & profile key
let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey()
let profileManager = OWSProfileManager.shared()
let displayName = profileManager.profileNameForRecipient(withID: userPublicKey)
let profilePictureURL = profileManager.profilePictureURL()
let profileKey = profileManager.localProfileKey().keyData
try! Storage.writeSync { transaction in
transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: LokiPublicChatAPI.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: LokiPublicChatAPI.lastDeletionServerIDCollection)
@ -143,6 +146,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
.done(on: .main) { [weak self] _ in
let _ = LokiPublicChatAPI.getMessages(for: channelID, on: urlAsString)
let _ = LokiPublicChatAPI.setDisplayName(to: displayName, on: urlAsString)
let _ = LokiPublicChatAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: urlAsString)
let _ = LokiPublicChatAPI.join(channelID, on: urlAsString)
let syncManager = SSKEnvironment.shared.syncManager
let _ = syncManager.syncAllOpenGroups()

@ -115,7 +115,8 @@ class ConversationViewItemActions: NSObject {
actions.append(deleteAction)
}
if isGroup && conversationViewItem.interaction.thread.name() == "Session Public Chat" {
if isGroup && conversationViewItem.interaction.thread.name() == "Loki Public Chat"
|| conversationViewItem.interaction.thread.name() == "Session Public Chat" {
let reportAction = MessageActionBuilder.report(conversationViewItem, delegate: delegate)
actions.append(reportAction)
}
@ -159,7 +160,8 @@ class ConversationViewItemActions: NSObject {
actions.append(deleteAction)
}
if isGroup && conversationViewItem.interaction.thread.name() == "Session Public Chat" {
if isGroup && conversationViewItem.interaction.thread.name() == "Loki Public Chat"
|| conversationViewItem.interaction.thread.name() == "Session Public Chat" {
let reportAction = MessageActionBuilder.report(conversationViewItem, delegate: delegate)
actions.append(reportAction)
}
@ -192,7 +194,8 @@ class ConversationViewItemActions: NSObject {
actions.append(deleteAction)
}
if isGroup && conversationViewItem.interaction.thread.name() == "Session Public Chat" {
if isGroup && conversationViewItem.interaction.thread.name() == "Loki Public Chat"
|| conversationViewItem.interaction.thread.name() == "Session Public Chat" {
let reportAction = MessageActionBuilder.report(conversationViewItem, delegate: delegate)
actions.append(reportAction)
}

@ -957,8 +957,15 @@ const CGFloat kIconViewLength = 24;
[stackView setLayoutMarginsRelativeArrangement:YES];
if (self.isGroupThread) {
profilePictureView.hexEncodedPublicKey = @"";
profilePictureView.isRSSFeed = true; // For now just always show the Session logo
TSGroupThread* groupThread = (TSGroupThread *)self.thread;
if (groupThread.isPublicChat && groupThread.groupModel.groupImage != nil
&& ![groupThread.groupModel.groupName isEqual:@"Loki Public Chat"] && ![groupThread.groupModel.groupName isEqual:@"Session Public Chat"]) {
profilePictureView.openGroupProfilePicture = groupThread.groupModel.groupImage;
profilePictureView.isRSSFeed = false;
} else {
profilePictureView.hexEncodedPublicKey = @"";
profilePictureView.isRSSFeed = true; // For now just always show the Session logo
}
} else {
profilePictureView.hexEncodedPublicKey = self.thread.contactIdentifier;

@ -7,6 +7,7 @@ public final class ProfilePictureView : UIView {
@objc public var isRSSFeed = false
@objc public var hexEncodedPublicKey: String!
@objc public var additionalHexEncodedPublicKey: String?
@objc public var openGroupProfilePicture: UIImage?
// MARK: Components
private lazy var imageView = getImageView()
@ -43,6 +44,7 @@ public final class ProfilePictureView : UIView {
// MARK: Updating
@objc public func update() {
AssertIsOnMainThread()
func getProfilePicture(of size: CGFloat, for hexEncodedPublicKey: String) -> UIImage? {
guard !hexEncodedPublicKey.isEmpty else { return nil }
return OWSProfileManager.shared().profileAvatar(forRecipientId: hexEncodedPublicKey) ?? Identicon.generateIcon(string: hexEncodedPublicKey, size: size)
@ -61,8 +63,8 @@ public final class ProfilePictureView : UIView {
additionalImageView.isHidden = true
additionalImageView.image = nil
}
guard hexEncodedPublicKey != nil else { return } // Can happen in rare cases
imageView.image = isRSSFeed ? nil : getProfilePicture(of: size, for: hexEncodedPublicKey)
guard hexEncodedPublicKey != nil || openGroupProfilePicture != nil else { return }
imageView.image = isRSSFeed ? nil : (openGroupProfilePicture ?? getProfilePicture(of: size, for: hexEncodedPublicKey))
imageView.backgroundColor = isRSSFeed ? UIColor(rgbHex: 0x353535) : UIColor(rgbHex: 0xD8D8D8) // UIColor(rgbHex: 0xD8D8D8) = Colors.unimportant
imageView.layer.cornerRadius = size / 2
imageView.contentMode = isRSSFeed ? .center : .scaleAspectFit

@ -239,7 +239,8 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
// Ensure that the success and failure blocks are called on the main thread.
void (^failureBlock)(NSError *) = ^(NSError *error) {
OWSLogError(@"Updating service with profile failed.");
/*
// We use a "self-only" contact sync to indicate to desktop
// that we've changed our profile and that it should do a
// profile fetch for "self".
@ -249,6 +250,10 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
if (requiresSync) {
[[self.syncManager syncLocalContact] retainUntilComplete];
}
*/
if (requiresSync) {
[LKSyncMessagesProtocol syncProfile];
}
dispatch_async(dispatch_get_main_queue(), ^{
failureBlockParameter(error);
@ -256,13 +261,18 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
};
void (^successBlock)(void) = ^{
OWSLogInfo(@"Successfully updated service with profile.");
/*
// We use a "self-only" contact sync to indicate to desktop
// that we've changed our profile and that it should do a
// profile fetch for "self".
if (requiresSync) {
[[self.syncManager syncLocalContact] retainUntilComplete];
}
*/
if (requiresSync) {
[LKSyncMessagesProtocol syncProfile];
}
dispatch_async(dispatch_get_main_queue(), ^{
successBlockParameter();

@ -19,7 +19,8 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
// MARK: Convenience
private static var userDisplayName: String {
return SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: getUserHexEncodedPublicKey()) ?? "Anonymous"
let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey()
return SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: userPublicKey) ?? "Anonymous"
}
// MARK: Database
@ -329,6 +330,51 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
}
static func updateProfileIfNeeded(for channel: UInt64, on server: String, from info: LokiPublicChatInfo) {
let storage = OWSPrimaryStorage.shared()
let publicChatID = "\(server).\(channel)"
try! Storage.writeSync { transaction in
// Update user count
storage.setUserCount(info.memberCount, forPublicChatWithID: publicChatID, in: transaction)
let groupThread = TSGroupThread.getOrCreateThread(withGroupId: publicChatID.data(using: .utf8)!, groupType: .openGroup, transaction: transaction)
// Update display name if needed
let groupModel = groupThread.groupModel
if groupModel.groupName != info.displayName {
let newGroupModel = TSGroupModel(title: info.displayName, memberIds: groupModel.groupMemberIds, image: groupModel.groupImage, groupId: groupModel.groupId, groupType: groupModel.groupType, adminIds: groupModel.groupAdminIds)
groupThread.groupModel = newGroupModel
groupThread.save(with: transaction)
}
// Download and update profile picture if needed
let oldProfilePictureURL = storage.getProfilePictureURL(forPublicChatWithID: publicChatID, in: transaction)
if oldProfilePictureURL != info.profilePictureURL || groupModel.groupImage == nil {
storage.setProfilePictureURL(info.profilePictureURL, forPublicChatWithID: publicChatID, in: transaction)
if let avatarURL = info.profilePictureURL {
let configuration = URLSessionConfiguration.default
let manager = AFURLSessionManager.init(sessionConfiguration: configuration)
let url = URL(string: "\(server)\(avatarURL)")!
let request = URLRequest(url: url)
let task = manager.downloadTask(with: request, progress: nil,
destination: { (targetPath: URL, response: URLResponse) -> URL in
let tempFilePath = URL(fileURLWithPath: OWSTemporaryDirectoryAccessibleAfterFirstAuth()).appendingPathComponent(UUID().uuidString)
return tempFilePath
},
completionHandler: { (response: URLResponse, filePath: URL?, error: Error?) in
if let error = error {
print("[Loki] Couldn't download profile picture for public chat channel with ID: \(channel) on server: \(server).")
return
}
if let filePath = filePath, let avatarData = try? Data.init(contentsOf: filePath) {
let attachmentStream = TSAttachmentStream(contentType: OWSMimeTypeImageJpeg, byteCount: UInt32(avatarData.count), sourceFilename: nil, caption: nil, albumMessageId: nil)
try! attachmentStream.write(avatarData)
groupThread.updateAvatar(with: attachmentStream)
}
})
task.resume()
}
}
}
}
// MARK: Joining & Leaving
@objc(getInfoForChannelWithID:onServer:)
public static func objc_getInfo(for channel: UInt64, on server: String) -> AnyPromise {
@ -348,6 +394,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let annotation = annotations.first,
let info = annotation["value"] as? JSON,
let displayName = info["name"] as? String,
let profilePictureURL = info["avatar"] as? String,
let countInfo = data["counts"] as? JSON,
let memberCount = countInfo["subscribers"] as? Int else {
print("[Loki] Couldn't parse info for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).")
@ -357,8 +404,9 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
try! Storage.writeSync { transaction in
storage.setUserCount(memberCount, forPublicChatWithID: "\(server).\(channel)", in: transaction)
}
// TODO: Use this to update open group names as needed
return LokiPublicChatInfo(displayName: displayName, memberCount: memberCount)
let publicChatInfo = LokiPublicChatInfo(displayName: displayName, profilePictureURL: profilePictureURL, memberCount: memberCount)
updateProfileIfNeeded(for: channel, on: server, from: publicChatInfo)
return publicChatInfo
}
}.handlingInvalidAuthTokenIfNeeded(for: server)
}

@ -1,5 +1,6 @@
public struct LokiPublicChatInfo {
public let displayName: String
public let profilePictureURL: String?
public let memberCount: Int
}

@ -150,6 +150,9 @@ public final class LokiPublicChatPoller : NSObject {
if !wasSentByCurrentUser {
content.setDataMessage(try! dataMessage.build())
} else {
// The line below is necessary to make it so that when a user sends a message in an open group and then
// deletes and re-joins the open group without closing the app in between, the message isn't ignored.
SyncMessagesProtocol.dropFromSyncMessageTimestampCache(message.timestamp, for: senderHexEncodedPublicKey)
let syncMessageSentBuilder = SSKProtoSyncMessageSent.builder()
syncMessageSentBuilder.setMessage(try! dataMessage.build())
syncMessageSentBuilder.setDestination(userHexEncodedPublicKey)

@ -139,4 +139,12 @@ public extension OWSPrimaryStorage {
public func setUserCount(_ userCount: Int, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(userCount, forKey: publicChatID, inCollection: Storage.openGroupUserCountCollection)
}
public func getProfilePictureURL(forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadTransaction) -> String? {
return transaction.object(forKey: publicChatID, inCollection: Storage.openGroupProfilePictureURLCollection) as? String
}
public func setProfilePictureURL(_ profilePictureURL: String?, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(profilePictureURL, forKey: publicChatID, inCollection: Storage.openGroupProfilePictureURLCollection)
}
}

@ -13,6 +13,7 @@
@objc public static let onionRequestPathCollection = "LokiOnionRequestPathCollection"
@objc public static let openGroupCollection = "LokiPublicChatCollection"
@objc public static let openGroupProfilePictureURLCollection = "LokiPublicChatAvatarURLCollection"
@objc public static let openGroupUserCountCollection = "LokiPublicChatUserCountCollection"
@objc public static let sessionRequestTimestampCollection = "LokiSessionRequestTimestampCollection"
@objc public static let snodePoolCollection = "LokiSnodePoolCollection"

@ -17,7 +17,7 @@
#pragma mark Building
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
SSKProtoContentBuilder *contentBuilder = SSKProtoContent.builder;
SSKProtoContentBuilder *contentBuilder = [super prepareCustomContentBuilder:recipient];
// Attach the pre key bundle for the contact in question
PreKeyBundle *preKeyBundle = [OWSPrimaryStorage.sharedManager generatePreKeyBundleForContact:recipient.recipientId];
SSKProtoPrekeyBundleMessageBuilder *preKeyBundleMessageBuilder = [SSKProtoPrekeyBundleMessage builderFromPreKeyBundle:preKeyBundle];

@ -27,7 +27,7 @@
#pragma mark Building
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
SSKProtoContentBuilder *contentBuilder = SSKProtoContent.builder;
SSKProtoContentBuilder *contentBuilder = [super prepareCustomContentBuilder:recipient];
SSKProtoLokiAddressMessageBuilder *addressMessageBuilder = SSKProtoLokiAddressMessage.builder;
[addressMessageBuilder setPtpAddress:self.address];
uint32_t portAsUInt32 = self.port;

@ -35,7 +35,7 @@
#pragma mark Building
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
SSKProtoContentBuilder *contentBuilder = SSKProtoContent.builder;
SSKProtoContentBuilder *contentBuilder = [super prepareCustomContentBuilder:recipient];
NSError *error;
if (self.kind == LKDeviceLinkMessageKindRequest) {
// The slave device attaches a pre key bundle with the request it sends, so that a

@ -9,9 +9,6 @@ import PromiseKit
// Document the expected cases for everything.
// Express those cases in tests.
// FIXME: We're manually attaching the sender certificate and UD recipient access to message sends in a lot of places. It'd be great
// to clean this up and just do it in one spot.
@objc(LKMultiDeviceProtocol)
public final class MultiDeviceProtocol : NSObject {
@ -45,15 +42,9 @@ public final class MultiDeviceProtocol : NSObject {
storage.dbReadConnection.read { transaction in
recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: destination.hexEncodedPublicKey, transaction: transaction)
}
let udManager = SSKEnvironment.shared.udManager
let senderCertificate = udManager.getSenderCertificate()
var recipientUDAccess: OWSUDAccess?
if let senderCertificate = senderCertificate {
recipientUDAccess = udManager.udAccess(forRecipientId: destination.hexEncodedPublicKey, requireSyncAccess: true) // Starts a new write transaction internally
}
// TODO: Why is it okay that the thread doesn't get changed?
// TODO: Why is it okay that the thread, sender certificate, etc. don't get changed?
return OWSMessageSend(message: messageSend.message, thread: messageSend.thread, recipient: recipient,
senderCertificate: senderCertificate, udAccess: recipientUDAccess, localNumber: messageSend.localNumber, success: {
senderCertificate: messageSend.senderCertificate, udAccess: messageSend.udAccess, localNumber: messageSend.localNumber, success: {
seal.fulfill(())
}, failure: { error in
seal.reject(error)
@ -73,6 +64,7 @@ public final class MultiDeviceProtocol : NSObject {
}
}
return threadPromise.then2 { thread -> Promise<Void> in
return threadPromise.then(on: OWSDispatch.sendingQueue()) { thread -> Promise<Void> in
let message = messageSend.message
let messageSender = SSKEnvironment.shared.messageSender
let (promise, seal) = Promise<Void>.pending()
@ -81,9 +73,7 @@ public final class MultiDeviceProtocol : NSObject {
&& message.shouldBeSaved() // shouldBeSaved indicates it isn't a transient message
if !shouldSendAutoGeneratedFR {
let messageSendCopy = copy(messageSend, for: destination, with: seal)
OWSDispatch.sendingQueue().async {
messageSender.sendMessage(messageSendCopy)
}
messageSender.sendMessage(messageSendCopy)
} else {
Storage.write { transaction in
getAutoGeneratedMultiDeviceFRMessageSend(for: destination.hexEncodedPublicKey, in: transaction, seal: seal)
@ -141,17 +131,7 @@ public final class MultiDeviceProtocol : NSObject {
}.catch2 { error in
// Proceed even if updating the recipient's device links failed, so that message sending
// is independent of whether the file server is online
let udManager = SSKEnvironment.shared.udManager
let senderCertificate = udManager.getSenderCertificate()
var recipientUDAccess: OWSUDAccess?
if let senderCertificate = senderCertificate {
recipientUDAccess = udManager.udAccess(forRecipientId: recipientID, requireSyncAccess: true) // Starts a new write transaction internally
}
messageSend.senderCertificate = senderCertificate
messageSend.udAccess = recipientUDAccess
OWSDispatch.sendingQueue().async {
messageSender.sendMessage(messageSend)
}
messageSender.sendMessage(messageSend)
}
}
@ -165,9 +145,7 @@ public final class MultiDeviceProtocol : NSObject {
@objc(getAutoGeneratedMultiDeviceFRMessageForHexEncodedPublicKey:in:)
public static func getAutoGeneratedMultiDeviceFRMessage(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> FriendRequestMessage {
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let result = FriendRequestMessage(timestamp: NSDate.ows_millisecondTimeStamp(), thread: thread, body: "Please accept to enable messages to be synced across devices")
result.skipSave = true // TODO: Why is this necessary again?
return result
return FriendRequestMessage(timestamp: NSDate.ows_millisecondTimeStamp(), thread: thread, body: "Please accept to enable messages to be synced across devices")
}
/// See [Auto-Generated Friend Requests](https://github.com/loki-project/session-protocol-docs/wiki/Auto-Generated-Friend-Requests) for more information.

@ -23,6 +23,22 @@ public final class SyncMessagesProtocol : NSObject {
// FIXME: We added this check to avoid a crash, but we should really figure out why that crash was happening in the first place
return !UserDefaults.standard[.hasLaunchedOnce]
}
@objc(syncProfile)
public static func syncProfile() {
try! Storage.writeSync { transaction in
let userPublicKey = getUserHexEncodedPublicKey()
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userPublicKey, in: transaction)
for publicKey in linkedDevices {
guard publicKey != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction)
let syncMessage = OWSOutgoingSyncMessage.init(in: thread, messageBody: "", attachmentId: nil)
syncMessage.save(with: transaction)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: syncMessage, transaction: transaction)
}
}
}
@objc(syncContactWithHexEncodedPublicKey:in:)
public static func syncContact(_ hexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> AnyPromise {
@ -97,6 +113,12 @@ public final class SyncMessagesProtocol : NSObject {
return LokiDatabaseUtilities.isUserLinkedDevice(hexEncodedPublicKey, transaction: transaction)
}
public static func dropFromSyncMessageTimestampCache(_ timestamp: UInt64, for hexEncodedPublicKey: String) {
var timestamps: Set<UInt64> = syncMessageTimestamps[hexEncodedPublicKey] ?? []
if timestamps.contains(timestamp) { timestamps.remove(timestamp) }
syncMessageTimestamps[hexEncodedPublicKey] = timestamps
}
// TODO: We should probably look at why sync messages are being duplicated rather than doing this
@objc(isDuplicateSyncMessage:fromHexEncodedPublicKey:)
public static func isDuplicateSyncMessage(_ protoContent: SSKProtoContent, from hexEncodedPublicKey: String) -> Bool {
@ -166,15 +188,17 @@ public final class SyncMessagesProtocol : NSObject {
let parser = ContactParser(data: data)
let hexEncodedPublicKeys = parser.parseHexEncodedPublicKeys()
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userHexEncodedPublicKey, in: transaction)
// Try to establish sessions
for hexEncodedPublicKey in hexEncodedPublicKeys {
guard hexEncodedPublicKey != userHexEncodedPublicKey else { continue } // Skip self
guard !linkedDevices.contains(hexEncodedPublicKey) else { continue } // Skip self and linked devices
// We don't update the friend request status; that's done in OWSMessageSender.sendMessage(_:)
let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)
switch friendRequestStatus {
case .none, .requestExpired:
// We need to send the FR message to all of the user's devices as the contact sync message excludes slave devices
let autoGeneratedFRMessage = MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessage(for: hexEncodedPublicKey, in: transaction)
autoGeneratedFRMessage.save(with: transaction)
// Use the message sender job queue for this to ensure that these messages get sent
// AFTER session requests (it's asssumed that the master device first syncs closed
// groups first and contacts after that).
@ -228,6 +252,9 @@ public final class SyncMessagesProtocol : NSObject {
for openGroup in groups {
let openGroupManager = LokiPublicChatManager.shared
guard openGroupManager.getChat(server: openGroup.url, channel: openGroup.channel) == nil else { return }
let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey()
let displayName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: userPublicKey)
LokiPublicChatAPI.setDisplayName(to: displayName, on: openGroup.url)
openGroupManager.addChat(server: openGroup.url, channel: openGroup.channel)
}
}

@ -51,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
SSKProtoContentBuilder *builder = SSKProtoContent.builder;
SSKProtoContentBuilder *builder = [super prepareCustomContentBuilder:recipient];
PreKeyBundle *bundle = [OWSPrimaryStorage.sharedManager generatePreKeyBundleForContact:recipient.recipientId];
SSKProtoPrekeyBundleMessageBuilder *preKeyBuilder = [SSKProtoPrekeyBundleMessage builderFromPreKeyBundle:bundle];

@ -1125,24 +1125,27 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
}
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
return SSKProtoContent.builder;
}
- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient
{
NSError *error;
SSKProtoDataMessage *_Nullable dataMessage = [self buildDataMessage:recipient.recipientId];
if (error || !dataMessage) {
OWSFailDebug(@"could not build protobuf: %@", error);
if (!dataMessage) {
OWSFailDebug(@"Couldn't build protobuf.");
return nil;
}
SSKProtoContentBuilder *contentBuilder = [self prepareCustomContentBuilder:recipient];
SSKProtoContentBuilder *contentBuilder = SSKProtoContent.builder;
[contentBuilder setDataMessage:dataMessage];
return contentBuilder;
}
- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient
{
SSKProtoContentBuilder *contentBuilder = [self prepareCustomContentBuilder:recipient];
NSError *error;
NSData *_Nullable contentData = [contentBuilder buildSerializedDataAndReturnError:&error];
if (error || !contentData) {
OWSFailDebug(@"could not serialize protobuf: %@", error);
OWSFailDebug(@"Couldn't serialize protobuf due to error: %@.", error);
return nil;
}

@ -928,7 +928,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
{
OWSAssertDebug(messageSend);
OWSAssertDebug(messageSend.thread || [messageSend.message isKindOfClass:[OWSOutgoingSyncMessage class]]);
OWSAssertDebug(messageSend.isUDSend);
TSOutgoingMessage *message = messageSend.message;
SignalRecipient *recipient = messageSend.recipient;

Loading…
Cancel
Save