Browse Source

Implement attachment downloading

pull/313/head
nielsandriesse 2 years ago
parent
commit
b218a16b05
  1. 4
      Podfile
  2. 2
      Podfile.lock
  3. 4
      Session/Meta/Signal-Bridging-Header.h
  4. 2
      Session/Signal/ConversationView/Cells/AttachmentUploadView.m
  5. 2
      Session/Signal/ConversationView/Cells/OWSGenericAttachmentView.m
  6. 2
      Session/Signal/ConversationView/ConversationViewController.m
  7. 2
      Session/Signal/MainAppContext.h
  8. 2
      Session/Signal/OWSBackupExportJob.m
  9. 2
      Session/Signal/OWSBackupIO.m
  10. 2
      Session/Signal/OWSBackupImportJob.m
  11. 2
      Session/Signal/OWSBackupSettingsViewController.m
  12. 2
      Session/Signal/OWSOrphanDataCleaner.m
  13. 68
      SessionMessagingKit/Jobs/AttachmentDownloadJob.swift
  14. 6
      SessionMessagingKit/Jobs/JobQueue.swift
  15. 0
      SessionMessagingKit/Messages/Control Messages/ClosedGroupUpdate.swift
  16. 0
      SessionMessagingKit/Messages/Control Messages/ControlMessage.swift
  17. 0
      SessionMessagingKit/Messages/Control Messages/ExpirationTimerUpdate.swift
  18. 0
      SessionMessagingKit/Messages/Control Messages/ReadReceipt.swift
  19. 0
      SessionMessagingKit/Messages/Control Messages/TypingIndicator.swift
  20. 0
      SessionMessagingKit/Messages/Control Messages/Unused/NullMessage.swift
  21. 0
      SessionMessagingKit/Messages/Control Messages/Unused/SessionRequest.swift
  22. 10
      SessionMessagingKit/Messages/Visible Messages/Attachments/OWSThumbnailService.swift
  23. 8
      SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachment.h
  24. 48
      SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachment.m
  25. 15
      SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentPointer.h
  26. 86
      SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentPointer.m
  27. 10
      SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentStream.h
  28. 86
      SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentStream.m
  29. 31
      SessionMessagingKit/Messages/Visible Messages/Attachments/VisibleMessage+Attachment.swift
  30. 0
      SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Contact.swift
  31. 0
      SessionMessagingKit/Messages/Visible Messages/VisibleMessage+LinkPreview.swift
  32. 0
      SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Profile.swift
  33. 0
      SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Quote.swift
  34. 0
      SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift
  35. 4
      SessionMessagingKit/Meta/SessionMessagingKit.h
  36. 30
      SessionMessagingKit/Sending & Receiving/MessageReceiver.swift
  37. 1
      SessionMessagingKit/Sending & Receiving/MessageReceiverDelegate.swift
  38. 6
      SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift
  39. 7
      SessionMessagingKit/Sending & Receiving/MessageSender.swift
  40. 1
      SessionMessagingKit/Sending & Receiving/MessageSenderDelegate.swift
  41. 12
      SessionMessagingKit/Utilities/AttachmentStream.swift
  42. 4
      SessionMessagingKit/Utilities/DotNetAPI.swift
  43. 2
      SessionShareExtension/Meta/SignalShareExtension-Bridging-Header.h
  44. 2
      SessionShareExtension/SAEScreenLockViewController.m
  45. 2
      SessionShareExtension/ShareAppExtensionContext.h
  46. 4
      SessionUtilitiesKit/AppContext.h
  47. 10
      SessionUtilitiesKit/AppContext.m
  48. 20
      SessionUtilitiesKit/Configuration.swift
  49. 4
      SessionUtilitiesKit/DataSource.h
  50. 69
      SessionUtilitiesKit/DataSource.m
  51. 4
      SessionUtilitiesKit/MIMETypeUtil.h
  52. 15
      SessionUtilitiesKit/MIMETypeUtil.m
  53. 8
      SessionUtilitiesKit/Meta/SessionUtilitiesKit.h
  54. 4
      SessionUtilitiesKit/NSData+Image.h
  55. 28
      SessionUtilitiesKit/NSData+Image.m
  56. 6
      SessionUtilitiesKit/OWSFileSystem.h
  57. 99
      SessionUtilitiesKit/OWSFileSystem.m
  58. 32
      SessionUtilitiesKit/OWSMediaUtils.swift
  59. 7
      SessionUtilitiesKit/OWSPrimaryStorageProtocol.swift
  60. 5
      SessionUtilitiesKit/Storage.swift
  61. 5
      SessionUtilitiesKit/TSYapDatabaseObject.h
  62. 25
      SessionUtilitiesKit/TSYapDatabaseObject.m
  63. 4
      SessionUtilitiesKit/UIImage+OWS.h
  64. 22
      SessionUtilitiesKit/UIImage+OWS.m
  65. 192
      Signal.xcodeproj/project.pbxproj
  66. 2
      SignalUtilitiesKit/Database/Migration/OWSDatabaseMigrationRunner.m
  67. 2
      SignalUtilitiesKit/Database/Storage/Storage+Conformances.swift
  68. 2
      SignalUtilitiesKit/Database/Utilities/ThreadViewHelper.m
  69. 2
      SignalUtilitiesKit/Messaging/Quotes/OWSQuotedReplyModel.m
  70. 36
      SignalUtilitiesKit/Messaging/Sending & Receiving/MessageReceiverDelegate.swift
  71. 4
      SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSenderDelegate.swift
  72. 3
      SignalUtilitiesKit/Meta/SignalUtilitiesKit.h
  73. 2
      SignalUtilitiesKit/OWSPreferences.m
  74. 2
      SignalUtilitiesKit/OWSSounds.m
  75. 6
      SignalUtilitiesKit/To Do/OWSProfileManager.m
  76. 4
      SignalUtilitiesKit/To Do/OWSUserProfile.m
  77. 2
      SignalUtilitiesKit/UI/SelectRecipientViewController.m
  78. 2
      SignalUtilitiesKit/UI/UIUtil.h
  79. 2
      SignalUtilitiesKit/UI/UIUtil.m
  80. 2
      SignalUtilitiesKit/UI/UIView+OWS.m
  81. 2
      SignalUtilitiesKit/UI/UIViewController+OWS.m
  82. 2
      SignalUtilitiesKit/Utilities/AttachmentSharing.m
  83. 4
      SignalUtilitiesKit/Utilities/DebugLogger.m
  84. 2
      SignalUtilitiesKit/Utilities/NSAttributedString+OWS.m
  85. 2
      SignalUtilitiesKit/Utilities/SSKAsserts.h
  86. 2
      SignalUtilitiesKit/VersionMigrations.m

4
Podfile

@ -69,9 +69,11 @@ target 'SessionMessagingKit' do
pod 'AFNetworking', inhibit_warnings: true
pod 'CryptoSwift', :inhibit_warnings => true
pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit.git', :inhibit_warnings => true
pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master', :inhibit_warnings => true
pod 'PromiseKit', :inhibit_warnings => true
pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true
pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/signalapp/YapDatabase.git', branch: 'signal-release', :inhibit_warnings => true
end
target 'SessionProtocolKit' do
@ -95,8 +97,10 @@ end
target 'SessionUtilitiesKit' do
pod 'CryptoSwift', :inhibit_warnings => true
pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit.git', :inhibit_warnings => true
pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master', :inhibit_warnings => true
pod 'PromiseKit', :inhibit_warnings => true
pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/signalapp/YapDatabase.git', branch: 'signal-release', :inhibit_warnings => true
end
post_install do |installer|

2
Podfile.lock

@ -230,6 +230,6 @@ SPEC CHECKSUMS:
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 3489ed70ea51f2bf705bf99703efc71d697de373
PODFILE CHECKSUM: 62df79698293257648cb6e60724f720f8477bd0f
COCOAPODS: 1.10.0.rc.1

4
Session/Meta/Signal-Bridging-Header.h

@ -64,7 +64,7 @@
#import <SignalUtilitiesKit/UIViewController+OWS.h>
#import <SignalUtilitiesKit/AppVersion.h>
#import <SignalUtilitiesKit/DataSource.h>
#import <SignalUtilitiesKit/MIMETypeUtil.h>
#import <SessionUtilitiesKit/MIMETypeUtil.h>
#import <SignalUtilitiesKit/NSData+Image.h>
#import <SignalUtilitiesKit/NSNotificationCenter+OWS.h>
#import <SignalUtilitiesKit/NSString+SSK.h>
@ -72,7 +72,7 @@
#import <SignalUtilitiesKit/OWSContactsOutputStream.h>
#import <SignalUtilitiesKit/OWSDispatch.h>
#import <SignalUtilitiesKit/OWSError.h>
#import <SignalUtilitiesKit/OWSFileSystem.h>
#import <SessionUtilitiesKit/OWSFileSystem.h>
#import <SignalUtilitiesKit/OWSIdentityManager.h>
#import <SignalUtilitiesKit/OWSMediaGalleryFinder.h>
#import <SignalUtilitiesKit/OWSPrimaryStorage+SessionStore.h>

2
Session/Signal/ConversationView/Cells/AttachmentUploadView.m

@ -7,7 +7,7 @@
#import "OWSProgressView.h"
#import <SignalUtilitiesKit/UIFont+OWS.h>
#import <SignalUtilitiesKit/UIView+OWS.h>
#import <SignalUtilitiesKit/AppContext.h>
#import <SessionUtilitiesKit/AppContext.h>
#import <SignalUtilitiesKit/OWSUploadOperation.h>
#import <SignalUtilitiesKit/TSAttachmentStream.h>

2
Session/Signal/ConversationView/Cells/OWSGenericAttachmentView.m

@ -10,7 +10,7 @@
#import <SignalUtilitiesKit/OWSFormat.h>
#import <SignalUtilitiesKit/UIColor+OWS.h>
#import <SignalUtilitiesKit/MimeTypeUtil.h>
#import <SessionUtilitiesKit/MIMETypeUtil.h>
#import <SignalUtilitiesKit/NSString+SSK.h>
#import <SignalUtilitiesKit/TSAttachmentStream.h>
#import <SignalCoreKit/NSString+OWS.h>

2
Session/Signal/ConversationView/ConversationViewController.m

@ -50,7 +50,7 @@
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <SignalUtilitiesKit/UIUtil.h>
#import <SignalUtilitiesKit/UIViewController+OWS.h>
#import <SignalUtilitiesKit/MIMETypeUtil.h>
#import <SessionUtilitiesKit/MIMETypeUtil.h>
#import <SignalUtilitiesKit/NSString+SSK.h>
#import <SignalUtilitiesKit/OWSAttachmentDownloads.h>
#import <SignalUtilitiesKit/OWSBlockingManager.h>

2
Session/Signal/MainAppContext.h

@ -2,7 +2,7 @@
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/AppContext.h>
#import <SessionUtilitiesKit/AppContext.h>
NS_ASSUME_NONNULL_BEGIN

2
Session/Signal/OWSBackupExportJob.m

@ -12,7 +12,7 @@
#import <SignalCoreKit/Threading.h>
#import <SignalUtilitiesKit/OWSBackgroundTask.h>
#import <SignalUtilitiesKit/OWSError.h>
#import <SignalUtilitiesKit/OWSFileSystem.h>
#import <SessionUtilitiesKit/OWSFileSystem.h>
#import <SignalUtilitiesKit/TSAttachment.h>
#import <SignalUtilitiesKit/TSAttachmentStream.h>
#import <SignalUtilitiesKit/TSMessage.h>

2
Session/Signal/OWSBackupIO.m

@ -4,7 +4,7 @@
#import "OWSBackupIO.h"
#import <SignalCoreKit/Randomness.h>
#import <SignalUtilitiesKit/OWSFileSystem.h>
#import <SessionUtilitiesKit/OWSFileSystem.h>
@import Compression;

2
Session/Signal/OWSBackupImportJob.m

@ -10,7 +10,7 @@
#import <PromiseKit/AnyPromise.h>
#import <SignalCoreKit/NSData+OWS.h>
#import <SignalUtilitiesKit/OWSBackgroundTask.h>
#import <SignalUtilitiesKit/OWSFileSystem.h>
#import <SessionUtilitiesKit/OWSFileSystem.h>
#import <SignalUtilitiesKit/TSAttachment.h>
#import <SignalUtilitiesKit/TSMessage.h>
#import <SignalUtilitiesKit/TSThread.h>

2
Session/Signal/OWSBackupSettingsViewController.m

@ -13,7 +13,7 @@
#import <SignalUtilitiesKit/UIColor+OWS.h>
#import <SignalUtilitiesKit/UIFont+OWS.h>
#import <SignalUtilitiesKit/UIView+OWS.h>
#import <SignalUtilitiesKit/MIMETypeUtil.h>
#import <SessionUtilitiesKit/MIMETypeUtil.h>
NS_ASSUME_NONNULL_BEGIN

2
Session/Signal/OWSOrphanDataCleaner.m

@ -10,7 +10,7 @@
#import <SignalUtilitiesKit/AppReadiness.h>
#import <SignalUtilitiesKit/AppVersion.h>
#import <SignalUtilitiesKit/OWSFileSystem.h>
#import <SessionUtilitiesKit/OWSFileSystem.h>
#import <SignalUtilitiesKit/OWSPrimaryStorage.h>
#import <SignalUtilitiesKit/TSAttachmentStream.h>
#import <SignalUtilitiesKit/TSInteraction.h>

68
SessionMessagingKit/Jobs/AttachmentDownloadJob.swift

@ -1,29 +1,85 @@
import Foundation
import SessionUtilitiesKit
// TODO: Implementation
import SignalCoreKit
public final class AttachmentDownloadJob : NSObject, Job, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
public var delegate: JobDelegate?
private let attachmentID: String
public var id: String?
public var failureCount: UInt = 0
public enum Error : LocalizedError {
case noAttachment
public var errorDescription: String? {
switch self {
case .noAttachment: return "No such attachment."
}
}
}
// MARK: Settings
public class var collection: String { return "AttachmentDownloadJobCollection" }
public static let maxFailureCount: UInt = 20
// MARK: Initialization
public init(attachmentID: String) {
self.attachmentID = attachmentID
}
// MARK: Coding
public init?(coder: NSCoder) { }
public init?(coder: NSCoder) {
guard let attachmentID = coder.decodeObject(forKey: "attachmentID") as! String? else { return nil }
self.attachmentID = attachmentID
}
public func encode(with coder: NSCoder) { }
public func encode(with coder: NSCoder) {
coder.encode(attachmentID, forKey: "attachmentID")
}
// MARK: Running
public func execute() { }
public func execute() {
guard let pointer = TSAttachmentPointer.fetch(uniqueId: attachmentID) else {
return handleFailure(error: Error.noAttachment)
}
let temporaryFilePath = URL(fileURLWithPath: OWSTemporaryDirectoryAccessibleAfterFirstAuth() + UUID().uuidString)
FileServerAPI.downloadAttachment(from: pointer.downloadURL).done(on: DispatchQueue.global(qos: .userInitiated)) { data in // Intentionally capture self
do {
try data.write(to: temporaryFilePath, options: .atomic)
} catch {
return self.handleFailure(error: error)
}
let plaintext: Data
if let key = pointer.encryptionKey, let digest = pointer.digest {
do {
plaintext = try Cryptography.decryptAttachment(data, withKey: key, digest: digest, unpaddedSize: pointer.byteCount)
} catch {
return self.handleFailure(error: error)
}
} else {
plaintext = data // Open group attachments are unencrypted
}
let stream = TSAttachmentStream(pointer: pointer)
do {
try stream.write(plaintext)
} catch {
return self.handleFailure(error: error)
}
OWSFileSystem.deleteFile(temporaryFilePath.absoluteString)
Configuration.shared.storage.withAsync({ transaction in
stream.save(with: transaction as! YapDatabaseReadWriteTransaction)
// TODO: Update the message
}, completion: { })
}.catch(on: DispatchQueue.global()) { error in
self.handleFailure(error: error)
}
}
private func handleSuccess() {
delegate?.handleJobSucceeded(self)
}
private func handleFailure(error: Error) {
private func handleFailure(error: Swift.Error) {
delegate?.handleJobFailed(self, with: error)
}
}

6
SessionMessagingKit/Jobs/JobQueue.swift

@ -6,10 +6,14 @@ public final class JobQueue : NSObject, JobDelegate {
@objc public static let shared = JobQueue()
@objc public func add(_ job: Job, using transaction: Any) {
addWithoutExecuting(job, using: transaction)
job.execute()
}
@objc public func addWithoutExecuting(_ job: Job, using transaction: Any) {
job.id = String(NSDate.millisecondTimestamp())
Configuration.shared.storage.persist(job, using: transaction)
job.delegate = self
job.execute()
}
@objc public func resumePendingJobs() {

0
SessionMessagingKit/Messages/Control Message/ClosedGroupUpdate.swift → SessionMessagingKit/Messages/Control Messages/ClosedGroupUpdate.swift

0
SessionMessagingKit/Messages/Control Message/ControlMessage.swift → SessionMessagingKit/Messages/Control Messages/ControlMessage.swift

0
SessionMessagingKit/Messages/Control Message/ExpirationTimerUpdate.swift → SessionMessagingKit/Messages/Control Messages/ExpirationTimerUpdate.swift

0
SessionMessagingKit/Messages/Control Message/ReadReceipt.swift → SessionMessagingKit/Messages/Control Messages/ReadReceipt.swift

0
SessionMessagingKit/Messages/Control Message/TypingIndicator.swift → SessionMessagingKit/Messages/Control Messages/TypingIndicator.swift

0
SessionMessagingKit/Messages/Control Message/NullMessage.swift → SessionMessagingKit/Messages/Control Messages/Unused/NullMessage.swift

0
SessionMessagingKit/Messages/Control Message/SessionRequest.swift → SessionMessagingKit/Messages/Control Messages/Unused/SessionRequest.swift

10
SignalUtilitiesKit/OWSThumbnailService.swift → SessionMessagingKit/Messages/Visible Messages/Attachments/OWSThumbnailService.swift

@ -75,12 +75,6 @@ private struct OWSThumbnailRequest {
// arrive so that we prioritize the most recent view state.
private var thumbnailRequestStack = [OWSThumbnailRequest]()
private override init() {
super.init()
SwiftSingletons.register(self)
}
private func canThumbnailAttachment(attachment: TSAttachmentStream) -> Bool {
return attachment.isImage || attachment.isAnimated || attachment.isVideo
}
@ -117,8 +111,6 @@ private struct OWSThumbnailRequest {
thumbnailRequest.success(loadedThumbnail)
}
} catch {
Logger.error("Could not create thumbnail: \(error)")
DispatchQueue.global().async {
thumbnailRequest.failure(error)
}
@ -146,8 +138,6 @@ private struct OWSThumbnailRequest {
return OWSLoadedThumbnail(image: image, filePath: thumbnailPath)
}
Logger.verbose("Creating thumbnail of size: \(thumbnailRequest.thumbnailDimensionPoints)")
let thumbnailDirPath = (thumbnailPath as NSString).deletingLastPathComponent
guard OWSFileSystem.ensureDirectoryExists(thumbnailDirPath) else {
throw OWSThumbnailError.failure(description: "Could not create attachment's thumbnail directory.")

8
SignalUtilitiesKit/Messaging/Attachments/TSAttachment.h → SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachment.h

@ -1,13 +1,8 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/TSYapDatabaseObject.h>
#import <SessionUtilitiesKit/TSYapDatabaseObject.h>
NS_ASSUME_NONNULL_BEGIN
@class TSAttachmentPointer;
@class TSMessage;
typedef NS_ENUM(NSUInteger, TSAttachmentType) {
TSAttachmentTypeDefault = 0,
@ -45,7 +40,6 @@ typedef NS_ENUM(NSUInteger, TSAttachmentType) {
@property (nonatomic, readonly, nullable) NSString *caption;
@property (nonatomic, nullable) NSString *albumMessageId;
- (nullable TSMessage *)fetchAlbumMessageWithTransaction:(YapDatabaseReadTransaction *)transaction;
// `migrateAlbumMessageId` is only used in the migration to the new multi-attachment message scheme,
// and shouldn't be used as a general purpose setter. Instead, `albumMessageId` should be passed as

48
SignalUtilitiesKit/Messaging/Attachments/TSAttachment.m → SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachment.m

@ -1,13 +1,7 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "TSAttachment.h"
#import "MIMETypeUtil.h"
#import "NSString+SSK.h"
#import "TSAttachmentPointer.h"
#import "TSMessage.h"
#import <SessionProtocolKit/SessionProtocolKit.h>
#import <SignalCoreKit/NSString+OWS.h>
NS_ASSUME_NONNULL_BEGIN
@ -16,9 +10,7 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
@interface TSAttachment ()
@property (nonatomic, readonly) NSUInteger attachmentSchemaVersion;
@property (nonatomic, nullable) NSString *sourceFilename;
@property (nonatomic) NSString *contentType;
@end
@ -35,17 +27,9 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
caption:(nullable NSString *)caption
albumMessageId:(nullable NSString *)albumMessageId
{
OWSAssertDebug(serverId > 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);
}
if (contentType.length < 1) {
OWSLogWarn(@"incoming attachment has invalid content type");
contentType = OWSMimeTypeApplicationOctetStream;
}
OWSAssertDebug(contentType.length > 0);
self = [super init];
if (!self) {
@ -73,13 +57,9 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
caption:(nullable NSString *)caption
albumMessageId:(nullable NSString *)albumMessageId
{
OWSAssertDebug(uniqueId.length > 0);
if (contentType.length < 1) {
OWSLogWarn(@"incoming attachment has invalid content type");
contentType = OWSMimeTypeApplicationOctetStream;
}
OWSAssertDebug(contentType.length > 0);
// If saved, this AttachmentPointer would replace the AttachmentStream in the attachments collection.
// However we only use this AttachmentPointer should only be used during the export process so it
@ -108,17 +88,13 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
albumMessageId:(nullable NSString *)albumMessageId
{
if (contentType.length < 1) {
OWSLogWarn(@"outgoing attachment has invalid content type");
contentType = OWSMimeTypeApplicationOctetStream;
}
OWSAssertDebug(contentType.length > 0);
self = [super init];
if (!self) {
return self;
}
OWSLogVerbose(@"init attachment with uniqueId: %@", self.uniqueId);
_contentType = contentType;
_byteCount = byteCount;
@ -135,15 +111,6 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
// that represent downloaded incoming attachments.
- (instancetype)initWithPointer:(TSAttachmentPointer *)pointer
{
if (!pointer.lazyRestoreFragment) {
OWSAssertDebug(pointer.serverId > 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);
}
}
OWSAssertDebug(pointer.contentType.length > 0);
// Once saved, this AttachmentStream will replace the AttachmentPointer in the attachments collection.
self = [super initWithUniqueId:pointer.uniqueId];
if (!self) {
@ -156,8 +123,6 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
_sourceFilename = pointer.sourceFilename;
NSString *contentType = pointer.contentType;
if (contentType.length < 1) {
OWSLogWarn(@"incoming attachment has invalid content type");
contentType = OWSMimeTypeApplicationOctetStream;
}
_contentType = contentType;
@ -184,12 +149,9 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
if (!_sourceFilename) {
// renamed _filename to _sourceFilename
_sourceFilename = [coder decodeObjectForKey:@"filename"];
OWSAssertDebug(!_sourceFilename || [_sourceFilename isKindOfClass:[NSString class]]);
}
if (_contentType.length < 1) {
OWSLogWarn(@"legacy attachment has invalid content type");
_contentType = OWSMimeTypeApplicationOctetStream;
}
@ -284,14 +246,6 @@ NSUInteger const TSAttachmentSchemaVersion = 4;
#pragma mark - Relationships
- (nullable TSMessage *)fetchAlbumMessageWithTransaction:(YapDatabaseReadTransaction *)transaction
{
if (self.albumMessageId == nil) {
return nil;
}
return [TSMessage fetchObjectWithUniqueID:self.albumMessageId transaction:transaction];
}
- (void)migrateAlbumMessageId:(NSString *)albumMesssageId
{
_albumMessageId = albumMesssageId;

15
SignalUtilitiesKit/Messaging/Attachments/TSAttachmentPointer.h → SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentPointer.h

@ -1,8 +1,4 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/TSAttachment.h>
#import <SessionMessagingKit/TSAttachment.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@ -39,9 +35,6 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
@property (nonatomic, readonly) CGSize mediaSize;
// Non-nil for attachments which need "lazy backup restore."
- (nullable OWSBackupFragment *)lazyRestoreFragment;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithServerId:(UInt64)serverId
@ -64,12 +57,6 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
(NSArray<SNProtoAttachmentPointer *> *)attachmentProtos
albumMessage:(TSMessage *)message;
#pragma mark - Update With... Methods
// Marks attachment as needing "lazy backup restore."
- (void)markForLazyRestoreWithFragment:(OWSBackupFragment *)lazyRestoreFragment
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

86
SignalUtilitiesKit/Messaging/Attachments/TSAttachmentPointer.m → SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentPointer.m

@ -1,14 +1,9 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "TSAttachmentPointer.h"
#import "OWSBackupFragment.h"
#import "TSAttachmentStream.h"
#import <SignalUtilitiesKit/MIMETypeUtil.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <SessionUtilitiesKit/MIMETypeUtil.h>
#import <YapDatabase/YapDatabase.h>
#import <YapDatabase/YapDatabaseTransaction.h>
#import <SessionMessagingKit/SessionMessagingKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -86,8 +81,6 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initForRestoreWithAttachmentStream:(TSAttachmentStream *)attachmentStream
{
OWSAssertDebug(attachmentStream);
self = [super initForRestoreWithUniqueId:attachmentStream.uniqueId
contentType:attachmentStream.contentType
sourceFilename:attachmentStream.sourceFilename
@ -109,21 +102,14 @@ NS_ASSUME_NONNULL_BEGIN
albumMessage:(nullable TSMessage *)albumMessage
{
if (attachmentProto.id < 1) {
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) {
// Content type might not set if the sending client can't
// infer a MIME type from the file extension.
OWSLogWarn(@"Invalid attachment content type.");
NSString *_Nullable fileExtension = [fileName pathExtension].lowercaseString;
if (fileExtension.length > 0) {
contentType = [MIMETypeUtil mimeTypeForFileExtension:fileExtension];
@ -148,11 +134,6 @@ NS_ASSUME_NONNULL_BEGIN
caption = attachmentProto.caption;
}
NSString *_Nullable albumMessageId;
if (albumMessage != nil) {
albumMessageId = albumMessage.uniqueId;
}
CGSize mediaSize = CGSizeZero;
if (attachmentProto.hasWidth && attachmentProto.hasHeight && attachmentProto.width > 0
&& attachmentProto.height > 0) {
@ -166,22 +147,17 @@ NS_ASSUME_NONNULL_BEGIN
contentType:contentType
sourceFilename:fileName
caption:caption
albumMessageId:albumMessageId
albumMessageId:0
attachmentType:attachmentType
mediaSize:mediaSize];
pointer.downloadURL = attachmentProto.url; // Loki
pointer.downloadURL = attachmentProto.url;
return pointer;
}
+ (NSArray<TSAttachmentPointer *> *)attachmentPointersFromProtos:
(NSArray<SNProtoAttachmentPointer *> *)attachmentProtos
+ (NSArray<TSAttachmentPointer *> *)attachmentPointersFromProtos:(NSArray<SNProtoAttachmentPointer *> *)attachmentProtos
albumMessage:(TSMessage *)albumMessage
{
OWSAssertDebug(attachmentProtos);
OWSAssertDebug(albumMessage);
NSMutableArray *attachmentPointers = [NSMutableArray new];
for (SNProtoAttachmentPointer *attachmentProto in attachmentProtos) {
TSAttachmentPointer *_Nullable attachmentPointer =
@ -203,63 +179,13 @@ NS_ASSUME_NONNULL_BEGIN
// Legacy instances of TSAttachmentPointer apparently used the serverId as their
// uniqueId.
if (attachmentSchemaVersion < 2 && self.serverId == 0) {
OWSAssertDebug([self isDecimalNumberText:self.uniqueId]);
if ([self isDecimalNumberText:self.uniqueId]) {
// For legacy instances, try to parse the serverId from the uniqueId.
self.serverId = (UInt64)[self.uniqueId integerValue];
} else {
OWSLogError(@"invalid legacy attachment uniqueId: %@.", self.uniqueId);
}
}
}
- (nullable OWSBackupFragment *)lazyRestoreFragment
{
if (!self.lazyRestoreFragmentId) {
return nil;
}
OWSBackupFragment *_Nullable backupFragment =
[OWSBackupFragment fetchObjectWithUniqueID:self.lazyRestoreFragmentId];
OWSAssertDebug(backupFragment);
return backupFragment;
}
#pragma mark - Update With... Methods
- (void)markForLazyRestoreWithFragment:(OWSBackupFragment *)lazyRestoreFragment
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssertDebug(lazyRestoreFragment);
OWSAssertDebug(transaction);
if (!lazyRestoreFragment.uniqueId) {
// If metadata hasn't been saved yet, save now.
[lazyRestoreFragment saveWithTransaction:transaction];
OWSAssertDebug(lazyRestoreFragment.uniqueId);
}
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSAttachmentPointer *attachment) {
[attachment setLazyRestoreFragmentId:lazyRestoreFragment.uniqueId];
}];
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
#ifdef DEBUG
if (self.uniqueId.length > 0) {
id _Nullable oldObject = [transaction objectForKey:self.uniqueId inCollection:TSAttachment.collection];
if ([oldObject isKindOfClass:[TSAttachmentStream class]]) {
OWSFailDebug(@"We should never overwrite a TSAttachmentStream with a TSAttachmentPointer.");
}
} else {
OWSFailDebug(@"Missing uniqueId.");
}
#endif
[super saveWithTransaction:transaction];
}
@end
NS_ASSUME_NONNULL_END

10
SignalUtilitiesKit/Messaging/Attachments/TSAttachmentStream.h → SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentStream.h

@ -1,9 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/DataSource.h>
#import <SignalUtilitiesKit/TSAttachment.h>
#import <SessionUtilitiesKit/DataSource.h>
#import <SessionMessagingKit/TSAttachment.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
@ -19,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
typedef void (^OWSThumbnailSuccess)(UIImage *image);
typedef void (^OWSThumbnailFailure)(void);
@interface TSAttachmentStream : TSAttachment <SNAttachmentStream>
@interface TSAttachmentStream : TSAttachment
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithContentType:(NSString *)contentType

86
SignalUtilitiesKit/Messaging/Attachments/TSAttachmentStream.m → SessionMessagingKit/Messages/Visible Messages/Attachments/TSAttachmentStream.m

@ -1,16 +1,11 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "TSAttachmentStream.h"
#import "MIMETypeUtil.h"
#import "NSData+Image.h"
#import "OWSFileSystem.h"
#import "TSAttachmentPointer.h"
#import <AVFoundation/AVFoundation.h>
#import <SignalCoreKit/Threading.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <YapDatabase/YapDatabase.h>
#import <SessionUtilitiesKit/SessionUtilitiesKit.h>
#import <SessionMessagingKit/SessionMessagingKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -149,21 +144,17 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
sourceFilename:self.sourceFilename
inFolder:attachmentsFolder];
if (!filePath) {
OWSFailDebug(@"Could not generate path for attachment.");
return;
}
if (![filePath hasPrefix:attachmentsFolder]) {
OWSFailDebug(@"Attachment paths should all be in the attachments folder.");
return;
}
NSString *localRelativeFilePath = [filePath substringFromIndex:attachmentsFolder.length];
if (localRelativeFilePath.length < 1) {
OWSFailDebug(@"Empty local relative attachment paths.");
return;
}
self.localRelativeFilePath = localRelativeFilePath;
OWSAssertDebug(self.originalFilePath);
}
#pragma mark - File Management
@ -173,7 +164,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
*error = nil;
NSString *_Nullable filePath = self.originalFilePath;
if (!filePath) {
OWSFailDebug(@"Missing path for attachment.");
return nil;
}
return [NSData dataWithContentsOfFile:filePath options:0 error:error];
@ -181,28 +171,20 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (BOOL)writeData:(NSData *)data error:(NSError **)error
{
OWSAssertDebug(data);
*error = nil;
NSString *_Nullable filePath = self.originalFilePath;
if (!filePath) {
OWSFailDebug(@"Missing path for attachment.");
return NO;
}
OWSLogDebug(@"Writing attachment to file: %@", filePath);
return [data writeToFile:filePath options:0 error:error];
}
- (BOOL)writeDataSource:(DataSource *)dataSource
{
OWSAssertDebug(dataSource);
NSString *_Nullable filePath = self.originalFilePath;
if (!filePath) {
OWSFailDebug(@"Missing path for attachment.");
return NO;
}
OWSLogDebug(@"Writing attachment to file: %@", filePath);
return [dataSource writeToPath:filePath];
}
@ -218,8 +200,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
+ (nullable NSError *)migrateToSharedData
{
OWSLogInfo(@"");
return [OWSFileSystem moveAppFilePath:self.legacyAttachmentsDirPath
sharedDataFilePath:self.sharedDataAttachmentsDirPath];
}
@ -239,7 +219,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (nullable NSString *)originalFilePath
{
if (!self.localRelativeFilePath) {
OWSFailDebug(@"Attachment missing local file path.");
return nil;
}
@ -250,7 +229,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
{
NSString *filePath = self.originalFilePath;
if (!filePath) {
OWSFailDebug(@"Attachment missing local file path.");
return nil;
}
@ -268,7 +246,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (NSString *)thumbnailsDirPath
{
if (!self.localRelativeFilePath) {
OWSFailDebug(@"Attachment missing local file path.");
return nil;
}
@ -288,7 +265,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
{
NSString *_Nullable filePath = self.originalFilePath;
if (!filePath) {
OWSFailDebug(@"Missing path for attachment.");
return nil;
}
return [NSURL fileURLWithPath:filePath];
@ -300,30 +276,19 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
NSString *thumbnailsDirPath = self.thumbnailsDirPath;
if ([[NSFileManager defaultManager] fileExistsAtPath:thumbnailsDirPath]) {
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:thumbnailsDirPath error:&error];
if (error || !success) {
OWSLogError(@"remove thumbnails dir failed with: %@", error);
}
[[NSFileManager defaultManager] removeItemAtPath:thumbnailsDirPath error:&error];
}
NSString *_Nullable legacyThumbnailPath = self.legacyThumbnailPath;
if (legacyThumbnailPath) {
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:legacyThumbnailPath error:&error];
if (error || !success) {
OWSLogError(@"remove legacy thumbnail failed with: %@", error);
}
[[NSFileManager defaultManager] removeItemAtPath:legacyThumbnailPath error:&error];
}
NSString *_Nullable filePath = self.originalFilePath;
if (!filePath) {
OWSFailDebug(@"Missing path for attachment.");
return;
}
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
if (error || !success) {
OWSLogError(@"remove file failed with: %@", error);
}
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
}
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
@ -353,13 +318,10 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (BOOL)isValidImage
{
OWSAssertDebug(self.isImage || self.isAnimated);
BOOL result;
BOOL didUpdateCache = NO;
@synchronized(self) {
if (!self.isValidImageCached) {
OWSLogVerbose(@"Updating isValidImageCached.");
self.isValidImageCached = @([NSData ows_isValidImageAtPath:self.originalFilePath
mimeType:self.contentType]);
didUpdateCache = YES;
@ -378,13 +340,10 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (BOOL)isValidVideo
{
OWSAssertDebug(self.isVideo);
BOOL result;
BOOL didUpdateCache = NO;
@synchronized(self) {
if (!self.isValidVideoCached) {
OWSLogVerbose(@"Updating isValidVideoCached.");
self.isValidVideoCached = @([OWSMediaUtils isValidVideoWithPath:self.originalFilePath]);
didUpdateCache = YES;
}
@ -423,16 +382,13 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (nullable NSData *)validStillImageData
{
if ([self isVideo]) {
OWSFailDebug(@"isVideo was unexpectedly true");
return nil;
}
if ([self isAnimated]) {
OWSFailDebug(@"isAnimated was unexpectedly true");
return nil;
}
if (![NSData ows_isValidImageAtPath:self.originalFilePath mimeType:self.contentType]) {
OWSFailDebug(@"skipping invalid image");
return nil;
}
@ -452,7 +408,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
maxDimension:ThumbnailDimensionPointsLarge()
error:&error];
if (error || !image) {
OWSLogError(@"Could not create video still: %@.", error);
return nil;
}
return image;
@ -468,15 +423,11 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
[fileManager contentsOfDirectoryAtURL:fileURL includingPropertiesForKeys:nil options:0 error:&error];
if (error) {
OWSFailDebug(@"failed to get contents of attachments folder: %@ with error: %@", self.attachmentsFolder, error);
return;
}
for (NSURL *url in contents) {
[fileManager removeItemAtURL:url error:&error];
if (error) {
OWSFailDebug(@"failed to remove item at path: %@ with error: %@", url, error);
}
}
}
@ -529,8 +480,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (CGSize)cachedMediaSize
{
OWSAssertDebug(self.shouldHaveImageSize);
@synchronized(self) {
if (self.cachedImageWidth && self.cachedImageHeight) {
return CGSizeMake(self.cachedImageWidth.floatValue, self.cachedImageHeight.floatValue);
@ -544,8 +493,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (void)applyChangeAsyncToLatestCopyWithChangeBlock:(void (^)(TSAttachmentStream *))changeBlock
{
OWSAssertDebug(changeBlock);
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSString *collection = [TSAttachmentStream collection];
TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
@ -555,9 +502,8 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
// _very_ rare.
//
// An exception is incoming group avatar updates which we don't ever save.
OWSLogWarn(@"Attachment not yet saved.");
} else if (![latestInstance isKindOfClass:[TSAttachmentStream class]]) {
OWSFailDebug(@"Attachment has unexpected type: %@", latestInstance.class);
// Shouldn't occur
} else {
changeBlock(latestInstance);
@ -570,9 +516,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
- (CGFloat)calculateAudioDurationSeconds
{
OWSAssertIsOnMainThread();
OWSAssertDebug([self isAudio]);
NSError *error;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.originalMediaURL error:&error];
if (error && [error.domain isEqualToString:NSOSStatusErrorDomain]
@ -583,15 +526,12 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
if (!error) {
return (CGFloat)[audioPlayer duration];
} else {
OWSLogError(@"Could not find audio duration: %@", self.originalMediaURL);
return 0;
}
}
- (CGFloat)audioDurationSeconds
{
OWSAssertIsOnMainThread();
if (self.cachedAudioDurationSeconds) {
return self.cachedAudioDurationSeconds.floatValue;
}
@ -686,7 +626,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
if ([[NSFileManager defaultManager] fileExistsAtPath:thumbnailPath]) {
UIImage *_Nullable image = [UIImage imageWithContentsOfFile:thumbnailPath];
if (!image) {
OWSFailDebug(@"couldn't load image.");
// Any time we return nil from this method we have to call the failure handler
// or else the caller waits for an async thumbnail
failure();
@ -699,7 +638,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
thumbnailDimensionPoints:thumbnailDimensionPoints
success:success
failure:^(NSError *error) {
OWSLogError(@"Failed to create thumbnail: %@", error);
failure();
}];
return nil;
@ -737,7 +675,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
{
OWSLoadedThumbnail *_Nullable loadedThumbnail = [self loadedThumbnailSmallSync];
if (!loadedThumbnail) {
OWSLogInfo(@"Couldn't load small thumbnail sync.");
return nil;
}
return loadedThumbnail.image;
@ -747,13 +684,11 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
{
OWSLoadedThumbnail *_Nullable loadedThumbnail = [self loadedThumbnailSmallSync];
if (!loadedThumbnail) {
OWSLogInfo(@"Couldn't load small thumbnail sync.");
return nil;
}
NSError *error;
NSData *_Nullable data = [loadedThumbnail dataAndReturnError:&error];
if (error || !data) {
OWSFailDebug(@"Couldn't load thumbnail data: %@", error);
return nil;
}
return data;
@ -769,7 +704,7 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
NSArray<NSString *> *_Nullable fileNames =
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:thumbnailsDirPath error:&error];
if (error || !fileNames) {
OWSFailDebug(@"contentsOfDirectoryAtPath failed with error: %@", error);
// Do nothing
} else {
for (NSString *fileName in fileNames) {
NSString *filePath = [thumbnailsDirPath stringByAppendingPathComponent:fileName];
@ -812,7 +747,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
NSError *error;
BOOL success = [thumbnailAttachment writeData:thumbnailData error:&error];
if (!success || error) {
OWSLogError(@"Couldn't copy attachment data for message sent to self: %@.", error);
return nil;
}
@ -823,14 +757,11 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
+ (nullable SNProtoAttachmentPointer *)buildProtoForAttachmentId:(nullable NSString *)attachmentId
{
OWSAssertDebug(attachmentId.length > 0);
// TODO we should past in a transaction, rather than sneakily generate one in `fetch...` to make sure we're
// getting a consistent view in the message sending process. A brief glance shows it touches quite a bit of code,
// but should be straight forward.
TSAttachment *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
OWSLogError(@"Unexpected type for attachment builder: %@", attachment);
return nil;
}
@ -843,10 +774,8 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
{
SNProtoAttachmentPointerBuilder *builder = [SNProtoAttachmentPointer builderWithId:self.serverId];
OWSAssertDebug(self.contentType.length > 0);
builder.contentType = self.contentType;
OWSLogVerbose(@"Sending attachment with filename: '%@'", self.sourceFilename);
if (self.sourceFilename.length > 0) {
builder.fileName = self.sourceFilename;
}
@ -876,7 +805,6 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
NSError *error;
SNProtoAttachmentPointer *_Nullable attachmentProto = [builder buildAndReturnError:&error];
if (error || !attachmentProto) {
OWSFailDebug(@"could not build protobuf: %@", error);
return nil;
}
return attachmentProto;

31
SessionMessagingKit/Messages/Visible Message/VisibleMessage+Attachment.swift → SessionMessagingKit/Messages/Visible Messages/Attachments/VisibleMessage+Attachment.swift

@ -16,7 +16,8 @@ public extension VisibleMessage {
public var url: String?
public var isValid: Bool {
fileName != nil && contentType != nil && key != nil && digest != nil && kind != nil && size != nil && sizeInBytes != nil && url != nil
// key and digest can be nil for open group attachments
fileName != nil && contentType != nil && kind != nil && size != nil && sizeInBytes != nil && url != nil
}
public enum Kind : String {
@ -50,7 +51,33 @@ public extension VisibleMessage {
}
public static func fromProto(_ proto: SNProtoAttachmentPointer) -> Attachment? {
preconditionFailure("Use MessageReceiverDelegate.parseAttachments(from:) instead.")
let result = Attachment()
result.fileName = proto.fileName
func inferContentType() -> String {
guard let fileName = result.fileName, let fileExtension = URL(string: fileName)?.pathExtension else { return OWSMimeTypeApplicationOctetStream }
return MIMETypeUtil.mimeType(forFileExtension: fileExtension) ?? OWSMimeTypeApplicationOctetStream
}
result.contentType = proto.contentType ?? inferContentType()
result.key = proto.key
result.digest = proto.digest
let kind: VisibleMessage.Attachment.Kind
if proto.hasFlags && (proto.flags & UInt32(SNProtoAttachmentPointer.SNProtoAttachmentPointerFlags.voiceMessage.rawValue)) > 0 {
kind = .voiceMessage
} else {
kind = .generic
}
result.kind = kind
result.caption = proto.hasCaption ? proto.caption : nil
let size: CGSize
if proto.hasWidth && proto.width > 0 && proto.hasHeight && proto.height > 0 {
size = CGSize(width: Int(proto.width), height: Int(proto.height))
} else {
size = CGSize.zero
}
result.size = size
result.sizeInBytes = proto.size > 0 ? UInt(proto.size) : nil
result.url = proto.url
return result
}
public func toProto() -> SNProtoDataMessageQuote? {

0
SessionMessagingKit/Messages/Visible Message/VisibleMessage+Contact.swift → SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Contact.swift

0
SessionMessagingKit/Messages/Visible Message/VisibleMessage+LinkPreview.swift → SessionMessagingKit/Messages/Visible Messages/VisibleMessage+LinkPreview.swift

0
SessionMessagingKit/Messages/Visible Message/VisibleMessage+Profile.swift → SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Profile.swift

0
SessionMessagingKit/Messages/Visible Message/VisibleMessage+Quote.swift → SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Quote.swift

0
SessionMessagingKit/Messages/Visible Message/VisibleMessage.swift → SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift

4
SessionMessagingKit/Meta/SessionMessagingKit.h

@ -2,3 +2,7 @@
FOUNDATION_EXPORT double SessionMessagingKitVersionNumber;
FOUNDATION_EXPORT const unsigned char SessionMessagingKitVersionString[];
#import <SessionMessagingKit/TSAttachment.h>
#import <SessionMessagingKit/TSAttachmentPointer.h>
#import <SessionMessagingKit/TSAttachmentStream.h>

30
SessionMessagingKit/Sending & Receiving/MessageReceiver.swift

@ -74,8 +74,6 @@ internal enum MessageReceiver {
// Parse the message
let message: Message? = {
if let readReceipt = ReadReceipt.fromProto(proto) { return readReceipt }
if let sessionRequest = SessionRequest.fromProto(proto) { return sessionRequest }
if let nullMessage = NullMessage.fromProto(proto) { return nullMessage }
if let typingIndicator = TypingIndicator.fromProto(proto) { return typingIndicator }
if let closedGroupUpdate = ClosedGroupUpdate.fromProto(proto) { return closedGroupUpdate }
if let expirationTimerUpdate = ExpirationTimerUpdate.fromProto(proto) { return expirationTimerUpdate }
@ -99,8 +97,6 @@ internal enum MessageReceiver {
internal static func handle(_ message: Message, associatedWithProto proto: SNProtoContent, using transaction: Any) throws {
switch message {
case let message as ReadReceipt: handleReadReceipt(message, using: transaction)
case let message as SessionRequest: handleSessionRequest(message, using: transaction)
case let message as NullMessage: handleNullMessage(message, using: transaction)
case let message as TypingIndicator: handleTypingIndicator(message, using: transaction)
case let message as ClosedGroupUpdate: handleClosedGroupUpdate(message, using: transaction)
case let message as ExpirationTimerUpdate: handleExpirationTimerUpdate(message, using: transaction)
@ -113,14 +109,6 @@ internal enum MessageReceiver {
Configuration.shared.messageReceiverDelegate.markMessagesAsRead(message.timestamps!, from: message.sender!, at: message.receivedTimestamp!)
}
private static func handleSessionRequest(_ message: SessionRequest, using transaction: Any) {
// We might not need this anymore
}
private static func handleNullMessage(_ message: NullMessage, u