parent
bc10aea202
commit
40dcc7c873
|
@ -75,7 +75,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
key:attachmentProto.key
|
||||
digest:digest
|
||||
contentType:attachmentProto.contentType
|
||||
relay:relay];
|
||||
relay:relay
|
||||
filename:attachmentProto.fileName];
|
||||
|
||||
[attachmentIds addObject:pointer.uniqueId];
|
||||
|
||||
|
|
|
@ -21,9 +21,11 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
|
|||
key:(NSData *)key
|
||||
digest:(nullable NSData *)digest
|
||||
contentType:(NSString *)contentType
|
||||
relay:(NSString *)relay NS_DESIGNATED_INITIALIZER;
|
||||
relay:(NSString *)relay
|
||||
filename:(nullable NSString *)filename NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, readonly) NSString *relay;
|
||||
@property (nonatomic, readonly, nullable) NSString *filename;
|
||||
@property (atomic) TSAttachmentPointerState state;
|
||||
|
||||
// Though now required, `digest` may be null for pre-existing records or from
|
||||
|
|
|
@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
digest:(nullable NSData *)digest
|
||||
contentType:(NSString *)contentType
|
||||
relay:(NSString *)relay
|
||||
filename:(nullable NSString *)filename
|
||||
{
|
||||
self = [super initWithServerId:serverId encryptionKey:key contentType:contentType];
|
||||
if (!self) {
|
||||
|
@ -39,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
_digest = digest;
|
||||
_state = TSAttachmentPointerStateEnqueued;
|
||||
_relay = relay;
|
||||
_filename = filename;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// This only applies for attachments being uploaded.
|
||||
@property (atomic) BOOL isUploaded;
|
||||
|
||||
@property (nonatomic, readonly, nullable) NSString *filename;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
- (nullable UIImage *)image;
|
||||
#endif
|
||||
|
|
|
@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// state, but this constructor is used only for new incoming
|
||||
// attachments which don't need to be uploaded.
|
||||
_isUploaded = YES;
|
||||
_filename = pointer.filename;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -115,6 +116,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
return [MIMETypeUtil filePathForAttachment:self.uniqueId
|
||||
ofMIMEType:self.contentType
|
||||
filename:self.filename
|
||||
inFolder:[[self class] attachmentsFolder]];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSSyncContactsMessage.h"
|
||||
#import "Contact.h"
|
||||
|
@ -39,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
OWSSignalServiceProtosAttachmentPointer *attachmentProto =
|
||||
[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0]];
|
||||
[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0] filename:nil];
|
||||
|
||||
OWSSignalServiceProtosSyncMessageContactsBuilder *contactsBuilder =
|
||||
[OWSSignalServiceProtosSyncMessageContactsBuilder new];
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSSyncGroupsMessage.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
|
@ -25,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
(unsigned long)self.attachmentIds.count);
|
||||
}
|
||||
OWSSignalServiceProtosAttachmentPointer *attachmentProto =
|
||||
[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0]];
|
||||
[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0] filename:nil];
|
||||
|
||||
OWSSignalServiceProtosSyncMessageGroupsBuilder *groupsBuilder =
|
||||
[OWSSignalServiceProtosSyncMessageGroupsBuilder new];
|
||||
|
|
|
@ -23,6 +23,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
|
|||
@interface TSMessage : TSInteraction
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray<NSString *> *attachmentIds;
|
||||
// A map of attachment id-to-filename.
|
||||
@property (nullable, nonatomic) NSString *body;
|
||||
@property (nonatomic) TSGroupMetaMessage groupMetaMessage;
|
||||
@property (nonatomic) uint32_t expiresInSeconds;
|
||||
|
|
|
@ -128,7 +128,6 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
|
|||
}
|
||||
|
||||
if (!_attachmentIds) {
|
||||
// previously allowed nil _attachmentIds
|
||||
_attachmentIds = [NSMutableArray new];
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) {
|
|||
@property BOOL hasSyncedTranscript;
|
||||
@property NSString *customMessage;
|
||||
@property (atomic, readonly) NSString *mostRecentFailureText;
|
||||
@property (nonatomic, readonly) NSMutableDictionary<NSString *, NSString *> *attachmentFilenameMap;
|
||||
|
||||
/**
|
||||
* Whether the message should be serialized as a modern aka Content, or the old style legacy message.
|
||||
|
@ -94,10 +95,14 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) {
|
|||
* @param attachmentId
|
||||
* id of an AttachmentStream containing the meta data used when populating the attachment proto
|
||||
*
|
||||
* @param filename
|
||||
* optional filename of the attachment.
|
||||
*
|
||||
* @return
|
||||
* An attachment pointer protobuf suitable for including in various container protobuf builders
|
||||
*/
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildAttachmentProtoForAttachmentId:(NSString *)attachmentId;
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildAttachmentProtoForAttachmentId:(NSString *)attachmentId
|
||||
filename:(nullable NSString *)filename;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -15,7 +15,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
return [super initWithCoder:coder];
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
if (!_attachmentFilenameMap) {
|
||||
_attachmentFilenameMap = [NSMutableDictionary new];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
|
@ -86,6 +92,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
} else {
|
||||
self.groupMetaMessage = TSGroupMessageNone;
|
||||
}
|
||||
_attachmentFilenameMap = [NSMutableDictionary new];
|
||||
|
||||
OWSAssert(self.receivedAtDate);
|
||||
|
||||
|
@ -142,7 +149,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
case TSGroupMessageNew: {
|
||||
if (gThread.groupModel.groupImage != nil && self.attachmentIds.count == 1) {
|
||||
attachmentWasGroupAvatar = YES;
|
||||
[groupBuilder setAvatar:[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0]]];
|
||||
[groupBuilder
|
||||
setAvatar:[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0] filename:nil]];
|
||||
}
|
||||
|
||||
[groupBuilder setMembersArray:gThread.groupModel.groupMemberIds];
|
||||
|
@ -160,7 +168,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
if (!attachmentWasGroupAvatar) {
|
||||
NSMutableArray *attachments = [NSMutableArray new];
|
||||
for (NSString *attachmentId in self.attachmentIds) {
|
||||
[attachments addObject:[self buildAttachmentProtoForAttachmentId:attachmentId]];
|
||||
NSString *filename = self.attachmentFilenameMap[attachmentId];
|
||||
[attachments addObject:[self buildAttachmentProtoForAttachmentId:attachmentId filename:filename]];
|
||||
}
|
||||
[builder setAttachmentsArray:attachments];
|
||||
}
|
||||
|
@ -191,7 +200,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildAttachmentProtoForAttachmentId:(NSString *)attachmentId
|
||||
filename:(nullable NSString *)filename
|
||||
{
|
||||
OWSAssert(attachmentId.length > 0);
|
||||
|
||||
TSAttachment *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
|
||||
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
DDLogError(@"Unexpected type for attachment builder: %@", attachment);
|
||||
|
@ -202,6 +214,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSSignalServiceProtosAttachmentPointerBuilder *builder = [OWSSignalServiceProtosAttachmentPointerBuilder new];
|
||||
[builder setId:attachmentStream.serverId];
|
||||
[builder setContentType:attachmentStream.contentType];
|
||||
[builder setFileName:filename];
|
||||
[builder setKey:attachmentStream.encryptionKey];
|
||||
[builder setDigest:attachmentStream.digest];
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ NS_SWIFT_NAME(MessageSender)
|
|||
*/
|
||||
- (void)sendAttachmentData:(NSData *)attachmentData
|
||||
contentType:(NSString *)contentType
|
||||
filename:(nullable NSString *)filename
|
||||
inMessage:(TSOutgoingMessage *)outgoingMessage
|
||||
success:(void (^)())successHandler
|
||||
failure:(void (^)(NSError *error))failureHandler;
|
||||
|
|
|
@ -437,6 +437,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
|
||||
[self sendAttachmentData:attachmentData
|
||||
contentType:contentType
|
||||
filename:nil
|
||||
inMessage:message
|
||||
success:successWithDeleteHandler
|
||||
failure:failureWithDeleteHandler];
|
||||
|
@ -444,6 +445,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
|
||||
- (void)sendAttachmentData:(NSData *)data
|
||||
contentType:(NSString *)contentType
|
||||
filename:(nullable NSString *)filename
|
||||
inMessage:(TSOutgoingMessage *)message
|
||||
success:(void (^)())successHandler
|
||||
failure:(void (^)(NSError *error))failureHandler
|
||||
|
@ -467,6 +469,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
|
||||
[attachmentStream save];
|
||||
[message.attachmentIds addObject:attachmentStream.uniqueId];
|
||||
if (filename) {
|
||||
message.attachmentFilenameMap[attachmentStream.uniqueId] = filename;
|
||||
}
|
||||
[message save];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
|
|
@ -29,11 +29,11 @@ extern NSString *const OWSMimeTypeUnknownForTests;
|
|||
+ (BOOL)isVideo:(NSString *)contentType;
|
||||
+ (BOOL)isAudio:(NSString *)contentType;
|
||||
|
||||
+ (NSString *)filePathForAttachment:(NSString *)uniqueId ofMIMEType:(NSString *)contentType inFolder:(NSString *)folder;
|
||||
+ (NSString *)filePathForImage:(NSString *)uniqueId ofMIMEType:(NSString *)contentType inFolder:(NSString *)folder;
|
||||
+ (NSString *)filePathForVideo:(NSString *)uniqueId ofMIMEType:(NSString *)contentType inFolder:(NSString *)folder;
|
||||
+ (NSString *)filePathForAudio:(NSString *)uniqueId ofMIMEType:(NSString *)contentType inFolder:(NSString *)folder;
|
||||
+ (NSString *)filePathForAnimated:(NSString *)uniqueId ofMIMEType:(NSString *)contentType inFolder:(NSString *)folder;
|
||||
// filename is optional and should not be trusted.
|
||||
+ (NSString *)filePathForAttachment:(NSString *)uniqueId
|
||||
ofMIMEType:(NSString *)contentType
|
||||
filename:(nullable NSString *)filename
|
||||
inFolder:(NSString *)folder;
|
||||
|
||||
+ (NSURL *)simLinkCorrectExtensionOfFile:(NSURL *)mediaURL ofMIMEType:(NSString *)contentType;
|
||||
|
||||
|
|
|
@ -261,7 +261,65 @@ NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype";
|
|||
|
||||
+ (NSString *)filePathForAttachment:(NSString *)uniqueId
|
||||
ofMIMEType:(NSString *)contentType
|
||||
inFolder:(NSString *)folder {
|
||||
filename:(nullable NSString *)filename
|
||||
inFolder:(NSString *)folder
|
||||
{
|
||||
NSString *kDefaultFileExtension = @"bin";
|
||||
|
||||
if (filename.length > 0) {
|
||||
NSString *normalizedFilename =
|
||||
[filename stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
// Ensure that the filename is a valid filesystem name,
|
||||
// replacing invalid characters with an underscore.
|
||||
for (NSCharacterSet *invalidCharacterSet in @[
|
||||
[NSCharacterSet whitespaceCharacterSet],
|
||||
[NSCharacterSet newlineCharacterSet],
|
||||
[NSCharacterSet illegalCharacterSet],
|
||||
[NSCharacterSet controlCharacterSet],
|
||||
[NSCharacterSet characterSetWithCharactersInString:@"<>|\\:()&;?*"],
|
||||
]) {
|
||||
normalizedFilename = [[normalizedFilename componentsSeparatedByCharactersInSet:invalidCharacterSet]
|
||||
componentsJoinedByString:@"_"];
|
||||
}
|
||||
|
||||
NSString *fileExtension = [[normalizedFilename pathExtension]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
NSString *filenameWithoutExtension = [[[normalizedFilename lastPathComponent] stringByDeletingPathExtension]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
// If the filename has not file extension, deduce one
|
||||
// from the MIME type.
|
||||
if (fileExtension.length < 1) {
|
||||
fileExtension = [self fileExtensionForMIMEType:contentType];
|
||||
if (fileExtension.length < 1) {
|
||||
fileExtension = kDefaultFileExtension;
|
||||
}
|
||||
}
|
||||
fileExtension = [fileExtension lowercaseString];
|
||||
|
||||
if (filenameWithoutExtension.length > 0) {
|
||||
// Store the file in a subdirectory whose name is the uniqueId of this attachment,
|
||||
// to avoid collisions between multiple attachments with the same name.
|
||||
NSString *attachmentFolderPath = [folder stringByAppendingPathComponent:uniqueId];
|
||||
NSError *error = nil;
|
||||
BOOL attachmentFolderPathExists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentFolderPath];
|
||||
if (!attachmentFolderPathExists) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:attachmentFolderPath
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&error];
|
||||
if (error) {
|
||||
DDLogError(@"Failed to create attachment directory: %@", error);
|
||||
OWSAssert(0);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return [attachmentFolderPath
|
||||
stringByAppendingPathComponent:[NSString
|
||||
stringWithFormat:@"%@.%@", filenameWithoutExtension, fileExtension]];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self isVideo:contentType]) {
|
||||
return [MIMETypeUtil filePathForVideo:uniqueId ofMIMEType:contentType inFolder:folder];
|
||||
} else if ([self isAudio:contentType]) {
|
||||
|
@ -291,7 +349,7 @@ NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype";
|
|||
|
||||
DDLogError(@"Got asked for path of file %@ which is unsupported", contentType);
|
||||
// Use a fallback file extension.
|
||||
return [self filePathForData:uniqueId withFileExtension:@"bin" inFolder:folder];
|
||||
return [self filePathForData:uniqueId withFileExtension:kDefaultFileExtension inFolder:folder];
|
||||
}
|
||||
|
||||
+ (NSURL *)simLinkCorrectExtensionOfFile:(NSURL *)mediaURL ofMIMEType:(NSString *)contentType {
|
||||
|
|
Loading…
Reference in New Issue