Clean up ahead of protocol changes

pull/228/head
nielsandriesse 4 years ago
parent e1f6da05e3
commit 2d9982684b

@ -1 +1 @@
Subproject commit 158e066d79806ef0e466370da5c303dedb90c654
Subproject commit cdc2638b2adeedffe8cd936f023a737bfd8a65d5

@ -83,7 +83,7 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab
private extension MentionCandidateSelectionView {
final class Cell : UITableViewCell {
var mentionCandidate = Mention(hexEncodedPublicKey: "", displayName: "") { didSet { update() } }
var mentionCandidate = Mention(publicKey: "", displayName: "") { didSet { update() } }
var publicChatServer: String?
var publicChatChannel: UInt64?
@ -161,10 +161,10 @@ private extension MentionCandidateSelectionView {
// MARK: Updating
private func update() {
displayNameLabel.text = mentionCandidate.displayName
profilePictureView.hexEncodedPublicKey = mentionCandidate.hexEncodedPublicKey
profilePictureView.hexEncodedPublicKey = mentionCandidate.publicKey
profilePictureView.update()
if let server = publicChatServer, let channel = publicChatChannel {
let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: channel, on: server)
let isUserModerator = PublicChatAPI.isUserModerator(mentionCandidate.publicKey, for: channel, on: server)
moderatorIconImageView.isHidden = !isUserModerator
} else {
moderatorIconImageView.isHidden = true

@ -10,7 +10,7 @@ public final class MentionUtilities : NSObject {
@objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, threadID: String, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString {
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
var publicChat: LokiPublicChat?
var publicChat: PublicChat?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
}

@ -159,7 +159,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
qrCodeImageViewContainer.isHidden = true
titleLabel.text = NSLocalizedString("Linking Request Received", comment: "")
subtitleLabel.text = NSLocalizedString("Please check that the words below match those shown on your other device", comment: "")
let hexEncodedPublicKey = deviceLink.slave.hexEncodedPublicKey.removing05PrefixIfNeeded()
let hexEncodedPublicKey = deviceLink.slave.publicKey.removing05PrefixIfNeeded()
mnemonicLabel.text = Mnemonic.hash(hexEncodedString: hexEncodedPublicKey)
mnemonicLabel.isHidden = false
authorizeButton.isHidden = false
@ -180,12 +180,12 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
DeviceLinkingSession.current!.markLinkingRequestAsProcessed()
DeviceLinkingSession.current!.stopListeningForLinkingRequests()
let linkingAuthorizationMessage = DeviceLinkingUtilities.getLinkingAuthorizationMessage(for: deviceLink)
let master = DeviceLink.Device(hexEncodedPublicKey: deviceLink.master.hexEncodedPublicKey, signature: linkingAuthorizationMessage.masterSignature)
let master = DeviceLink.Device(publicKey: deviceLink.master.publicKey, signature: linkingAuthorizationMessage.masterSignature)
let signedDeviceLink = DeviceLink(between: master, and: deviceLink.slave)
FileServerAPI.addDeviceLink(signedDeviceLink).done(on: DispatchQueue.main) { [weak self] in
SSKEnvironment.shared.messageSender.send(linkingAuthorizationMessage, success: {
let storage = OWSPrimaryStorage.shared()
let slaveHexEncodedPublicKey = deviceLink.slave.hexEncodedPublicKey
let slaveHexEncodedPublicKey = deviceLink.slave.publicKey
try! Storage.writeSync { transaction in
let thread = TSContactThread.getOrCreateThread(withContactId: slaveHexEncodedPublicKey, transaction: transaction)
thread.save(with: transaction)
@ -252,7 +252,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
delegate?.handleDeviceLinkingModalDismissed() // Only relevant in slave mode
if let deviceLink = deviceLink {
try! Storage.writeSync { transaction in
OWSPrimaryStorage.shared().removePreKeyBundle(forContact: deviceLink.slave.hexEncodedPublicKey, transaction: transaction)
OWSPrimaryStorage.shared().removePreKeyBundle(forContact: deviceLink.slave.publicKey, transaction: transaction)
}
}
dismiss(animated: true, completion: nil)

@ -78,7 +78,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
var deviceLinks: [DeviceLink] = []
storage.dbReadConnection.read { transaction in
deviceLinks = storage.getDeviceLinks(for: userHexEncodedPublicKey, in: transaction).sorted { lhs, rhs in
return lhs.other.hexEncodedPublicKey > rhs.other.hexEncodedPublicKey
return lhs.other.publicKey > rhs.other.publicKey
}
}
self.deviceLinks = deviceLinks
@ -141,7 +141,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
private func removeDeviceLink(_ deviceLink: DeviceLink) {
FileServerAPI.removeDeviceLink(deviceLink).done { [weak self] in
let linkedDevicePublicKey = deviceLink.other.hexEncodedPublicKey
let linkedDevicePublicKey = deviceLink.other.publicKey
guard let thread = TSContactThread.fetch(uniqueId: TSContactThread.threadId(fromContactId: linkedDevicePublicKey)) else { return }
let unlinkDeviceMessage = UnlinkDeviceMessage(thread: thread)
SSKEnvironment.shared.messageSender.send(unlinkDeviceMessage, success: {
@ -226,7 +226,7 @@ private extension DeviceLinksVC {
// MARK: Updating
private func update() {
titleLabel.text = device.displayName
subtitleLabel.text = Mnemonic.hash(hexEncodedString: device.hexEncodedPublicKey.removing05PrefixIfNeeded())
subtitleLabel.text = Mnemonic.hash(hexEncodedString: device.publicKey.removing05PrefixIfNeeded())
}
}
}

@ -92,7 +92,7 @@ final class DeviceNameModal : Modal {
@objc private func changeName() {
let name = nameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if !name.isEmpty {
UserDefaults.standard[.slaveDeviceName(device.hexEncodedPublicKey)] = name
UserDefaults.standard[.slaveDeviceName(device.publicKey)] = name
delegate?.handleDeviceNameChanged(to: name, for: device)
} else {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""), message: NSLocalizedString("Please pick a name", comment: ""), preferredStyle: .alert)

@ -359,7 +359,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
guard let threadID = self.thread(at: indexPath.row)?.uniqueId else { return false }
var publicChat: LokiPublicChat?
var publicChat: PublicChat?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
}
@ -372,7 +372,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
guard let thread = self.thread(at: indexPath.row) else { return [] }
var publicChat: LokiPublicChat?
var publicChat: PublicChat?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction)
}
@ -386,9 +386,9 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
messageIDs.insert(interaction.uniqueId!)
}
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
transaction.removeObject(forKey: "\(publicChat.server).\(publicChat.channel)", inCollection: LokiPublicChatAPI.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(publicChat.server).\(publicChat.channel)", inCollection: LokiPublicChatAPI.lastDeletionServerIDCollection)
let _ = LokiPublicChatAPI.leave(publicChat.channel, on: publicChat.server)
transaction.removeObject(forKey: "\(publicChat.server).\(publicChat.channel)", inCollection: PublicChatAPI.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(publicChat.server).\(publicChat.channel)", inCollection: PublicChatAPI.lastDeletionServerIDCollection)
let _ = PublicChatAPI.leave(publicChat.channel, on: publicChat.server)
}
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)

@ -139,14 +139,14 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
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)
transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: PublicChatAPI.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: PublicChatAPI.lastDeletionServerIDCollection)
}
LokiPublicChatManager.shared.addChat(server: urlAsString, channel: channelID)
PublicChatManager.shared.addChat(server: urlAsString, channel: channelID)
.done(on: .main) { [weak self] _ in
let _ = LokiPublicChatAPI.setDisplayName(to: displayName, on: urlAsString)
let _ = LokiPublicChatAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: urlAsString)
let _ = LokiPublicChatAPI.join(channelID, on: urlAsString)
let _ = PublicChatAPI.setDisplayName(to: displayName, on: urlAsString)
let _ = PublicChatAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: urlAsString)
let _ = PublicChatAPI.join(channelID, on: urlAsString)
let syncManager = SSKEnvironment.shared.syncManager
let _ = syncManager.syncAllOpenGroups()
self?.presentingViewController!.dismiss(animated: true, completion: nil)

@ -151,7 +151,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
}
func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) {
UserDefaults.standard[.masterHexEncodedPublicKey] = deviceLink.master.hexEncodedPublicKey
UserDefaults.standard[.masterHexEncodedPublicKey] = deviceLink.master.publicKey
fakeChatViewContentOffset = fakeChatView.contentOffset
DispatchQueue.main.async {
self.fakeChatView.contentOffset = self.fakeChatViewContentOffset

@ -62,10 +62,10 @@ final class OpenGroupSuggestionSheet : Sheet {
let url = "https://chat.getsession.org"
let displayName = OWSProfileManager.shared().localProfileName()
// TODO: Profile picture & profile key
let _ = LokiPublicChatManager.shared.addChat(server: url, channel: channelID).done(on: .main) { _ in
let _ = LokiPublicChatAPI.getMessages(for: channelID, on: url)
let _ = LokiPublicChatAPI.setDisplayName(to: displayName, on: url)
let _ = LokiPublicChatAPI.join(channelID, on: url)
let _ = PublicChatManager.shared.addChat(server: url, channel: channelID).done(on: .main) { _ in
let _ = PublicChatAPI.getMessages(for: channelID, on: url)
let _ = PublicChatAPI.setDisplayName(to: displayName, on: url)
let _ = PublicChatAPI.join(channelID, on: url)
}
close()
}

@ -3893,7 +3893,7 @@ typedef enum : NSUInteger {
NSString *result = self.inputToolbar.messageText;
for (LKMention *mention in self.mentions) {
NSRange range = [result rangeOfString:[NSString stringWithFormat:@"@%@", mention.displayName]];
result = [result stringByReplacingCharactersInRange:range withString:[[NSString alloc] initWithFormat:@"@%@", mention.hexEncodedPublicKey]];
result = [result stringByReplacingCharactersInRange:range withString:[[NSString alloc] initWithFormat:@"@%@", mention.publicKey]];
}
return result;
}

@ -679,7 +679,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
case let incomingMessage as TSIncomingMessage:
let hexEncodedPublicKey = incomingMessage.authorId
if incomingMessage.thread.isGroupThread() {
var publicChat: LokiPublicChat?
var publicChat: PublicChat?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: incomingMessage.thread.uniqueId!, in: transaction)
}

@ -211,7 +211,7 @@ internal extension Promise {
internal func handlingInvalidAuthTokenIfNeeded(for server: String) -> Promise<T> {
return recover2 { error -> Promise<T> in
if let error = error as? NetworkManagerError, (error.statusCode == 401 || error.statusCode == 403) {
print("[Loki] Group chat auth token for: \(server) expired; dropping it.")
print("[Loki] Auth token for: \(server) expired; dropping it.")
DotNetAPI.clearAuthToken(for: server)
}
throw error

@ -65,8 +65,8 @@ public final class FileServerAPI : DotNetAPI {
masterSignature = nil
}
let slaveSignature = Data(base64Encoded: base64EncodedSlaveSignature)
let master = DeviceLink.Device(hexEncodedPublicKey: masterHexEncodedPublicKey, signature: masterSignature)
let slave = DeviceLink.Device(hexEncodedPublicKey: slaveHexEncodedPublicKey, signature: slaveSignature)
let master = DeviceLink.Device(publicKey: masterHexEncodedPublicKey, signature: masterSignature)
let slave = DeviceLink.Device(publicKey: slaveHexEncodedPublicKey, signature: slaveSignature)
let deviceLink = DeviceLink(between: master, and: slave)
if let masterSignature = masterSignature {
guard DeviceLinkingUtilities.hasValidMasterSignature(deviceLink) else {
@ -91,7 +91,7 @@ public final class FileServerAPI : DotNetAPI {
public static func setDeviceLinks(_ deviceLinks: Set<DeviceLink>) -> Promise<Void> {
print("[Loki] Updating device links.")
return getAuthToken(for: server).then2 { token -> Promise<Void> in
let isMaster = deviceLinks.contains { $0.master.hexEncodedPublicKey == getUserHexEncodedPublicKey() }
let isMaster = deviceLinks.contains { $0.master.publicKey == getUserHexEncodedPublicKey() }
let deviceLinksAsJSON = deviceLinks.map { $0.toJSON() }
let value = !deviceLinksAsJSON.isEmpty ? [ "isPrimary" : isMaster ? 1 : 0, "authorisations" : deviceLinksAsJSON ] : nil
let annotation: JSON = [ "type" : deviceLinkType, "value" : value ]

@ -1,6 +1,6 @@
@objc(LKPublicChat)
public final class LokiPublicChat : NSObject, NSCoding {
public final class PublicChat : NSObject, NSCoding {
@objc public let id: String
@objc public let idAsData: Data
@objc public let channel: UInt64

@ -1,10 +1,10 @@
import PromiseKit
@objc(LKPublicChatAPI)
public final class LokiPublicChatAPI : DotNetAPI {
public final class PublicChatAPI : DotNetAPI {
private static var moderators: [String:[UInt64:Set<String>]] = [:] // Server URL to (channel ID to set of moderator IDs)
@objc public static let defaultChats: [LokiPublicChat] = [] // Currently unused
@objc public static let defaultChats: [PublicChat] = [] // Currently unused
public static var displayNameUpdatees: [String:Set<String>] = [:]
@ -80,14 +80,14 @@ public final class LokiPublicChatAPI : DotNetAPI {
return AnyPromise.from(getMessages(for: group, on: server))
}
public static func getMessages(for channel: UInt64, on server: String) -> Promise<[LokiPublicChatMessage]> {
public static func getMessages(for channel: UInt64, on server: String) -> Promise<[PublicChatMessage]> {
var queryParameters = "include_annotations=1"
if let lastMessageServerID = getLastMessageServerID(for: channel, on: server) {
queryParameters += "&since_id=\(lastMessageServerID)"
} else {
queryParameters += "&count=\(fallbackBatchCount)&include_deleted=0"
}
return getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise<[LokiPublicChatMessage]> in
return getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise<[PublicChatMessage]> in
let url = URL(string: "\(server)/channels/\(channel)/messages?\(queryParameters)")!
let request = TSRequest(url: url)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
@ -101,32 +101,32 @@ public final class LokiPublicChatAPI : DotNetAPI {
guard !isDeleted else { return nil }
guard let annotations = message["annotations"] as? [JSON], let annotation = annotations.first(where: { $0["type"] as? String == publicChatMessageType }), let value = annotation["value"] as? JSON,
let serverID = message["id"] as? UInt64, let hexEncodedSignatureData = value["sig"] as? String, let signatureVersion = value["sigver"] as? UInt64,
let body = message["text"] as? String, let user = message["user"] as? JSON, let hexEncodedPublicKey = user["username"] as? String,
let body = message["text"] as? String, let user = message["user"] as? JSON, let senderPublicKey = user["username"] as? String,
let timestamp = value["timestamp"] as? UInt64 else {
print("[Loki] Couldn't parse message for public chat channel with ID: \(channel) on server: \(server) from: \(message).")
return nil
}
var profilePicture: LokiPublicChatMessage.ProfilePicture? = nil
var profilePicture: PublicChatMessage.ProfilePicture? = nil
let displayName = user["name"] as? String ?? NSLocalizedString("Anonymous", comment: "")
if let userAnnotations = user["annotations"] as? [JSON], let profilePictureAnnotation = userAnnotations.first(where: { $0["type"] as? String == profilePictureType }),
let profilePictureValue = profilePictureAnnotation["value"] as? JSON, let profileKeyString = profilePictureValue["profileKey"] as? String, let profileKey = Data(base64Encoded: profileKeyString), let url = profilePictureValue["url"] as? String {
profilePicture = LokiPublicChatMessage.ProfilePicture(profileKey: profileKey, url: url)
profilePicture = PublicChatMessage.ProfilePicture(profileKey: profileKey, url: url)
}
let lastMessageServerID = getLastMessageServerID(for: channel, on: server)
if serverID > (lastMessageServerID ?? 0) { setLastMessageServerID(for: channel, on: server, to: serverID) }
let quote: LokiPublicChatMessage.Quote?
if let quoteAsJSON = value["quote"] as? JSON, let quotedMessageTimestamp = quoteAsJSON["id"] as? UInt64, let quoteeHexEncodedPublicKey = quoteAsJSON["author"] as? String,
let quote: PublicChatMessage.Quote?
if let quoteAsJSON = value["quote"] as? JSON, let quotedMessageTimestamp = quoteAsJSON["id"] as? UInt64, let quoteePublicKey = quoteAsJSON["author"] as? String,
let quotedMessageBody = quoteAsJSON["text"] as? String {
let quotedMessageServerID = message["reply_to"] as? UInt64
quote = LokiPublicChatMessage.Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteeHexEncodedPublicKey: quoteeHexEncodedPublicKey, quotedMessageBody: quotedMessageBody,
quote = PublicChatMessage.Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteePublicKey: quoteePublicKey, quotedMessageBody: quotedMessageBody,
quotedMessageServerID: quotedMessageServerID)
} else {
quote = nil
}
let signature = LokiPublicChatMessage.Signature(data: Data(hex: hexEncodedSignatureData), version: signatureVersion)
let signature = PublicChatMessage.Signature(data: Data(hex: hexEncodedSignatureData), version: signatureVersion)
let attachmentsAsJSON = annotations.filter { $0["type"] as? String == attachmentType }
let attachments: [LokiPublicChatMessage.Attachment] = attachmentsAsJSON.compactMap { attachmentAsJSON in
guard let value = attachmentAsJSON["value"] as? JSON, let kindAsString = value["lokiType"] as? String, let kind = LokiPublicChatMessage.Attachment.Kind(rawValue: kindAsString),
let attachments: [PublicChatMessage.Attachment] = attachmentsAsJSON.compactMap { attachmentAsJSON in
guard let value = attachmentAsJSON["value"] as? JSON, let kindAsString = value["lokiType"] as? String, let kind = PublicChatMessage.Attachment.Kind(rawValue: kindAsString),
let serverID = value["id"] as? UInt64, let contentType = value["contentType"] as? String, let size = value["size"] as? UInt, let url = value["url"] as? String else { return nil }
let fileName = value["fileName"] as? String ?? UUID().description
let width = value["width"] as? UInt ?? 0
@ -141,10 +141,10 @@ public final class LokiPublicChatAPI : DotNetAPI {
return nil
}
}
return LokiPublicChatMessage.Attachment(kind: kind, server: server, serverID: serverID, contentType: contentType, size: size, fileName: fileName, flags: flags,
return PublicChatMessage.Attachment(kind: kind, server: server, serverID: serverID, contentType: contentType, size: size, fileName: fileName, flags: flags,
width: width, height: height, caption: caption, url: url, linkPreviewURL: linkPreviewURL, linkPreviewTitle: linkPreviewTitle)
}
let result = LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, profilePicture: profilePicture,
let result = PublicChatMessage(serverID: serverID, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: profilePicture,
body: body, type: publicChatMessageType, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature)
guard result.hasValidSignature() else {
print("[Loki] Ignoring public chat message with invalid signature.")
@ -166,17 +166,17 @@ public final class LokiPublicChatAPI : DotNetAPI {
// MARK: Sending
@objc(sendMessage:toGroup:onServer:)
public static func objc_sendMessage(_ message: LokiPublicChatMessage, to group: UInt64, on server: String) -> AnyPromise {
public static func objc_sendMessage(_ message: PublicChatMessage, to group: UInt64, on server: String) -> AnyPromise {
return AnyPromise.from(sendMessage(message, to: group, on: server))
}
public static func sendMessage(_ message: LokiPublicChatMessage, to channel: UInt64, on server: String) -> Promise<LokiPublicChatMessage> {
public static func sendMessage(_ message: PublicChatMessage, to channel: UInt64, on server: String) -> Promise<PublicChatMessage> {
print("[Loki] Sending message to public chat channel with ID: \(channel) on server: \(server).")
let (promise, seal) = Promise<LokiPublicChatMessage>.pending()
let (promise, seal) = Promise<PublicChatMessage>.pending()
DispatchQueue.global(qos: .userInitiated).async { [privateKey = userKeyPair.privateKey] in
guard let signedMessage = message.sign(with: privateKey) else { return seal.reject(DotNetAPIError.signingFailed) }
attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global(qos: .default)) {
getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise<LokiPublicChatMessage> in
getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise<PublicChatMessage> in
let url = URL(string: "\(server)/channels/\(channel)/messages")!
let parameters = signedMessage.toJSON()
let request = TSRequest(url: url, method: "POST", parameters: parameters)
@ -192,7 +192,7 @@ public final class LokiPublicChatAPI : DotNetAPI {
throw DotNetAPIError.parsingFailed
}
let timestamp = UInt64(date.timeIntervalSince1970) * 1000
return LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: getUserHexEncodedPublicKey(), displayName: displayName, profilePicture: signedMessage.profilePicture, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, attachments: signedMessage.attachments, signature: signedMessage.signature)
return PublicChatMessage(serverID: serverID, senderPublicKey: getUserHexEncodedPublicKey(), displayName: displayName, profilePicture: signedMessage.profilePicture, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, attachments: signedMessage.attachments, signature: signedMessage.signature)
}
}.handlingInvalidAuthTokenIfNeeded(for: server)
}.done(on: DispatchQueue.global(qos: .default)) { message in
@ -330,7 +330,7 @@ public final class LokiPublicChatAPI : DotNetAPI {
}
}
static func updateProfileIfNeeded(for channel: UInt64, on server: String, from info: LokiPublicChatInfo) {
static func updateProfileIfNeeded(for channel: UInt64, on server: String, from info: PublicChatInfo) {
let storage = OWSPrimaryStorage.shared()
let publicChatID = "\(server).\(channel)"
try! Storage.writeSync { transaction in
@ -381,9 +381,9 @@ public final class LokiPublicChatAPI : DotNetAPI {
return AnyPromise.from(getInfo(for: channel, on: server))
}
public static func getInfo(for channel: UInt64, on server: String) -> Promise<LokiPublicChatInfo> {
public static func getInfo(for channel: UInt64, on server: String) -> Promise<PublicChatInfo> {
return attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global(qos: .default)) {
getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise<LokiPublicChatInfo> in
getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise<PublicChatInfo> in
let url = URL(string: "\(server)/channels/\(channel)?include_annotations=1")!
let request = TSRequest(url: url)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
@ -404,7 +404,7 @@ public final class LokiPublicChatAPI : DotNetAPI {
try! Storage.writeSync { transaction in
storage.setUserCount(memberCount, forPublicChatWithID: "\(server).\(channel)", in: transaction)
}
let publicChatInfo = LokiPublicChatInfo(displayName: displayName, profilePictureURL: profilePictureURL, memberCount: memberCount)
let publicChatInfo = PublicChatInfo(displayName: displayName, profilePictureURL: profilePictureURL, memberCount: memberCount)
updateProfileIfNeeded(for: channel, on: server, from: publicChatInfo)
return publicChatInfo
}

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

@ -3,10 +3,10 @@ import PromiseKit
// TODO: Clean
@objc(LKPublicChatManager)
public final class LokiPublicChatManager : NSObject {
public final class PublicChatManager : NSObject {
private let storage = OWSPrimaryStorage.shared()
@objc public var chats: [String:LokiPublicChat] = [:]
private var pollers: [String:LokiPublicChatPoller] = [:]
@objc public var chats: [String:PublicChat] = [:]
private var pollers: [String:PublicChatPoller] = [:]
private var isPolling = false
private var userHexEncodedPublicKey: String? {
@ -18,7 +18,7 @@ public final class LokiPublicChatManager : NSObject {
case userPublicKeyNotFound
}
@objc public static let shared = LokiPublicChatManager()
@objc public static let shared = PublicChatManager()
private override init() {
super.init()
@ -35,7 +35,7 @@ public final class LokiPublicChatManager : NSObject {
if let poller = pollers[threadID] {
poller.startIfNeeded()
} else {
let poller = LokiPublicChatPoller(for: publicChat)
let poller = PublicChatPoller(for: publicChat)
poller.startIfNeeded()
pollers[threadID] = poller
}
@ -48,7 +48,7 @@ public final class LokiPublicChatManager : NSObject {
isPolling = false
}
public func addChat(server: String, channel: UInt64) -> Promise<LokiPublicChat> {
public func addChat(server: String, channel: UInt64) -> Promise<PublicChat> {
if let existingChat = getChat(server: server, channel: channel) {
if let newChat = self.addChat(server: server, channel: channel, name: existingChat.displayName) {
return Promise.value(newChat)
@ -56,9 +56,9 @@ public final class LokiPublicChatManager : NSObject {
return Promise(error: Error.chatCreationFailed)
}
}
return LokiPublicChatAPI.getAuthToken(for: server).then2 { token in
return LokiPublicChatAPI.getInfo(for: channel, on: server)
}.map2 { channelInfo -> LokiPublicChat in
return PublicChatAPI.getAuthToken(for: server).then2 { token in
return PublicChatAPI.getInfo(for: channel, on: server)
}.map2 { channelInfo -> PublicChat in
guard let chat = self.addChat(server: server, channel: channel, name: channelInfo.displayName) else { throw Error.chatCreationFailed }
return chat
}
@ -66,8 +66,8 @@ public final class LokiPublicChatManager : NSObject {
@discardableResult
@objc(addChatWithServer:channel:name:)
public func addChat(server: String, channel: UInt64, name: String) -> LokiPublicChat? {
guard let chat = LokiPublicChat(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil }
public func addChat(server: String, channel: UInt64, name: String) -> PublicChat? {
guard let chat = PublicChat(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil }
let model = TSGroupModel(title: chat.displayName, memberIds: [userHexEncodedPublicKey!, chat.server], image: nil, groupId: LKGroupUtilities.getEncodedOpenGroupIDAsData(chat.id), groupType: .openGroup, adminIds: [])
// Store the group chat mapping
@ -114,7 +114,7 @@ public final class LokiPublicChatManager : NSObject {
// Reset the last message cache
if let chat = self.chats[threadId] {
LokiPublicChatAPI.clearCaches(for: chat.channel, on: chat.server)
PublicChatAPI.clearCaches(for: chat.channel, on: chat.server)
}
// Remove the chat from the db
@ -125,7 +125,7 @@ public final class LokiPublicChatManager : NSObject {
refreshChatsAndPollers()
}
public func getChat(server: String, channel: UInt64) -> LokiPublicChat? {
public func getChat(server: String, channel: UInt64) -> PublicChat? {
return chats.values.first { chat in
return chat.server == server && chat.channel == channel
}

@ -1,9 +1,9 @@
import PromiseKit
@objc(LKGroupMessage)
public final class LokiPublicChatMessage : NSObject {
@objc(LKPublicChatMessage)
public final class PublicChatMessage : NSObject {
public let serverID: UInt64?
public let hexEncodedPublicKey: String
public let senderPublicKey: String
public let displayName: String
public let profilePicture: ProfilePicture?
public let body: String
@ -29,7 +29,7 @@ public final class LokiPublicChatMessage : NSObject {
public struct Quote {
public let quotedMessageTimestamp: UInt64
public let quoteeHexEncodedPublicKey: String
public let quoteePublicKey: String
public let quotedMessageBody: String
public let quotedMessageServerID: UInt64?
}
@ -72,9 +72,9 @@ public final class LokiPublicChatMessage : NSObject {
}
// MARK: Initialization
public init(serverID: UInt64?, hexEncodedPublicKey: String, displayName: String, profilePicture: ProfilePicture?, body: String, type: String, timestamp: UInt64, quote: Quote?, attachments: [Attachment], signature: Signature?) {
public init(serverID: UInt64?, senderPublicKey: String, displayName: String, profilePicture: ProfilePicture?, body: String, type: String, timestamp: UInt64, quote: Quote?, attachments: [Attachment], signature: Signature?) {
self.serverID = serverID
self.hexEncodedPublicKey = hexEncodedPublicKey
self.senderPublicKey = senderPublicKey
self.displayName = displayName
self.profilePicture = profilePicture
self.body = body
@ -86,11 +86,11 @@ public final class LokiPublicChatMessage : NSObject {
super.init()
}
@objc public convenience init(hexEncodedPublicKey: String, displayName: String, body: String, type: String, timestamp: UInt64, quotedMessageTimestamp: UInt64, quoteeHexEncodedPublicKey: String?, quotedMessageBody: String?, quotedMessageServerID: UInt64, signatureData: Data?, signatureVersion: UInt64) {
@objc public convenience init(senderPublicKey: String, displayName: String, body: String, type: String, timestamp: UInt64, quotedMessageTimestamp: UInt64, quoteePublicKey: String?, quotedMessageBody: String?, quotedMessageServerID: UInt64, signatureData: Data?, signatureVersion: UInt64) {
let quote: Quote?
if quotedMessageTimestamp != 0, let quoteeHexEncodedPublicKey = quoteeHexEncodedPublicKey, let quotedMessageBody = quotedMessageBody {
if quotedMessageTimestamp != 0, let quoteeHexEncodedPublicKey = quoteePublicKey, let quotedMessageBody = quotedMessageBody {
let quotedMessageServerID = (quotedMessageServerID != 0) ? quotedMessageServerID : nil
quote = Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteeHexEncodedPublicKey: quoteeHexEncodedPublicKey, quotedMessageBody: quotedMessageBody, quotedMessageServerID: quotedMessageServerID)
quote = Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteePublicKey: quoteeHexEncodedPublicKey, quotedMessageBody: quotedMessageBody, quotedMessageServerID: quotedMessageServerID)
} else {
quote = nil
}
@ -100,11 +100,11 @@ public final class LokiPublicChatMessage : NSObject {
} else {
signature = nil
}
self.init(serverID: nil, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, profilePicture: nil, body: body, type: type, timestamp: timestamp, quote: quote, attachments: [], signature: signature)
self.init(serverID: nil, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: nil, body: body, type: type, timestamp: timestamp, quote: quote, attachments: [], signature: signature)
}
// MARK: Crypto
internal func sign(with privateKey: Data) -> LokiPublicChatMessage? {
internal func sign(with privateKey: Data) -> PublicChatMessage? {
guard let data = getValidationData(for: signatureVersion) else {
print("[Loki] Failed to sign public chat message.")
return nil
@ -115,13 +115,13 @@ public final class LokiPublicChatMessage : NSObject {
return nil
}
let signature = Signature(data: signatureData, version: signatureVersion)
return LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, profilePicture: profilePicture, body: body, type: type, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature)
return PublicChatMessage(serverID: serverID, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: profilePicture, body: body, type: type, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature)
}
internal func hasValidSignature() -> Bool {
guard let signature = signature else { return false }
guard let data = getValidationData(for: signature.version) else { return false }
let publicKey = Data(hex: hexEncodedPublicKey.removing05PrefixIfNeeded())
let publicKey = Data(hex: self.senderPublicKey.removing05PrefixIfNeeded())
return (try? Ed25519.verifySignature(signature.data, publicKey: publicKey, data: data)) ?? false
}
@ -129,7 +129,7 @@ public final class LokiPublicChatMessage : NSObject {
internal func toJSON() -> JSON {
var value: JSON = [ "timestamp" : timestamp ]
if let quote = quote {
value["quote"] = [ "id" : quote.quotedMessageTimestamp, "author" : quote.quoteeHexEncodedPublicKey, "text" : quote.quotedMessageBody ]
value["quote"] = [ "id" : quote.quotedMessageTimestamp, "author" : quote.quoteePublicKey, "text" : quote.quotedMessageBody ]
}
if let signature = signature {
value["sig"] = signature.data.toHexString()
@ -175,7 +175,7 @@ public final class LokiPublicChatMessage : NSObject {
private func getValidationData(for signatureVersion: UInt64) -> Data? {
var string = "\(body.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines))\(timestamp)"
if let quote = quote {
string += "\(quote.quotedMessageTimestamp)\(quote.quoteeHexEncodedPublicKey)\(quote.quotedMessageBody.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines))"
string += "\(quote.quotedMessageTimestamp)\(quote.quoteePublicKey)\(quote.quotedMessageBody.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines))"
if let quotedMessageServerID = quote.quotedMessageServerID {
string += "\(quotedMessageServerID)"
}

@ -1,8 +1,8 @@
import PromiseKit
@objc(LKPublicChatPoller)
public final class LokiPublicChatPoller : NSObject {
private let publicChat: LokiPublicChat
public final class PublicChatPoller : NSObject {
private let publicChat: PublicChat
private var pollForNewMessagesTimer: Timer? = nil
private var pollForDeletedMessagesTimer: Timer? = nil
private var pollForModeratorsTimer: Timer? = nil
@ -17,7 +17,7 @@ public final class LokiPublicChatPoller : NSObject {
// MARK: Lifecycle
@objc(initForPublicChat:)
public init(for publicChat: LokiPublicChat) {
public init(for publicChat: PublicChat) {
self.publicChat = publicChat
super.init()
}
@ -55,38 +55,38 @@ public final class LokiPublicChatPoller : NSObject {
public func pollForNewMessages() -> Promise<Void> {
let publicChat = self.publicChat
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
return LokiPublicChatAPI.getMessages(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { messages in
let uniqueHexEncodedPublicKeys = Set(messages.map { $0.hexEncodedPublicKey })
let userPublicKey = getUserHexEncodedPublicKey()
return PublicChatAPI.getMessages(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { messages in
let uniquePublicKeys = Set(messages.map { $0.senderPublicKey })
func proceed() {
let storage = OWSPrimaryStorage.shared()
var newDisplayNameUpdatees: Set<String> = []
storage.dbReadConnection.read { transaction in
newDisplayNameUpdatees = Set(uniqueHexEncodedPublicKeys.filter { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) != $0 }.compactMap { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) })
newDisplayNameUpdatees = Set(uniquePublicKeys.filter { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) != $0 }.compactMap { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) })
}
if !newDisplayNameUpdatees.isEmpty {
let displayNameUpdatees = LokiPublicChatAPI.displayNameUpdatees[publicChat.id] ?? []
LokiPublicChatAPI.displayNameUpdatees[publicChat.id] = displayNameUpdatees.union(newDisplayNameUpdatees)
let displayNameUpdatees = PublicChatAPI.displayNameUpdatees[publicChat.id] ?? []
PublicChatAPI.displayNameUpdatees[publicChat.id] = displayNameUpdatees.union(newDisplayNameUpdatees)
}
// Sorting the messages by timestamp before importing them fixes an issue where messages that quote older messages can't find those older messages
messages.sorted { $0.timestamp < $1.timestamp }.forEach { message in
var wasSentByCurrentUser = false
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
wasSentByCurrentUser = LokiDatabaseUtilities.isUserLinkedDevice(message.hexEncodedPublicKey, transaction: transaction)
wasSentByCurrentUser = LokiDatabaseUtilities.isUserLinkedDevice(message.senderPublicKey, transaction: transaction)
}
var masterHexEncodedPublicKey: String? = nil
var masterPublicKey: String? = nil
storage.dbReadConnection.read { transaction in
masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: message.hexEncodedPublicKey, in: transaction)
masterPublicKey = storage.getMasterHexEncodedPublicKey(for: message.senderPublicKey, in: transaction)
}
let senderHexEncodedPublicKey = masterHexEncodedPublicKey ?? message.hexEncodedPublicKey
let senderPublicKey = masterPublicKey ?? message.senderPublicKey
func generateDisplayName(from rawDisplayName: String) -> String {
let endIndex = senderHexEncodedPublicKey.endIndex
let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8)
return "\(rawDisplayName) (...\(senderHexEncodedPublicKey[cutoffIndex..<endIndex]))"
let endIndex = senderPublicKey.endIndex
let cutoffIndex = senderPublicKey.index(endIndex, offsetBy: -8)
return "\(rawDisplayName) (...\(senderPublicKey[cutoffIndex..<endIndex]))"
}
var senderDisplayName = ""
if let masterHexEncodedPublicKey = masterHexEncodedPublicKey {
senderDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: senderHexEncodedPublicKey, in: publicChat.channel, on: publicChat.server) ?? generateDisplayName(from: NSLocalizedString("Anonymous", comment: ""))
if let masterHexEncodedPublicKey = masterPublicKey {
senderDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: senderPublicKey, in: publicChat.channel, on: publicChat.server) ?? generateDisplayName(from: NSLocalizedString("Anonymous", comment: ""))
} else {
senderDisplayName = generateDisplayName(from: message.displayName)
}
@ -137,7 +137,7 @@ public final class LokiPublicChatPoller : NSObject {
dataMessage.setTimestamp(message.timestamp)
dataMessage.setGroup(try! groupContext.build())
if let quote = message.quote {
let signalQuote = SSKProtoDataMessageQuote.builder(id: quote.quotedMessageTimestamp, author: quote.quoteeHexEncodedPublicKey)
let signalQuote = SSKProtoDataMessageQuote.builder(id: quote.quotedMessageTimestamp, author: quote.quoteePublicKey)
signalQuote.setText(quote.quotedMessageBody)
dataMessage.setQuote(try! signalQuote.build())
}
@ -154,10 +154,10 @@ public final class LokiPublicChatPoller : NSObject {
} 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)
SyncMessagesProtocol.dropFromSyncMessageTimestampCache(message.timestamp, for: senderPublicKey)
let syncMessageSentBuilder = SSKProtoSyncMessageSent.builder()
syncMessageSentBuilder.setMessage(try! dataMessage.build())
syncMessageSentBuilder.setDestination(userHexEncodedPublicKey)
syncMessageSentBuilder.setDestination(userPublicKey)
syncMessageSentBuilder.setTimestamp(message.timestamp)
let syncMessageSent = try! syncMessageSentBuilder.build()
let syncMessageBuilder = SSKProtoSyncMessage.builder()
@ -165,25 +165,25 @@ public final class LokiPublicChatPoller : NSObject {
content.setSyncMessage(try! syncMessageBuilder.build())
}
let envelope = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: message.timestamp)
envelope.setSource(senderHexEncodedPublicKey)
envelope.setSource(senderPublicKey)
envelope.setSourceDevice(OWSDevicePrimaryDeviceId)
envelope.setContent(try! content.build().serializedData())
try! Storage.writeSync { transaction in
transaction.setObject(senderDisplayName, forKey: senderHexEncodedPublicKey, inCollection: publicChat.id)
transaction.setObject(senderDisplayName, forKey: senderPublicKey, inCollection: publicChat.id)
let messageServerID = message.serverID
SSKEnvironment.shared.messageManager.throws_processEnvelope(try! envelope.build(), plaintextData: try! content.build().serializedData(), wasReceivedByUD: false, transaction: transaction, serverID: messageServerID ?? 0)
// If we got a message from our master device then we should use its profile picture
if let profilePicture = message.profilePicture, masterHexEncodedPublicKey == message.hexEncodedPublicKey {
if let profilePicture = message.profilePicture, masterPublicKey == message.senderPublicKey {
if (message.displayName.count > 0) {
SSKEnvironment.shared.profileManager.updateProfileForContact(withID: masterHexEncodedPublicKey!, displayName: message.displayName, with: transaction)
SSKEnvironment.shared.profileManager.updateProfileForContact(withID: masterPublicKey!, displayName: message.displayName, with: transaction)
}
SSKEnvironment.shared.profileManager.updateService(withProfileName: message.displayName, avatarURL: profilePicture.url)
SSKEnvironment.shared.profileManager.setProfileKeyData(profilePicture.profileKey, forRecipientId: masterHexEncodedPublicKey!, avatarURL: profilePicture.url)
SSKEnvironment.shared.profileManager.setProfileKeyData(profilePicture.profileKey, forRecipientId: masterPublicKey!, avatarURL: profilePicture.url)
}
}
}
}
let hexEncodedPublicKeysToUpdate = uniqueHexEncodedPublicKeys.filter { hexEncodedPublicKey in
let hexEncodedPublicKeysToUpdate = uniquePublicKeys.filter { hexEncodedPublicKey in
let timeSinceLastUpdate: TimeInterval
if let lastDeviceLinkUpdate = MultiDeviceProtocol.lastDeviceLinkUpdate[hexEncodedPublicKey] {
timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate)
@ -217,7 +217,7 @@ public final class LokiPublicChatPoller : NSObject {
private func pollForDeletedMessages() {
let publicChat = self.publicChat
let _ = LokiPublicChatAPI.getDeletedMessageServerIDs(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { deletedMessageServerIDs in
let _ = PublicChatAPI.getDeletedMessageServerIDs(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { deletedMessageServerIDs in
try! Storage.writeSync { transaction in
let deletedMessageIDs = deletedMessageServerIDs.compactMap { OWSPrimaryStorage.shared().getIDForMessage(withServerID: UInt($0), in: transaction) }
deletedMessageIDs.forEach { messageID in
@ -228,10 +228,10 @@ public final class LokiPublicChatPoller : NSObject {
}
private func pollForModerators() {
let _ = LokiPublicChatAPI.getModerators(for: publicChat.channel, on: publicChat.server)
let _ = PublicChatAPI.getModerators(for: publicChat.channel, on: publicChat.server)
}
private func pollForDisplayNames() {
let _ = LokiPublicChatAPI.getDisplayNames(for: publicChat.channel, on: publicChat.server)
let _ = PublicChatAPI.getDisplayNames(for: publicChat.channel, on: publicChat.server)
}
}

@ -14,11 +14,11 @@ public final class SnodeAPI : NSObject {
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
// MARK: Settings
internal static let snodeFailureThreshold = 2
private static let maxRetryCount: UInt = 4
private static let minimumSnodePoolCount = 32
private static let minimumSwarmSnodeCount = 2
private static let seedNodePool: Set<String> = [ "https://storage.seed1.loki.network", "https://storage.seed3.loki.network", "https://public.loki.foundation" ]
private static let snodeFailureThreshold = 2
private static let targetSwarmSnodeCount = 2
internal static var powDifficulty: UInt = 1

@ -29,7 +29,7 @@ public final class LokiDatabaseUtilities : NSObject {
let storage = OWSPrimaryStorage.shared()
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
var result = Set(storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction).flatMap { deviceLink in
return [ deviceLink.master.hexEncodedPublicKey, deviceLink.slave.hexEncodedPublicKey ]
return [ deviceLink.master.publicKey, deviceLink.slave.publicKey ]
})
result.insert(hexEncodedPublicKey)
return result
@ -63,22 +63,22 @@ public final class LokiDatabaseUtilities : NSObject {
private static let publicChatCollection = "LokiPublicChatCollection"
@objc(getAllPublicChats:)
public static func getAllPublicChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiPublicChat] {
var result = [String:LokiPublicChat]()
public static func getAllPublicChats(in transaction: YapDatabaseReadTransaction) -> [String:PublicChat] {
var result = [String:PublicChat]()
transaction.enumerateKeysAndObjects(inCollection: publicChatCollection) { threadID, object, _ in
guard let publicChat = object as? LokiPublicChat else { return }
guard let publicChat = object as? PublicChat else { return }
result[threadID] = publicChat
}
return result
}
@objc(getPublicChatForThreadID:transaction:)
public static func getPublicChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiPublicChat? {
return transaction.object(forKey: threadID, inCollection: publicChatCollection) as? LokiPublicChat
public static func getPublicChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> PublicChat? {
return transaction.object(forKey: threadID, inCollection: publicChatCollection) as? PublicChat
}
@objc(setPublicChat:threadID:transaction:)
public static func setPublicChat(_ publicChat: LokiPublicChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) {
public static func setPublicChat(_ publicChat: PublicChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(publicChat, forKey: threadID, inCollection: publicChatCollection)
}

@ -110,19 +110,19 @@ public extension OWSPrimaryStorage {
}
public func getDeviceLinks(for masterHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> Set<DeviceLink> {
return OWSPrimaryStorage.deviceLinkCache.filter { $0.master.hexEncodedPublicKey == masterHexEncodedPublicKey }
return OWSPrimaryStorage.deviceLinkCache.filter { $0.master.publicKey == masterHexEncodedPublicKey }
}
public func getDeviceLink(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> DeviceLink? {
return OWSPrimaryStorage.deviceLinkCache.filter { $0.slave.hexEncodedPublicKey == slaveHexEncodedPublicKey }.first
return OWSPrimaryStorage.deviceLinkCache.filter { $0.slave.publicKey == slaveHexEncodedPublicKey }.first
}
public func getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? {
return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.hexEncodedPublicKey
return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.publicKey
}
// MARK: Open Groups
public func getUserCount(for publicChat: LokiPublicChat, in transaction: YapDatabaseReadTransaction) -> Int? {
public func getUserCount(for publicChat: PublicChat, in transaction: YapDatabaseReadTransaction) -> Int? {
return transaction.object(forKey: publicChat.id, inCollection: Storage.openGroupUserCountCollection) as? Int
}

@ -34,7 +34,7 @@ public final class ClosedGroupsProtocol : NSObject {
var membersAndLinkedDevices: Set<String> = members
for member in members {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] })
}
let senderKeys: [ClosedGroupSenderKey] = membersAndLinkedDevices.map { publicKey in
let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
@ -92,7 +92,7 @@ public final class ClosedGroupsProtocol : NSObject {
var newMembersAndLinkedDevices: Set<String> = newMembers
for member in newMembers {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
newMembersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
newMembersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] })
}
let senderKeys: [ClosedGroupSenderKey] = newMembersAndLinkedDevices.map { publicKey in
let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
@ -265,7 +265,7 @@ public final class ClosedGroupsProtocol : NSObject {
var membersAndLinkedDevices: Set<String> = []
for member in group.groupMemberIds {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] })
}
guard membersAndLinkedDevices.contains(senderPublicKey) else {
return print("[Loki] Ignoring closed group info message from non-member.")
@ -323,7 +323,7 @@ public final class ClosedGroupsProtocol : NSObject {
var membersAndLinkedDevices: Set<String> = []
for member in group.groupMemberIds {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] })
}
guard membersAndLinkedDevices.contains(senderPublicKey) else {
return print("[Loki] Ignoring closed group sender key request from non-member.")
@ -355,7 +355,7 @@ public final class ClosedGroupsProtocol : NSObject {
var membersAndLinkedDevices: Set<String> = []
for member in group.groupMemberIds {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] })
}
guard membersAndLinkedDevices.contains(senderPublicKey) else {
return print("[Loki] Ignoring closed group sender key from non-member.")

@ -1,11 +1,11 @@
@objc(LKMention)
public final class Mention : NSObject {
@objc public let hexEncodedPublicKey: String
@objc public let publicKey: String
@objc public let displayName: String
@objc public init(hexEncodedPublicKey: String, displayName: String) {
self.hexEncodedPublicKey = hexEncodedPublicKey
@objc public init(publicKey: String, displayName: String) {
self.publicKey = publicKey
self.displayName = displayName
}

@ -17,11 +17,11 @@ public final class MentionsManager : NSObject {
private override init() { }
// MARK: Implementation
@objc public static func cache(_ hexEncodedPublicKey: String, for threadID: String) {
@objc public static func cache(_ publicKey: String, for threadID: String) {
if let cache = userPublicKeyCache[threadID] {
userPublicKeyCache[threadID] = cache.union([ hexEncodedPublicKey ])
userPublicKeyCache[threadID] = cache.union([ publicKey ])
} else {
userPublicKeyCache[threadID] = [ hexEncodedPublicKey ]
userPublicKeyCache[threadID] = [ publicKey ]
}
}
@ -30,24 +30,24 @@ public final class MentionsManager : NSObject {
guard let cache = userPublicKeyCache[threadID] else { return [] }
var candidates: [Mention] = []
// Gather candidates
var publicChat: LokiPublicChat?
var publicChat: PublicChat?
storage.dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
}
storage.dbReadConnection.read { transaction in
candidates = cache.flatMap { hexEncodedPublicKey in
candidates = cache.flatMap { publicKey in
let uncheckedDisplayName: String?
if let publicChat = publicChat {
uncheckedDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: hexEncodedPublicKey, in: publicChat.channel, on: publicChat.server)
uncheckedDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: publicKey, in: publicChat.channel, on: publicChat.server)
} else {
uncheckedDisplayName = UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey)
uncheckedDisplayName = UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey)
}
guard let displayName = uncheckedDisplayName else { return nil }
guard !displayName.hasPrefix("Anonymous") else { return nil }
return Mention(hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName)
return Mention(publicKey: publicKey, displayName: displayName)
}
}
candidates = candidates.filter { $0.hexEncodedPublicKey != getUserHexEncodedPublicKey() }
candidates = candidates.filter { $0.publicKey != getUserHexEncodedPublicKey() }
// Sort alphabetically first
candidates.sort { $0.displayName < $1.displayName }
if query.count >= 2 {

@ -18,13 +18,12 @@ public final class SessionMetaProtocol : NSObject {
// MARK: - Sending
// MARK: Message Destination(s)
@objc(getDestinationsForOutgoingSyncMessage)
public static func objc_getDestinationsForOutgoingSyncMessage() -> NSMutableSet {
@objc public static func getDestinationsForOutgoingSyncMessage() -> NSMutableSet {
return NSMutableSet(set: MultiDeviceProtocol.getUserLinkedDevices())
}
@objc(getDestinationsForOutgoingGroupMessage:inThread:)
public static func objc_getDestinations(for outgoingGroupMessage: TSOutgoingMessage, in thread: TSThread) -> NSMutableSet {
public static func getDestinations(for outgoingGroupMessage: TSOutgoingMessage, in thread: TSThread) -> NSMutableSet {
guard let thread = thread as? TSGroupThread else { preconditionFailure("Can't get destinations for group message in non-group thread.") }
var result: Set<String> = []
if thread.isPublicChat {

@ -7,51 +7,51 @@ public final class DeviceLink : NSObject, NSCoding {
@objc public var isAuthorized: Bool { return master.signature != nil }
@objc public var other: Device {
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
return (userHexEncodedPublicKey == master.hexEncodedPublicKey) ? slave : master
let userPublicKey = getUserHexEncodedPublicKey()
return (userPublicKey == master.publicKey) ? slave : master
}
// MARK: Device
@objc(LKDevice)
public final class Device : NSObject, NSCoding {
@objc public let hexEncodedPublicKey: String
@objc public let publicKey: String
@objc public let signature: Data?
@objc public var displayName: String {
if let customDisplayName = UserDefaults.standard[.slaveDeviceName(hexEncodedPublicKey)] {
if let customDisplayName = UserDefaults.standard[.slaveDeviceName(publicKey)] {
return customDisplayName
} else {
return NSLocalizedString("Unnamed Device", comment: "")
}
}
@objc public init(hexEncodedPublicKey: String, signature: Data? = nil) {
self.hexEncodedPublicKey = hexEncodedPublicKey
@objc public init(publicKey: String, signature: Data? = nil) {
self.publicKey = publicKey
self.signature = signature
}
@objc public init?(coder: NSCoder) {
hexEncodedPublicKey = coder.decodeObject(forKey: "hexEncodedPublicKey") as! String
publicKey = coder.decodeObject(forKey: "hexEncodedPublicKey") as! String
signature = coder.decodeObject(forKey: "signature") as! Data?
}
@objc public func encode(with coder: NSCoder) {
coder.encode(hexEncodedPublicKey, forKey: "hexEncodedPublicKey")
coder.encode(publicKey, forKey: "hexEncodedPublicKey")
if let signature = signature { coder.encode(signature, forKey: "signature") }
}
@objc public override func isEqual(_ other: Any?) -> Bool {
guard let other = other as? Device else { return false }
return hexEncodedPublicKey == other.hexEncodedPublicKey && signature == other.signature
return publicKey == other.publicKey && signature == other.signature
}
@objc override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:)
var result = hexEncodedPublicKey.hashValue
var result = publicKey.hashValue
if let signature = signature { result = result ^ signature.hashValue }
return result
}
@objc override public var description: String { return hexEncodedPublicKey }
@objc override public var description: String { return publicKey }
}
// MARK: Lifecycle
@ -74,7 +74,7 @@ public final class DeviceLink : NSObject, NSCoding {
// MARK: JSON
public func toJSON() -> JSON {
var result = [ "primaryDevicePubKey" : master.hexEncodedPublicKey, "secondaryDevicePubKey" : slave.hexEncodedPublicKey ]
var result = [ "primaryDevicePubKey" : master.publicKey, "secondaryDevicePubKey" : slave.publicKey ]
if let masterSignature = master.signature { result["grantSignature"] = masterSignature.base64EncodedString() }
if let slaveSignature = slave.signature { result["requestSignature"] = slaveSignature.base64EncodedString() }
return result

@ -4,19 +4,19 @@ public final class DeviceLinkIndex : NSObject {
private static let name = "loki_device_link_index"
@objc public static let masterHexEncodedPublicKey = "master_hex_encoded_public_key"
@objc public static let slaveHexEncodedPublicKey = "slave_hex_encoded_public_key"
@objc public static let masterPublicKey = "master_hex_encoded_public_key"
@objc public static let slavePublicKey = "slave_hex_encoded_public_key"
@objc public static let isAuthorized = "is_authorized"
@objc public static let indexDatabaseExtension: YapDatabaseSecondaryIndex = {
let setup = YapDatabaseSecondaryIndexSetup()
setup.addColumn(masterHexEncodedPublicKey, with: .text)
setup.addColumn(slaveHexEncodedPublicKey, with: .text)
setup.addColumn(masterPublicKey, with: .text)
setup.addColumn(slavePublicKey, with: .text)
setup.addColumn(isAuthorized, with: .integer)
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, map, _, _, object in
guard let deviceLink = object as? DeviceLink else { return }
map[masterHexEncodedPublicKey] = deviceLink.master.hexEncodedPublicKey
map[slaveHexEncodedPublicKey] = deviceLink.slave.hexEncodedPublicKey
map[masterPublicKey] = deviceLink.master.publicKey
map[slavePublicKey] = deviceLink.slave.publicKey
map[isAuthorized] = deviceLink.isAuthorized
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler)

@ -30,10 +30,10 @@ public final class DeviceLinkingSession : NSObject {
return session
}
@objc public func processLinkingRequest(from slaveHexEncodedPublicKey: String, to masterHexEncodedPublicKey: String, with slaveSignature: Data) {
guard isListeningForLinkingRequests, !isProcessingLinkingRequest, masterHexEncodedPublicKey == getUserHexEncodedPublicKey() else { return }
let master = DeviceLink.Device(hexEncodedPublicKey: masterHexEncodedPublicKey)
let slave = DeviceLink.Device(hexEncodedPublicKey: slaveHexEncodedPublicKey, signature: slaveSignature)
@objc public func processLinkingRequest(from slavePublicKey: String, to masterPublicKey: String, with slaveSignature: Data) {
guard isListeningForLinkingRequests, !isProcessingLinkingRequest, masterPublicKey == getUserHexEncodedPublicKey() else { return }
let master = DeviceLink.Device(publicKey: masterPublicKey)
let slave = DeviceLink.Device(publicKey: slavePublicKey, signature: slaveSignature)
let deviceLink = DeviceLink(between: master, and: slave)
guard DeviceLinkingUtilities.hasValidSlaveSignature(deviceLink) else { return }
isProcessingLinkingRequest = true
@ -42,10 +42,10 @@ public final class DeviceLinkingSession : NSObject {
}
}
@objc public func processLinkingAuthorization(from masterHexEncodedPublicKey: String, for slaveHexEncodedPublicKey: String, masterSignature: Data, slaveSignature: Data) {
guard isListeningForLinkingAuthorization, slaveHexEncodedPublicKey == getUserHexEncodedPublicKey() else { return }
let master = DeviceLink.Device(hexEncodedPublicKey: masterHexEncodedPublicKey, signature: masterSignature)
let slave = DeviceLink.Device(hexEncodedPublicKey: slaveHexEncodedPublicKey, signature: slaveSignature)
@objc public func processLinkingAuthorization(from masterPublicKey: String, for slavePublicKey: String, masterSignature: Data, slaveSignature: Data) {
guard isListeningForLinkingAuthorization, slavePublicKey == getUserHexEncodedPublicKey() else { return }
let master = DeviceLink.Device(publicKey: masterPublicKey, signature: masterSignature)
let slave = DeviceLink.Device(publicKey: slavePublicKey, signature: slaveSignature)
let deviceLink = DeviceLink(between: master, and: slave)
guard DeviceLinkingUtilities.hasValidSlaveSignature(deviceLink) && DeviceLinkingUtilities.hasValidMasterSignature(deviceLink) else { return }
DispatchQueue.main.async {

@ -17,41 +17,41 @@ public final class DeviceLinkingUtilities : NSObject {
// When requesting a device link, the slave device signs the master device's public key. When authorizing
// a device link, the master device signs the slave device's public key.
public static func getLinkingRequestMessage(for masterHexEncodedPublicKey: String) -> DeviceLinkMessage {
public static func getLinkingRequestMessage(for masterPublicKey: String) -> DeviceLinkMessage {
let slaveKeyPair = OWSIdentityManager.shared().identityKeyPair()!
let slaveHexEncodedPublicKey = slaveKeyPair.hexEncodedPublicKey
let slavePublicKey = slaveKeyPair.hexEncodedPublicKey
var kind = UInt8(LKDeviceLinkMessageKind.request.rawValue)
let data = Data(hex: masterHexEncodedPublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
let data = Data(hex: masterPublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
let slaveSignature = try! Ed25519.sign(data, with: slaveKeyPair)
let thread = TSContactThread.getOrCreateThread(contactId: masterHexEncodedPublicKey)
return DeviceLinkMessage(in: thread, masterHexEncodedPublicKey: masterHexEncodedPublicKey, slaveHexEncodedPublicKey: slaveHexEncodedPublicKey, masterSignature: nil, slaveSignature: slaveSignature)
let thread = TSContactThread.getOrCreateThread(contactId: masterPublicKey)
return DeviceLinkMessage(in: thread, masterPublicKey: masterPublicKey, slavePublicKey: slavePublicKey, masterSignature: nil, slaveSignature: slaveSignature)
}
public static func getLinkingAuthorizationMessage(for deviceLink: DeviceLink) -> DeviceLinkMessage {
let masterKeyPair = OWSIdentityManager.shared().identityKeyPair()!
let masterHexEncodedPublicKey = masterKeyPair.hexEncodedPublicKey
let slaveHexEncodedPublicKey = deviceLink.slave.hexEncodedPublicKey
let masterPublicKey = masterKeyPair.hexEncodedPublicKey
let slavePublicKey = deviceLink.slave.publicKey
var kind = UInt8(LKDeviceLinkMessageKind.authorization.rawValue)
let data = Data(hex: slaveHexEncodedPublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
let data = Data(hex: slavePublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
let masterSignature = try! Ed25519.sign(data, with: masterKeyPair)
let slaveSignature = deviceLink.slave.signature!
let thread = TSContactThread.getOrCreateThread(contactId: slaveHexEncodedPublicKey)
return DeviceLinkMessage(in: thread, masterHexEncodedPublicKey: masterHexEncodedPublicKey, slaveHexEncodedPublicKey: slaveHexEncodedPublicKey, masterSignature: masterSignature, slaveSignature: slaveSignature)
let thread = TSContactThread.getOrCreateThread(contactId: slavePublicKey)
return DeviceLinkMessage(in: thread, masterPublicKey: masterPublicKey, slavePublicKey: slavePublicKey, masterSignature: masterSignature, slaveSignature: slaveSignature)
}
public static func hasValidSlaveSignature(_ deviceLink: DeviceLink) -> Bool {
guard let slaveSignature = deviceLink.slave.signature else { return false }
let slavePublicKey = Data(hex: deviceLink.slave.hexEncodedPublicKey.removing05PrefixIfNeeded())
let slavePublicKey = Data(hex: deviceLink.slave.publicKey.removing05PrefixIfNeeded())
var kind = UInt8(LKDeviceLinkMessageKind.request.rawValue)
let data = Data(hex: deviceLink.master.hexEncodedPublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
let data = Data(hex: deviceLink.master.publicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
return (try? Ed25519.verifySignature(slaveSignature, publicKey: slavePublicKey, data: data)) ?? false
}
public static func hasValidMasterSignature(_ deviceLink: DeviceLink) -> Bool {
guard let masterSignature = deviceLink.master.signature else { return false }
let masterPublicKey = Data(hex: deviceLink.master.hexEncodedPublicKey.removing05PrefixIfNeeded())
let masterPublicKey = Data(hex: deviceLink.master.publicKey.removing05PrefixIfNeeded())
var kind = UInt8(LKDeviceLinkMessageKind.authorization.rawValue)
let data = Data(hex: deviceLink.slave.hexEncodedPublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
let data = Data(hex: deviceLink.slave.publicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind))
return (try? Ed25519.verifySignature(masterSignature, publicKey: masterPublicKey, data: data)) ?? false
}
}

@ -8,12 +8,12 @@ typedef NS_ENUM(NSUInteger, LKDeviceLinkMessageKind) {
NS_SWIFT_NAME(DeviceLinkMessage)
@interface LKDeviceLinkMessage : TSOutgoingMessage
@property (nonatomic, readonly) NSString *masterHexEncodedPublicKey;
@property (nonatomic, readonly) NSString *slaveHexEncodedPublicKey;
@property (nonatomic, readonly) NSString *masterPublicKey;
@property (nonatomic, readonly) NSString *slavePublicKey;
@property (nonatomic, readonly) NSData *masterSignature; // nil for device linking requests
@property (nonatomic, readonly) NSData *slaveSignature;
@property (nonatomic, readonly) LKDeviceLinkMessageKind kind;
- (instancetype)initInThread:(TSThread *)thread masterHexEncodedPublicKey:(NSString *)masterHexEncodedPublicKey slaveHexEncodedPublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature;
- (instancetype)initInThread:(TSThread *)thread masterPublicKey:(NSString *)masterHexEncodedPublicKey slavePublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature;
@end

@ -21,12 +21,12 @@
}
#pragma mark Initialization
- (instancetype)initInThread:(TSThread *)thread masterHexEncodedPublicKey:(NSString *)masterHexEncodedPublicKey slaveHexEncodedPublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature {
- (instancetype)initInThread:(TSThread *)thread masterPublicKey:(NSString *)masterHexEncodedPublicKey slavePublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature {
self = [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray<NSString *> new]
expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil];
if (self) {
_masterHexEncodedPublicKey = masterHexEncodedPublicKey;
_slaveHexEncodedPublicKey = slaveHexEncodedPublicKey;
_masterPublicKey = masterHexEncodedPublicKey;
_slavePublicKey = slaveHexEncodedPublicKey;
_masterSignature = masterSignature;
_slaveSignature = slaveSignature;
}
@ -66,8 +66,8 @@
}
// Build the device link message
SSKProtoLokiDeviceLinkMessageBuilder *deviceLinkMessageBuilder = [SSKProtoLokiDeviceLinkMessage builder];
[deviceLinkMessageBuilder setMasterPublicKey:self.masterHexEncodedPublicKey];
[deviceLinkMessageBuilder setSlavePublicKey:self.slaveHexEncodedPublicKey];
[deviceLinkMessageBuilder setMasterPublicKey:self.masterPublicKey];
[deviceLinkMessageBuilder setSlavePublicKey:self.slavePublicKey];
if (self.masterSignature != nil) { [deviceLinkMessageBuilder setMasterSignature:self.masterSignature]; }
[deviceLinkMessageBuilder setSlaveSignature:self.slaveSignature];
SSKProtoLokiDeviceLinkMessage *deviceLinkMessage = [deviceLinkMessageBuilder buildAndReturnError:&error];

@ -247,7 +247,7 @@ public final class MultiDeviceProtocol : NSObject {
// Ignore the request if we don't know about the device link in question
let masterDeviceLinks = storage.getDeviceLinks(for: userMasterPublicKey, in: transaction)
if !masterDeviceLinks.contains(where: {
$0.master.hexEncodedPublicKey == userMasterPublicKey && $0.slave.hexEncodedPublicKey == userPublicKey
$0.master.publicKey == userMasterPublicKey && $0.slave.publicKey == userPublicKey
}) {
return
}
@ -256,7 +256,7 @@ public final class MultiDeviceProtocol : NSObject {
// Note that the device link as seen from the master device's perspective has been deleted at this point, but the
// device link as seen from the slave perspective hasn't.
if slaveDeviceLinks.contains(where: {
$0.master.hexEncodedPublicKey == userMasterPublicKey && $0.slave.hexEncodedPublicKey == userPublicKey
$0.master.publicKey == userMasterPublicKey && $0.slave.publicKey == userPublicKey
}) {
for deviceLink in slaveDeviceLinks { // In theory there should only be one
FileServerAPI.removeDeviceLink(deviceLink) // Attempt to clean up on the file server
@ -284,7 +284,7 @@ public extension MultiDeviceProtocol {
let masterDestination = MultiDeviceDestination(publicKey: masterPublicKey, isMaster: true)
destinations.insert(masterDestination)
let deviceLinks = storage.getDeviceLinks(for: masterPublicKey, in: transaction)
let slaveDestinations = deviceLinks.map { MultiDeviceDestination(publicKey: $0.slave.hexEncodedPublicKey, isMaster: false) }
let slaveDestinations = deviceLinks.map { MultiDeviceDestination(publicKey: $0.slave.publicKey, isMaster: false) }
destinations.formUnion(slaveDestinations)
seal.fulfill(destinations)
}

@ -84,7 +84,7 @@ public final class SyncMessagesProtocol : NSObject {
let userPublicKey = getUserHexEncodedPublicKey()
let masterPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey
let deviceLinks = storage.getDeviceLinks(for: masterPublicKey, in: transaction)
let linkedDevices = deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] }.filter { $0 != userPublicKey }
let linkedDevices = deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] }.filter { $0 != userPublicKey }
let senderKeys: [ClosedGroupSenderKey] = linkedDevices.map { publicKey in
let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey))
@ -291,13 +291,13 @@ public final class SyncMessagesProtocol : NSObject {
let openGroups = syncMessage.openGroups
guard !openGroups.isEmpty else { return }
print("[Loki] Open group sync message received.")
let openGroupManager = LokiPublicChatManager.shared
let openGroupManager = PublicChatManager.shared
let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey()
let userDisplayName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: userPublicKey, transaction: transaction)
for openGroup in openGroups {
guard openGroupManager.getChat(server: openGroup.url, channel: openGroup.channelID) == nil else { return }
openGroupManager.addChat(server: openGroup.url, channel: openGroup.channelID)
LokiPublicChatAPI.setDisplayName(to: userDisplayName, on: openGroup.url)
PublicChatAPI.setDisplayName(to: userDisplayName, on: openGroup.url)
// TODO: Should we also set the profile picture here?
}
}

@ -6,7 +6,7 @@ public final class UserDisplayNameUtilities : NSObject {
override private init() { }
private static var userHexEncodedPublicKey: String {
private static var userPublicKey: String {
return getUserHexEncodedPublicKey()
}
@ -15,29 +15,29 @@ public final class UserDisplayNameUtilities : NSObject {
}
// MARK: Sessions
@objc public static func getPrivateChatDisplayName(for hexEncodedPublicKey: String) -> String? {
if hexEncodedPublicKey == userHexEncodedPublicKey {
@objc public static func getPrivateChatDisplayName(for publicKey: String) -> String? {
if publicKey == userPublicKey {
return userDisplayName
} else {
return SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: hexEncodedPublicKey)
return SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: publicKey)
}
}
// MARK: Open Groups
@objc public static func getPublicChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String) -> String? {
@objc public static func getPublicChatDisplayName(for publicKey: String, in channel: UInt64, on server: String) -> String? {
var result: String?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
result = getPublicChatDisplayName(for: hexEncodedPublicKey, in: channel, on: server, using: transaction)
result = getPublicChatDisplayName(for: publicKey, in: channel, on: server, using: transaction)
}
return result
}
@objc public static func getPublicChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String, using transaction: YapDatabaseReadTransaction) -> String? {
if hexEncodedPublicKey == userHexEncodedPublicKey {
@objc public static func getPublicChatDisplayName(for publicKey: String, in channel: UInt64, on server: String, using transaction: YapDatabaseReadTransaction) -> String? {
if publicKey == userPublicKey {
return userDisplayName
} else {
let collection = "\(server).\(channel)"
return transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String?
return transaction.object(forKey: publicKey, inCollection: collection) as! String?
}
}
}

@ -1078,8 +1078,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}];
}
NSString *body = (message.body != nil && message.body.length > 0) ? message.body : [NSString stringWithFormat:@"%@", @(message.timestamp)]; // Workaround for the fact that the back-end doesn't accept messages without a body
LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userPublicKey displayName:displayName body:body type:LKPublicChatAPI.publicChatMessageType
timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteeHexEncodedPublicKey:quoteePublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0];
LKPublicChatMessage *groupMessage = [[LKPublicChatMessage alloc] initWithSenderPublicKey:userPublicKey displayName:displayName body:body type:LKPublicChatAPI.publicChatMessageType
timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteePublicKey:quoteePublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0];
OWSLinkPreview *linkPreview = message.linkPreview;
if (linkPreview != nil) {
TSAttachmentStream *attachment = [TSAttachmentStream fetchObjectWithUniqueID:linkPreview.imageAttachmentId];
@ -1096,7 +1096,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
message.actualSenderHexEncodedPublicKey = userPublicKey;
[[LKPublicChatAPI sendMessage:groupMessage toGroup:publicChat.channel onServer:publicChat.server]
.thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) {
.thenOn(OWSDispatch.sendingQueue, ^(LKPublicChatMessage *groupMessage) {
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message saveOpenGroupServerMessageID:groupMessage.serverID in:transaction];
[self.primaryStorage setIDForMessageWithServerID:groupMessage.serverID to:message.uniqueId in:transaction];

Loading…
Cancel
Save