diff --git a/protobuf/OWSSignalServiceProtos.proto b/protobuf/OWSSignalServiceProtos.proto index 61b4105bc..af05291a8 100644 --- a/protobuf/OWSSignalServiceProtos.proto +++ b/protobuf/OWSSignalServiceProtos.proto @@ -39,11 +39,15 @@ message Content { message CallMessage { message Offer { optional uint64 id = 1; + // We've renamed the description field on iOS to avoid + // conflicts with [NSObject description]. optional string sessionDescription = 2; } message Answer { optional uint64 id = 1; + // We've renamed the description field on iOS to avoid + // conflicts with [NSObject description]. optional string sessionDescription = 2; } @@ -134,14 +138,16 @@ message AttachmentPointer { optional uint32 size = 4; optional bytes thumbnail = 5; optional bytes digest = 6; + optional string fileName = 7; } message GroupContext { enum Type { - UNKNOWN = 0; - UPDATE = 1; - DELIVER = 2; - QUIT = 3; + UNKNOWN = 0; + UPDATE = 1; + DELIVER = 2; + QUIT = 3; + REQUEST_INFO = 4; } optional bytes id = 1; optional Type type = 2; diff --git a/src/Messages/Attachments/OWSAttachmentsProcessor.m b/src/Messages/Attachments/OWSAttachmentsProcessor.m index 766a372b7..87a869a42 100644 --- a/src/Messages/Attachments/OWSAttachmentsProcessor.m +++ b/src/Messages/Attachments/OWSAttachmentsProcessor.m @@ -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]; diff --git a/src/Messages/Attachments/TSAttachmentPointer.h b/src/Messages/Attachments/TSAttachmentPointer.h index cb8143fdb..9daf32879 100644 --- a/src/Messages/Attachments/TSAttachmentPointer.h +++ b/src/Messages/Attachments/TSAttachmentPointer.h @@ -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 diff --git a/src/Messages/Attachments/TSAttachmentPointer.m b/src/Messages/Attachments/TSAttachmentPointer.m index 73ca07a53..7097a204a 100644 --- a/src/Messages/Attachments/TSAttachmentPointer.m +++ b/src/Messages/Attachments/TSAttachmentPointer.m @@ -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; } diff --git a/src/Messages/Attachments/TSAttachmentStream.h b/src/Messages/Attachments/TSAttachmentStream.h index 63b33bcd9..42bb0fcbe 100644 --- a/src/Messages/Attachments/TSAttachmentStream.h +++ b/src/Messages/Attachments/TSAttachmentStream.h @@ -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 diff --git a/src/Messages/Attachments/TSAttachmentStream.m b/src/Messages/Attachments/TSAttachmentStream.m index 7ce6e5bf4..289549228 100644 --- a/src/Messages/Attachments/TSAttachmentStream.m +++ b/src/Messages/Attachments/TSAttachmentStream.m @@ -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]]; } diff --git a/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m b/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m index 438ea8e9e..0f3266c92 100644 --- a/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m +++ b/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m @@ -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]; diff --git a/src/Messages/DeviceSyncing/OWSSyncGroupsMessage.m b/src/Messages/DeviceSyncing/OWSSyncGroupsMessage.m index 87ffdcffe..084bb2e55 100644 --- a/src/Messages/DeviceSyncing/OWSSyncGroupsMessage.m +++ b/src/Messages/DeviceSyncing/OWSSyncGroupsMessage.m @@ -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]; diff --git a/src/Messages/Interactions/TSMessage.m b/src/Messages/Interactions/TSMessage.m index 92e4a784f..e98b6b063 100644 --- a/src/Messages/Interactions/TSMessage.m +++ b/src/Messages/Interactions/TSMessage.m @@ -128,7 +128,6 @@ static const NSUInteger OWSMessageSchemaVersion = 3; } if (!_attachmentIds) { - // previously allowed nil _attachmentIds _attachmentIds = [NSMutableArray new]; } diff --git a/src/Messages/Interactions/TSOutgoingMessage.h b/src/Messages/Interactions/TSOutgoingMessage.h index b58d72d95..4c236a4a3 100644 --- a/src/Messages/Interactions/TSOutgoingMessage.h +++ b/src/Messages/Interactions/TSOutgoingMessage.h @@ -57,6 +57,8 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) { @property BOOL hasSyncedTranscript; @property NSString *customMessage; @property (atomic, readonly) NSString *mostRecentFailureText; +// A map of attachment id-to-filename. +@property (nonatomic, readonly) NSMutableDictionary *attachmentFilenameMap; /** * Whether the message should be serialized as a modern aka Content, or the old style legacy message. @@ -94,10 +96,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 diff --git a/src/Messages/Interactions/TSOutgoingMessage.m b/src/Messages/Interactions/TSOutgoingMessage.m index b53014ccb..832cb40ce 100644 --- a/src/Messages/Interactions/TSOutgoingMessage.m +++ b/src/Messages/Interactions/TSOutgoingMessage.m @@ -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]; diff --git a/src/Messages/OWSCallAnswerMessage.m b/src/Messages/OWSCallAnswerMessage.m index 976288952..ae5f8e8f3 100644 --- a/src/Messages/OWSCallAnswerMessage.m +++ b/src/Messages/OWSCallAnswerMessage.m @@ -1,5 +1,6 @@ -// Created by Michael Kirk on 12/1/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSCallAnswerMessage.h" #import "OWSSignalServiceProtos.pb.h" diff --git a/src/Messages/OWSCallOfferMessage.m b/src/Messages/OWSCallOfferMessage.m index 17b741897..67f251b95 100644 --- a/src/Messages/OWSCallOfferMessage.m +++ b/src/Messages/OWSCallOfferMessage.m @@ -1,5 +1,6 @@ -// Created by Michael Kirk on 12/1/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSCallOfferMessage.h" #import "OWSSignalServiceProtos.pb.h" diff --git a/src/Messages/OWSMessageSender.h b/src/Messages/OWSMessageSender.h index 371c54ddc..267b2fa45 100644 --- a/src/Messages/OWSMessageSender.h +++ b/src/Messages/OWSMessageSender.h @@ -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; diff --git a/src/Messages/OWSMessageSender.m b/src/Messages/OWSMessageSender.m index efd0e0284..24f28a66c 100644 --- a/src/Messages/OWSMessageSender.m +++ b/src/Messages/OWSMessageSender.m @@ -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(), ^{ diff --git a/src/Messages/OWSSignalServiceProtos.pb.h b/src/Messages/OWSSignalServiceProtos.pb.h index ef02dfb63..c4656d7f2 100644 --- a/src/Messages/OWSSignalServiceProtos.pb.h +++ b/src/Messages/OWSSignalServiceProtos.pb.h @@ -1,4 +1,6 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import @@ -128,6 +130,7 @@ typedef NS_ENUM(SInt32, OWSSignalServiceProtosGroupContextType) { OWSSignalServiceProtosGroupContextTypeUpdate = 1, OWSSignalServiceProtosGroupContextTypeDeliver = 2, OWSSignalServiceProtosGroupContextTypeQuit = 3, + OWSSignalServiceProtosGroupContextTypeRequestInfo = 4, }; BOOL OWSSignalServiceProtosGroupContextTypeIsValidValue(OWSSignalServiceProtosGroupContextType value); @@ -1278,16 +1281,19 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro #define AttachmentPointer_size @"size" #define AttachmentPointer_thumbnail @"thumbnail" #define AttachmentPointer_digest @"digest" +#define AttachmentPointer_fileName @"fileName" @interface OWSSignalServiceProtosAttachmentPointer : PBGeneratedMessage { @private BOOL hasId_:1; BOOL hasContentType_:1; + BOOL hasFileName_:1; BOOL hasKey_:1; BOOL hasThumbnail_:1; BOOL hasDigest_:1; BOOL hasSize_:1; UInt64 id; NSString* contentType; + NSString* fileName; NSData* key; NSData* thumbnail; NSData* digest; @@ -1299,12 +1305,14 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro - (BOOL) hasSize; - (BOOL) hasThumbnail; - (BOOL) hasDigest; +- (BOOL) hasFileName; @property (readonly) UInt64 id; @property (readonly, strong) NSString* contentType; @property (readonly, strong) NSData* key; @property (readonly) UInt32 size; @property (readonly, strong) NSData* thumbnail; @property (readonly, strong) NSData* digest; +@property (readonly, strong) NSString* fileName; + (instancetype) defaultInstance; - (instancetype) defaultInstance; @@ -1370,6 +1378,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro - (NSData*) digest; - (OWSSignalServiceProtosAttachmentPointerBuilder*) setDigest:(NSData*) value; - (OWSSignalServiceProtosAttachmentPointerBuilder*) clearDigest; + +- (BOOL) hasFileName; +- (NSString*) fileName; +- (OWSSignalServiceProtosAttachmentPointerBuilder*) setFileName:(NSString*) value; +- (OWSSignalServiceProtosAttachmentPointerBuilder*) clearFileName; @end #define GroupContext_id @"id" diff --git a/src/Messages/OWSSignalServiceProtos.pb.m b/src/Messages/OWSSignalServiceProtos.pb.m index 1cd7cc552..05dfe314c 100644 --- a/src/Messages/OWSSignalServiceProtos.pb.m +++ b/src/Messages/OWSSignalServiceProtos.pb.m @@ -1,4 +1,6 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSSignalServiceProtos.pb.h" // @@protoc_insertion_point(imports) @@ -5320,6 +5322,7 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM @property UInt32 size; @property (strong) NSData* thumbnail; @property (strong) NSData* digest; +@property (strong) NSString* fileName; @end @implementation OWSSignalServiceProtosAttachmentPointer @@ -5366,6 +5369,13 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM hasDigest_ = !!_value_; } @synthesize digest; +- (BOOL) hasFileName { + return !!hasFileName_; +} +- (void) setHasFileName:(BOOL) _value_ { + hasFileName_ = !!_value_; +} +@synthesize fileName; - (instancetype) init { if ((self = [super init])) { self.id = 0L; @@ -5374,6 +5384,7 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM self.size = 0; self.thumbnail = [NSData data]; self.digest = [NSData data]; + self.fileName = @""; } return self; } @@ -5411,6 +5422,9 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt if (self.hasDigest) { [output writeData:6 value:self.digest]; } + if (self.hasFileName) { + [output writeString:7 value:self.fileName]; + } [self.unknownFields writeToCodedOutputStream:output]; } - (SInt32) serializedSize { @@ -5438,6 +5452,9 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt if (self.hasDigest) { size_ += computeDataSize(6, self.digest); } + if (self.hasFileName) { + size_ += computeStringSize(7, self.fileName); + } size_ += self.unknownFields.serializedSize; memoizedSerializedSize = size_; return size_; @@ -5491,6 +5508,9 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt if (self.hasDigest) { [output appendFormat:@"%@%@: %@\n", indent, @"digest", self.digest]; } + if (self.hasFileName) { + [output appendFormat:@"%@%@: %@\n", indent, @"fileName", self.fileName]; + } [self.unknownFields writeDescriptionTo:output withIndent:indent]; } - (void) storeInDictionary:(NSMutableDictionary *)dictionary { @@ -5512,6 +5532,9 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt if (self.hasDigest) { [dictionary setObject: self.digest forKey: @"digest"]; } + if (self.hasFileName) { + [dictionary setObject: self.fileName forKey: @"fileName"]; + } [self.unknownFields storeInDictionary:dictionary]; } - (BOOL) isEqual:(id)other { @@ -5535,6 +5558,8 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt (!self.hasThumbnail || [self.thumbnail isEqual:otherMessage.thumbnail]) && self.hasDigest == otherMessage.hasDigest && (!self.hasDigest || [self.digest isEqual:otherMessage.digest]) && + self.hasFileName == otherMessage.hasFileName && + (!self.hasFileName || [self.fileName isEqual:otherMessage.fileName]) && (self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields])); } - (NSUInteger) hash { @@ -5557,6 +5582,9 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt if (self.hasDigest) { hashCode = hashCode * 31 + [self.digest hash]; } + if (self.hasFileName) { + hashCode = hashCode * 31 + [self.fileName hash]; + } hashCode = hashCode * 31 + [self.unknownFields hash]; return hashCode; } @@ -5618,6 +5646,9 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt if (other.hasDigest) { [self setDigest:other.digest]; } + if (other.hasFileName) { + [self setFileName:other.fileName]; + } [self mergeUnknownFields:other.unknownFields]; return self; } @@ -5663,6 +5694,10 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt [self setDigest:[input readData]]; break; } + case 58: { + [self setFileName:[input readString]]; + break; + } } } } @@ -5762,6 +5797,22 @@ static OWSSignalServiceProtosAttachmentPointer* defaultOWSSignalServiceProtosAtt resultAttachmentPointer.digest = [NSData data]; return self; } +- (BOOL) hasFileName { + return resultAttachmentPointer.hasFileName; +} +- (NSString*) fileName { + return resultAttachmentPointer.fileName; +} +- (OWSSignalServiceProtosAttachmentPointerBuilder*) setFileName:(NSString*) value { + resultAttachmentPointer.hasFileName = YES; + resultAttachmentPointer.fileName = value; + return self; +} +- (OWSSignalServiceProtosAttachmentPointerBuilder*) clearFileName { + resultAttachmentPointer.hasFileName = NO; + resultAttachmentPointer.fileName = @""; + return self; +} @end @interface OWSSignalServiceProtosGroupContext () @@ -6001,6 +6052,7 @@ BOOL OWSSignalServiceProtosGroupContextTypeIsValidValue(OWSSignalServiceProtosGr case OWSSignalServiceProtosGroupContextTypeUpdate: case OWSSignalServiceProtosGroupContextTypeDeliver: case OWSSignalServiceProtosGroupContextTypeQuit: + case OWSSignalServiceProtosGroupContextTypeRequestInfo: return YES; default: return NO; @@ -6016,6 +6068,8 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro return @"OWSSignalServiceProtosGroupContextTypeDeliver"; case OWSSignalServiceProtosGroupContextTypeQuit: return @"OWSSignalServiceProtosGroupContextTypeQuit"; + case OWSSignalServiceProtosGroupContextTypeRequestInfo: + return @"OWSSignalServiceProtosGroupContextTypeRequestInfo"; default: return nil; } diff --git a/src/Util/MIMETypeUtil.h b/src/Util/MIMETypeUtil.h index 82d463c9c..8ca864fe6 100644 --- a/src/Util/MIMETypeUtil.h +++ b/src/Util/MIMETypeUtil.h @@ -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; diff --git a/src/Util/MIMETypeUtil.m b/src/Util/MIMETypeUtil.m index d9c38c765..a838a0452 100644 --- a/src/Util/MIMETypeUtil.m +++ b/src/Util/MIMETypeUtil.m @@ -261,7 +261,70 @@ 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 whitespaceAndNewlineCharacterSet], + [NSCharacterSet illegalCharacterSet], + [NSCharacterSet controlCharacterSet], + [NSCharacterSet characterSetWithCharactersInString:@"<>|\\:()&;?*/~"], + ]) { + normalizedFilename = [[normalizedFilename componentsSeparatedByCharactersInSet:invalidCharacterSet] + componentsJoinedByString:@"_"]; + } + + // Remove leading periods to prevent hidden files, + // "." and ".." special file names. + while ([normalizedFilename hasPrefix:@"."]) { + normalizedFilename = [normalizedFilename substringFromIndex:1]; + } + + 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 +354,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 {