Merge pull request #355 from oxen-io/multi-device

Sync Contacts
pull/356/head
Niels Andriesse 5 years ago committed by GitHub
commit b2574b19c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,7 @@ public final class ConfigurationMessage : ControlMessage {
public var displayName: String?
public var profilePictureURL: String?
public var profileKey: Data?
public var contacts: Set<Contact> = []
public override var ttl: UInt64 { 4 * 24 * 60 * 60 * 1000 }
@ -15,13 +16,14 @@ public final class ConfigurationMessage : ControlMessage {
// MARK: Initialization
public override init() { super.init() }
public init(displayName: String?, profilePictureURL: String?, profileKey: Data?, closedGroups: Set<ClosedGroup>, openGroups: Set<String>) {
public init(displayName: String?, profilePictureURL: String?, profileKey: Data?, closedGroups: Set<ClosedGroup>, openGroups: Set<String>, contacts: Set<Contact>) {
super.init()
self.displayName = displayName
self.profilePictureURL = profilePictureURL
self.profileKey = profileKey
self.closedGroups = closedGroups
self.openGroups = openGroups
self.contacts = contacts
}
// MARK: Coding
@ -32,6 +34,7 @@ public final class ConfigurationMessage : ControlMessage {
if let displayName = coder.decodeObject(forKey: "displayName") as! String? { self.displayName = displayName }
if let profilePictureURL = coder.decodeObject(forKey: "profilePictureURL") as! String? { self.profilePictureURL = profilePictureURL }
if let profileKey = coder.decodeObject(forKey: "profileKey") as! Data? { self.profileKey = profileKey }
if let contacts = coder.decodeObject(forKey: "contacts") as! Set<Contact>? { self.contacts = contacts }
}
public override func encode(with coder: NSCoder) {
@ -41,6 +44,7 @@ public final class ConfigurationMessage : ControlMessage {
coder.encode(displayName, forKey: "displayName")
coder.encode(profilePictureURL, forKey: "profilePictureURL")
coder.encode(profileKey, forKey: "profileKey")
coder.encode(contacts, forKey: "contacts")
}
// MARK: Proto Conversion
@ -51,7 +55,9 @@ public final class ConfigurationMessage : ControlMessage {
let profileKey = configurationProto.profileKey
let closedGroups = Set(configurationProto.closedGroups.compactMap { ClosedGroup.fromProto($0) })
let openGroups = Set(configurationProto.openGroups)
return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, closedGroups: closedGroups, openGroups: openGroups)
let contacts = Set(configurationProto.contacts.compactMap { Contact.fromProto($0) })
return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey,
closedGroups: closedGroups, openGroups: openGroups, contacts: contacts)
}
public override func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? {
@ -61,6 +67,7 @@ public final class ConfigurationMessage : ControlMessage {
if let profileKey = profileKey { configurationProto.setProfileKey(profileKey) }
configurationProto.setClosedGroups(closedGroups.compactMap { $0.toProto() })
configurationProto.setOpenGroups([String](openGroups))
configurationProto.setContacts(contacts.compactMap { $0.toProto() })
let contentProto = SNProtoContent.builder()
do {
contentProto.setConfigurationMessage(try configurationProto.build())
@ -77,6 +84,10 @@ public final class ConfigurationMessage : ControlMessage {
ConfigurationMessage(
closedGroups: \([ClosedGroup](closedGroups).prettifiedDescription)
openGroups: \([String](openGroups).prettifiedDescription)
displayName: \(displayName ?? "null")
profilePictureURL: \(profilePictureURL ?? "null")
profileKey: \(profileKey?.toHexString() ?? "null")
contacts: \([Contact](contacts).prettifiedDescription)
)
"""
}
@ -137,10 +148,13 @@ extension ConfigurationMessage {
}
let members = Set(proto.members.map { $0.toHexString() })
let admins = Set(proto.admins.map { $0.toHexString() })
return ClosedGroup(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins)
let result = ClosedGroup(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins)
guard result.isValid else { return nil }
return result
}
public func toProto() -> SNProtoConfigurationMessageClosedGroup? {
guard isValid else { return nil }
let result = SNProtoConfigurationMessageClosedGroup.builder()
result.setPublicKey(Data(hex: publicKey))
result.setName(name)
@ -164,3 +178,66 @@ extension ConfigurationMessage {
public override var description: String { name }
}
}
// MARK: Contact
extension ConfigurationMessage {
@objc(SNConfigurationMessageContact)
public final class Contact : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
public var publicKey: String?
public var displayName: String?
public var profilePictureURL: String?
public var profileKey: Data?
public var isValid: Bool { publicKey != nil && displayName != nil }
public init(publicKey: String, displayName: String, profilePictureURL: String?, profileKey: Data?) {
self.publicKey = publicKey
self.displayName = displayName
self.profilePictureURL = profilePictureURL
self.profileKey = profileKey
}
public required init?(coder: NSCoder) {
guard let publicKey = coder.decodeObject(forKey: "publicKey") as! String?,
let displayName = coder.decodeObject(forKey: "displayName") as! String? else { return nil }
self.publicKey = publicKey
self.displayName = displayName
self.profilePictureURL = coder.decodeObject(forKey: "profilePictureURL") as! String?
self.profileKey = coder.decodeObject(forKey: "profileKey") as! Data?
}
public func encode(with coder: NSCoder) {
coder.encode(publicKey, forKey: "publicKey")
coder.encode(displayName, forKey: "displayName")
coder.encode(profilePictureURL, forKey: "profilePictureURL")
coder.encode(profileKey, forKey: "profileKey")
}
public static func fromProto(_ proto: SNProtoConfigurationMessageContact) -> Contact? {
let publicKey = proto.publicKey.toHexString()
let displayName = proto.name
let profilePictureURL = proto.profilePicture
let profileKey = proto.profileKey
let result = Contact(publicKey: publicKey, displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey)
guard result.isValid else { return nil }
return result
}
public func toProto() -> SNProtoConfigurationMessageContact? {
guard isValid else { return nil }
guard let publicKey = publicKey, let displayName = displayName else { return nil }
let result = SNProtoConfigurationMessageContact.builder(publicKey: Data(hex: publicKey), name: displayName)
if let profilePictureURL = profilePictureURL { result.setProfilePicture(profilePictureURL) }
if let profileKey = profileKey { result.setProfileKey(profileKey) }
do {
return try result.build()
} catch {
SNLog("Couldn't construct contact proto from: \(self).")
return nil
}
}
public override var description: String { displayName! }
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,9 +1,10 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: WebSocketResources.proto
//
// For information on using the generated types, please see the documenation:
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
//*
@ -20,7 +21,7 @@ import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
@ -145,31 +146,31 @@ struct WebSocketProtos_WebSocketMessage {
/// @required
var type: WebSocketProtos_WebSocketMessage.TypeEnum {
get {return _storage._type ?? .unknown}
set {_uniqueStorage()._type = newValue}
get {return _type ?? .unknown}
set {_type = newValue}
}
/// Returns true if `type` has been explicitly set.
var hasType: Bool {return _storage._type != nil}
var hasType: Bool {return self._type != nil}
/// Clears the value of `type`. Subsequent reads from it will return its default value.
mutating func clearType() {_uniqueStorage()._type = nil}
mutating func clearType() {self._type = nil}
var request: WebSocketProtos_WebSocketRequestMessage {
get {return _storage._request ?? WebSocketProtos_WebSocketRequestMessage()}
set {_uniqueStorage()._request = newValue}
get {return _request ?? WebSocketProtos_WebSocketRequestMessage()}
set {_request = newValue}
}
/// Returns true if `request` has been explicitly set.
var hasRequest: Bool {return _storage._request != nil}
var hasRequest: Bool {return self._request != nil}
/// Clears the value of `request`. Subsequent reads from it will return its default value.
mutating func clearRequest() {_uniqueStorage()._request = nil}
mutating func clearRequest() {self._request = nil}
var response: WebSocketProtos_WebSocketResponseMessage {
get {return _storage._response ?? WebSocketProtos_WebSocketResponseMessage()}
set {_uniqueStorage()._response = newValue}
get {return _response ?? WebSocketProtos_WebSocketResponseMessage()}
set {_response = newValue}
}
/// Returns true if `response` has been explicitly set.
var hasResponse: Bool {return _storage._response != nil}
var hasResponse: Bool {return self._response != nil}
/// Clears the value of `response`. Subsequent reads from it will return its default value.
mutating func clearResponse() {_uniqueStorage()._response = nil}
mutating func clearResponse() {self._response = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
@ -204,7 +205,9 @@ struct WebSocketProtos_WebSocketMessage {
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
fileprivate var _type: WebSocketProtos_WebSocketMessage.TypeEnum? = nil
fileprivate var _request: WebSocketProtos_WebSocketRequestMessage? = nil
fileprivate var _response: WebSocketProtos_WebSocketResponseMessage? = nil
}
#if swift(>=4.2)
@ -333,70 +336,34 @@ extension WebSocketProtos_WebSocketMessage: SwiftProtobuf.Message, SwiftProtobuf
3: .same(proto: "response"),
]
fileprivate class _StorageClass {
var _type: WebSocketProtos_WebSocketMessage.TypeEnum? = nil
var _request: WebSocketProtos_WebSocketRequestMessage? = nil
var _response: WebSocketProtos_WebSocketResponseMessage? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_type = source._type
_request = source._request
_response = source._response
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularEnumField(value: &_storage._type)
case 2: try decoder.decodeSingularMessageField(value: &_storage._request)
case 3: try decoder.decodeSingularMessageField(value: &_storage._response)
default: break
}
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularEnumField(value: &self._type)
case 2: try decoder.decodeSingularMessageField(value: &self._request)
case 3: try decoder.decodeSingularMessageField(value: &self._response)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._type {
try visitor.visitSingularEnumField(value: v, fieldNumber: 1)
}
if let v = _storage._request {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
if let v = _storage._response {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}
if let v = self._type {
try visitor.visitSingularEnumField(value: v, fieldNumber: 1)
}
if let v = self._request {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
if let v = self._response {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: WebSocketProtos_WebSocketMessage, rhs: WebSocketProtos_WebSocketMessage) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._type != rhs_storage._type {return false}
if _storage._request != rhs_storage._request {return false}
if _storage._response != rhs_storage._response {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs._type != rhs._type {return false}
if lhs._request != rhs._request {return false}
if lhs._response != rhs._response {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

@ -209,7 +209,6 @@ message DataMessage {
optional LokiProfile profile = 101;
optional ClosedGroupControlMessage closedGroupControlMessage = 104;
optional string syncTarget = 105;
optional PublicChatInfo publicChatInfo = 999;
}
message ConfigurationMessage {
@ -222,11 +221,21 @@ message ConfigurationMessage {
repeated bytes admins = 5;
}
message Contact {
// @required
required bytes publicKey = 1;
// @required
required string name = 2;
optional string profilePicture = 3;
optional bytes profileKey = 4;
}
repeated ClosedGroup closedGroups = 1;
repeated string openGroups = 2;
optional string displayName = 3;
optional string profilePicture = 4;
optional bytes profileKey = 5;
repeated Contact contacts = 6;
}
message ReceiptMessage {
@ -262,26 +271,6 @@ message AttachmentPointer {
optional string url = 101;
}
message GroupContext {
enum Type {
UNKNOWN = 0;
UPDATE = 1;
DELIVER = 2;
QUIT = 3;
REQUEST_INFO = 4;
}
// @required
optional bytes id = 1;
// @required
optional Type type = 2;
optional string name = 3;
repeated string members = 4;
optional AttachmentPointer avatar = 5;
repeated string admins = 6;
}
message ContactDetails {
message Avatar {
@ -300,25 +289,24 @@ message ContactDetails {
optional string nickname = 101;
}
message GroupDetails {
// -------- Deprecated --------
message Avatar {
optional string contentType = 1;
optional uint32 length = 2;
message GroupContext {
enum Type {
UNKNOWN = 0;
UPDATE = 1;
DELIVER = 2;
QUIT = 3;
REQUEST_INFO = 4;
}
// @required
optional bytes id = 1;
optional string name = 2;
repeated string members = 3;
optional Avatar avatar = 4;
optional bool active = 5 [default = true];
optional uint32 expireTimer = 6;
optional string color = 7;
optional bool blocked = 8;
repeated string admins = 9;
}
message PublicChatInfo { // Intended for internal use only
optional uint64 serverID = 1;
optional bytes id = 1;
// @required
optional Type type = 2;
optional string name = 3;
repeated string members = 4;
optional AttachmentPointer avatar = 5;
repeated string admins = 6;
}

@ -135,12 +135,6 @@ public final class OpenGroupPoller : NSObject {
dataMessageProto.setProfileKey(profilePicture.profileKey)
}
dataMessageProto.setProfile(try! profileProto.build())
// Open group info
if let messageServerID = message.serverID {
let openGroupProto = SNProtoPublicChatInfo.builder()
openGroupProto.setServerID(messageServerID)
dataMessageProto.setPublicChatInfo(try! openGroupProto.build())
}
// Signal group context
let groupProto = SNProtoGroupContext.builder(id: id, type: .deliver)
groupProto.setName(openGroup.displayName)

@ -8,6 +8,8 @@ extension ConfigurationMessage {
let profileKey = storage.getUserProfileKey()
var closedGroups: Set<ClosedGroup> = []
var openGroups: Set<String> = []
var contacts: Set<Contact> = []
var contactCount = 0
Storage.read { transaction in
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
guard let thread = object as? TSGroupThread else { return }
@ -27,7 +29,21 @@ extension ConfigurationMessage {
default: break
}
}
OWSUserProfile.enumerateCollectionObjects(with: transaction) { object, stop in
guard let profile = object as? OWSUserProfile, let displayName = profile.profileName else { return }
let publicKey = profile.recipientId
let threadID = TSContactThread.threadId(fromContactId: publicKey)
guard let thread = TSContactThread.fetch(uniqueId: threadID, transaction: transaction), thread.shouldThreadBeVisible else { return }
let profilePictureURL = profile.avatarUrlPath
let profileKey = profile.profileKey?.keyData
let contact = ConfigurationMessage.Contact(publicKey: publicKey, displayName: displayName,
profilePictureURL: profilePictureURL, profileKey: profileKey)
contacts.insert(contact)
guard contactCount < 200 else { stop.pointee = true; return }
contactCount += 1
}
}
return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, closedGroups: closedGroups, openGroups: openGroups)
return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey,
closedGroups: closedGroups, openGroups: openGroups, contacts: contacts)
}
}

Loading…
Cancel
Save