Parse group attachments

This commit is contained in:
Niels Andriesse 2019-10-21 09:32:28 +11:00
parent b05c890ab9
commit 9eea1a3a83
9 changed files with 60 additions and 25 deletions

View File

@ -9,7 +9,8 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
private static let maxRetryCount: UInt = 8
// MARK: Public Chat
@objc private static let channelInfoType = "net.patter-app.settings"
private static let channelInfoType = "net.patter-app.settings"
private static let attachmentType = "net.app.core.oembed"
@objc public static let publicChatMessageType = "network.loki.messenger.publicChat"
@objc public static let defaultChats: [LokiPublicChat] = {
@ -90,7 +91,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
return rawMessages.flatMap { message in
let isDeleted = (message["is_deleted"] as? Int == 1)
guard !isDeleted else { return nil }
guard let annotations = message["annotations"] as? [JSON], let annotation = annotations.first, let value = annotation["value"] as? JSON,
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 timestamp = value["timestamp"] as? UInt64 else {
@ -108,7 +109,13 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
quote = nil
}
let signature = LokiPublicChatMessage.Signature(data: Data(hex: hexEncodedSignatureData), version: signatureVersion)
let result = LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: quote, attachments: [], signature: signature)
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 server = value["server"] as? String, let serverID = value["id"] as? UInt64, let contentType = value["contentType"] as? String, let size = value["size"] as? UInt, let fileName = value["fileName"] as? String, let flags = value["flags"] as? UInt, let width = value["width"] as? UInt, let height = value["height"] as? UInt, let url = value["url"] as? String else { return nil }
let caption = value["caption"] as? String
return LokiPublicChatMessage.Attachment(server: server, serverID: serverID, contentType: contentType, size: size, fileName: fileName, flags: flags, width: width, height: height, caption: caption, url: url)
}
let result = LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, 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.")
return nil

View File

@ -29,14 +29,16 @@ public final class LokiPublicChatMessage : NSObject {
}
public struct Attachment {
public let server: String
public let serverID: UInt64
public let kind: Kind
public let contentType: String
public let size: UInt
public let fileName: String
public let flags: UInt
public let width: UInt
public let height: UInt
public let caption: String?
public let url: String
public let server: String
public let serverDisplayName: String
public enum Kind : String { case photo, video }
}
@ -111,10 +113,15 @@ public final class LokiPublicChatMessage : NSObject {
}
let annotation: JSON = [ "type" : type, "value" : value ]
let attachmentAnnotations: [JSON] = attachments.map { attachment in
var attachmentValue: JSON = [ "version" : 1, "type" : attachment.kind.rawValue, "width" : attachment.width, "height" : attachment.height,
"url" : attachment.url, "provider_name" : attachment.serverDisplayName, "provider_url" : attachment.server ]
let type = attachment.contentType.hasPrefix("image") ? "photo" : "video" // TODO: We should do better than this
var attachmentValue: JSON = [
// Field required by the .NET API
"version" : 1, "type" : type,
// Custom fields
"server" : attachment.server, "id" : attachment.serverID, "contentType" : attachment.contentType, "size" : attachment.size, "fileName" : attachment.fileName, "flags" : attachment.flags, "width" : attachment.width, "height" : attachment.height, "url" : attachment.url
]
if let caption = attachment.caption {
attachmentValue["title"] = attachment.caption
attachmentValue["caption"] = attachment.caption
}
return [ "type" : attachmentType, "value" : attachmentValue ]
}
@ -126,9 +133,8 @@ public final class LokiPublicChatMessage : NSObject {
}
// MARK: Convenience
@objc public func addAttachment(serverID: UInt64, kind: String, width: UInt, height: UInt, caption: String?, url: String, server: String, serverDisplayName: String) {
guard let kind = Attachment.Kind(rawValue: kind) else { preconditionFailure() }
let attachment = Attachment(serverID: serverID, kind: kind, width: width, height: height, caption: caption, url: url, server: server, serverDisplayName: serverDisplayName)
@objc public func addAttachment(server: String, serverID: UInt64, contentType: String, size: UInt, fileName: String, flags: UInt, width: UInt, height: UInt, caption: String?, url: String) {
let attachment = Attachment(server: server, serverID: serverID, contentType: contentType, size: size, fileName: fileName, flags: flags, width: width, height: height, caption: caption, url: url)
attachments.append(attachment)
}

View File

@ -54,6 +54,21 @@ public final class LokiPublicChatPoller : NSObject {
let groupContext = SSKProtoGroupContext.builder(id: id, type: .deliver)
groupContext.setName(publicChat.displayName)
let dataMessage = SSKProtoDataMessage.builder()
let attachments: [SSKProtoAttachmentPointer] = message.attachments.map { attachment in
let result = SSKProtoAttachmentPointer.builder(id: attachment.serverID)
result.setContentType(attachment.contentType)
result.setSize(UInt32(attachment.size))
result.setFileName(attachment.fileName)
result.setFlags(UInt32(attachment.flags))
result.setWidth(UInt32(attachment.width))
result.setHeight(UInt32(attachment.height))
if let caption = attachment.caption {
result.setCaption(caption)
}
result.setUrl(attachment.url)
return try! result.build()
}
dataMessage.setAttachments(attachments)
dataMessage.setTimestamp(message.timestamp)
dataMessage.setGroup(try! groupContext.build())
if let quote = message.quote {

View File

@ -425,11 +425,16 @@ typedef void (^AttachmentDownloadFailure)(NSError *error);
OWSAssertDebug(attachmentPointer);
NSError *decryptError;
NSData *_Nullable plaintext = [Cryptography decryptAttachment:cipherText
withKey:attachmentPointer.encryptionKey
digest:attachmentPointer.digest
unpaddedSize:attachmentPointer.byteCount
error:&decryptError];
NSData *_Nullable plaintext;
if (attachmentPointer.encryptionKey != nil) {
plaintext = [Cryptography decryptAttachment:cipherText
withKey:attachmentPointer.encryptionKey
digest:attachmentPointer.digest
unpaddedSize:attachmentPointer.byteCount
error:&decryptError];
} else {
plaintext = cipherText;
}
if (decryptError) {
OWSLogError(@"failed to decrypt with error: %@", decryptError);

View File

@ -57,7 +57,7 @@ typedef NS_ENUM(NSUInteger, TSAttachmentType) {
// This constructor is used for new instances of TSAttachmentPointer,
// i.e. undownloaded incoming attachments.
- (instancetype)initWithServerId:(UInt64)serverId
encryptionKey:(NSData *)encryptionKey
encryptionKey:(nullable NSData *)encryptionKey
byteCount:(UInt32)byteCount
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename

View File

@ -28,7 +28,7 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
// This constructor is used for new instances of TSAttachmentPointer,
// i.e. undownloaded incoming attachments.
- (instancetype)initWithServerId:(UInt64)serverId
encryptionKey:(NSData *)encryptionKey
encryptionKey:(nullable NSData *)encryptionKey
byteCount:(UInt32)byteCount
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
@ -36,7 +36,7 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
albumMessageId:(nullable NSString *)albumMessageId
{
OWSAssertDebug(serverId > 0);
OWSAssertDebug(encryptionKey.length > 0);
// OWSAssertDebug(encryptionKey.length > 0);
if (byteCount <= 0) {
// This will fail with legacy iOS clients which don't upload attachment size.
OWSLogWarn(@"Missing byteCount for attachment with serverId: %lld", serverId);
@ -138,7 +138,7 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
{
if (!pointer.lazyRestoreFragment) {
OWSAssertDebug(pointer.serverId > 0);
OWSAssertDebug(pointer.encryptionKey.length > 0);
// OWSAssertDebug(pointer.encryptionKey.length > 0);
if (pointer.byteCount <= 0) {
// This will fail with legacy iOS clients which don't upload attachment size.
OWSLogWarn(@"Missing pointer.byteCount for attachment with serverId: %lld", pointer.serverId);

View File

@ -44,7 +44,7 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithServerId:(UInt64)serverId
key:(NSData *)key
key:(nullable NSData *)key
digest:(nullable NSData *)digest
byteCount:(UInt32)byteCount
contentType:(NSString *)contentType

View File

@ -54,7 +54,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (instancetype)initWithServerId:(UInt64)serverId
key:(NSData *)key
key:(nullable NSData *)key
digest:(nullable NSData *)digest
byteCount:(UInt32)byteCount
contentType:(NSString *)contentType
@ -112,10 +112,12 @@ NS_ASSUME_NONNULL_BEGIN
OWSFailDebug(@"Invalid attachment id.");
return nil;
}
/*
if (attachmentProto.key.length < 1) {
OWSFailDebug(@"Invalid attachment key.");
return nil;
}
*/
NSString *_Nullable fileName = attachmentProto.fileName;
NSString *_Nullable contentType = attachmentProto.contentType;
if (contentType.length < 1) {

View File

@ -1208,13 +1208,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
quotedMessageServerID = [LKDatabaseUtilities getServerIDForQuoteWithID:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey threadID:messageSend.thread.uniqueId transaction:transaction];
}];
}
LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:message.body type:LKPublicChatAPI.publicChatMessageType
LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:@"Test" type:LKPublicChatAPI.publicChatMessageType
timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0];
for (NSString *attachmentID in message.attachmentIds) {
TSAttachmentStream *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentID];
if (attachment == nil) { continue; }
// TODO: Videos
[groupMessage addAttachmentWithServerID:attachment.serverId kind:@"photo" width:@(attachment.imageSize.width).unsignedIntegerValue height:@(attachment.imageSize.height).unsignedIntegerValue caption:attachment.caption url:attachment.downloadURL server:publicChat.server serverDisplayName:publicChat.displayName];
[groupMessage addAttachmentWithServer:publicChat.server serverID:attachment.serverId contentType:attachment.contentType size:attachment.byteCount fileName:attachment.sourceFilename flags:0 width:@(attachment.imageSize.width).unsignedIntegerValue height:@(attachment.imageSize.height).unsignedIntegerValue caption:attachment.caption url:attachment.downloadURL];
}
[[LKPublicChatAPI sendMessage:groupMessage toGroup:publicChat.channel onServer:publicChat.server]
.thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) {