FIRStorageMetadata.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Copyright 2017 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FIRStorageMetadata.h"
  15. #import "FIRStorageConstants.h"
  16. #import "FIRStorageConstants_Private.h"
  17. #import "FIRStorageMetadata_Private.h"
  18. #import "FIRStorageUtils.h"
  19. // TODO: consider rewriting this using GTLR (GTLRStorageObjects.h)
  20. @implementation FIRStorageMetadata
  21. #pragma mark - Initializers
  22. - (instancetype)init {
  23. return [self initWithDictionary:[NSDictionary dictionary]];
  24. }
  25. - (instancetype)initWithDictionary:(NSDictionary *)dictionary {
  26. self = [super init];
  27. if (self) {
  28. _initialMetadata = [dictionary copy];
  29. _bucket = dictionary[kFIRStorageMetadataBucket];
  30. _cacheControl = dictionary[kFIRStorageMetadataCacheControl];
  31. _contentDisposition = dictionary[kFIRStorageMetadataContentDisposition];
  32. _contentEncoding = dictionary[kFIRStorageMetadataContentEncoding];
  33. _contentLanguage = dictionary[kFIRStorageMetadataContentLanguage];
  34. _contentType = dictionary[kFIRStorageMetadataContentType];
  35. _customMetadata = dictionary[kFIRStorageMetadataCustomMetadata];
  36. _size = [dictionary[kFIRStorageMetadataSize] longLongValue];
  37. _downloadURLs = dictionary[kFIRStorageMetadataDownloadURLs];
  38. _generation = [dictionary[kFIRStorageMetadataGeneration] longLongValue];
  39. _metageneration = [dictionary[kFIRStorageMetadataMetageneration] longLongValue];
  40. _timeCreated = [self dateFromRFC3339String:dictionary[kFIRStorageMetadataTimeCreated]];
  41. _updated = [self dateFromRFC3339String:dictionary[kFIRStorageMetadataUpdated]];
  42. _md5Hash = dictionary[kFIRStorageMetadataMd5Hash];
  43. // GCS "name" is our path, our "name" is just the last path component of the path
  44. _path = dictionary[kFIRStorageMetadataName];
  45. _name = [_path lastPathComponent];
  46. NSString *downloadTokens = dictionary[kFIRStorageMetadataDownloadTokens];
  47. if (downloadTokens) {
  48. NSArray<NSString *> *downloadStringArray = [downloadTokens componentsSeparatedByString:@","];
  49. NSMutableArray<NSURL *> *downloadURLArray =
  50. [[NSMutableArray alloc] initWithCapacity:[downloadStringArray count]];
  51. [downloadStringArray enumerateObjectsUsingBlock:^(NSString *_Nonnull token, NSUInteger idx,
  52. BOOL *_Nonnull stop) {
  53. NSURLComponents *components = [[NSURLComponents alloc] init];
  54. components.scheme = kFIRStorageScheme;
  55. components.host = kFIRStorageHost;
  56. NSString *path = [FIRStorageUtils GCSEscapedString:self->_path];
  57. NSString *fullPath =
  58. [NSString stringWithFormat:kFIRStorageFullPathFormat, self->_bucket, path];
  59. components.percentEncodedPath = fullPath;
  60. components.query = [NSString stringWithFormat:@"alt=media&token=%@", token];
  61. [downloadURLArray insertObject:[components URL] atIndex:idx];
  62. }];
  63. _downloadURLs = downloadURLArray;
  64. }
  65. }
  66. return self;
  67. }
  68. #pragma mark - NSObject overrides
  69. - (instancetype)copyWithZone:(NSZone *)zone {
  70. FIRStorageMetadata *clone =
  71. [[[self class] allocWithZone:zone] initWithDictionary:[self dictionaryRepresentation]];
  72. clone.initialMetadata = [self.initialMetadata copy];
  73. return clone;
  74. }
  75. - (BOOL)isEqual:(id)object {
  76. if (self == object) {
  77. return YES;
  78. }
  79. if (![object isKindOfClass:[FIRStorageMetadata class]]) {
  80. return NO;
  81. }
  82. BOOL isEqualObject = [self isEqualToFIRStorageMetadata:(FIRStorageMetadata *)object];
  83. return isEqualObject;
  84. }
  85. - (BOOL)isEqualToFIRStorageMetadata:(FIRStorageMetadata *)metadata {
  86. return [[self dictionaryRepresentation] isEqualToDictionary:[metadata dictionaryRepresentation]];
  87. }
  88. - (NSUInteger)hash {
  89. NSUInteger hash = [[self dictionaryRepresentation] hash];
  90. return hash;
  91. }
  92. - (NSString *)description {
  93. NSDictionary *metadataDictionary = [self dictionaryRepresentation];
  94. return [NSString stringWithFormat:@"%@ %p: %@", [self class], self, metadataDictionary];
  95. }
  96. #pragma mark - Public methods
  97. - (NSDictionary *)dictionaryRepresentation {
  98. NSMutableDictionary *metadataDictionary = [[NSMutableDictionary alloc] initWithCapacity:13];
  99. if (_bucket) {
  100. metadataDictionary[kFIRStorageMetadataBucket] = _bucket;
  101. }
  102. if (_cacheControl) {
  103. metadataDictionary[kFIRStorageMetadataCacheControl] = _cacheControl;
  104. }
  105. if (_contentDisposition) {
  106. metadataDictionary[kFIRStorageMetadataContentDisposition] = _contentDisposition;
  107. }
  108. if (_contentEncoding) {
  109. metadataDictionary[kFIRStorageMetadataContentEncoding] = _contentEncoding;
  110. }
  111. if (_contentLanguage) {
  112. metadataDictionary[kFIRStorageMetadataContentLanguage] = _contentLanguage;
  113. }
  114. if (_contentType) {
  115. metadataDictionary[kFIRStorageMetadataContentType] = _contentType;
  116. }
  117. if (_md5Hash) {
  118. metadataDictionary[kFIRStorageMetadataMd5Hash] = _md5Hash;
  119. }
  120. if (_customMetadata) {
  121. metadataDictionary[kFIRStorageMetadataCustomMetadata] = _customMetadata;
  122. }
  123. if (_downloadURLs) {
  124. NSMutableArray *downloadTokens = [[NSMutableArray alloc] init];
  125. [_downloadURLs
  126. enumerateObjectsUsingBlock:^(NSURL *_Nonnull URL, NSUInteger idx, BOOL *_Nonnull stop) {
  127. NSArray *queryItems = [URL.query componentsSeparatedByString:@"&"];
  128. [queryItems enumerateObjectsUsingBlock:^(NSString *queryString, NSUInteger idx,
  129. BOOL *_Nonnull stop) {
  130. NSString *key;
  131. NSString *value;
  132. NSScanner *scanner = [NSScanner scannerWithString:queryString];
  133. [scanner scanUpToString:@"=" intoString:&key];
  134. [scanner scanString:@"=" intoString:NULL];
  135. [scanner scanUpToString:@"\n" intoString:&value];
  136. if ([key isEqual:@"token"]) {
  137. [downloadTokens addObject:value];
  138. *stop = YES;
  139. }
  140. }];
  141. }];
  142. NSString *downloadTokenString = [downloadTokens componentsJoinedByString:@","];
  143. metadataDictionary[kFIRStorageMetadataDownloadTokens] = downloadTokenString;
  144. }
  145. if (_generation) {
  146. NSString *generationString = [NSString stringWithFormat:@"%lld", _generation];
  147. metadataDictionary[kFIRStorageMetadataGeneration] = generationString;
  148. }
  149. if (_metageneration) {
  150. NSString *metagenerationString = [NSString stringWithFormat:@"%lld", _metageneration];
  151. metadataDictionary[kFIRStorageMetadataMetageneration] = metagenerationString;
  152. }
  153. if (_timeCreated) {
  154. metadataDictionary[kFIRStorageMetadataTimeCreated] = [self RFC3339StringFromDate:_timeCreated];
  155. }
  156. if (_updated) {
  157. metadataDictionary[kFIRStorageMetadataUpdated] = [self RFC3339StringFromDate:_updated];
  158. }
  159. if (_path) {
  160. metadataDictionary[kFIRStorageMetadataName] = _path;
  161. }
  162. if (_size) {
  163. metadataDictionary[kFIRStorageMetadataSize] = [NSNumber numberWithLongLong:_size];
  164. }
  165. return [metadataDictionary copy];
  166. }
  167. - (BOOL)isFile {
  168. return _type == FIRStorageMetadataTypeFile;
  169. }
  170. - (BOOL)isFolder {
  171. return _type == FIRStorageMetadataTypeFolder;
  172. }
  173. - (nullable NSURL *)downloadURL {
  174. return [_downloadURLs firstObject];
  175. }
  176. #pragma mark - Private methods
  177. + (void)removeMatchingMetadata:(NSMutableDictionary *)metadata
  178. oldMetadata:(NSDictionary *)oldMetadata {
  179. for (NSString *metadataKey in [oldMetadata allKeys]) {
  180. id oldValue = [oldMetadata objectForKey:metadataKey];
  181. id newValue = [metadata objectForKey:metadataKey];
  182. if (oldValue && !newValue) {
  183. [metadata setObject:[NSNull null] forKey:metadataKey];
  184. } else if ([oldValue isKindOfClass:[NSString class]] &&
  185. [newValue isKindOfClass:[NSString class]]) {
  186. if ([oldValue isEqualToString:newValue]) {
  187. [metadata removeObjectForKey:metadataKey];
  188. }
  189. } else if ([oldValue isKindOfClass:[NSDictionary class]] &&
  190. [newValue isKindOfClass:[NSDictionary class]]) {
  191. NSMutableDictionary *nestedMetadata = [newValue mutableCopy];
  192. [self removeMatchingMetadata:nestedMetadata oldMetadata:oldValue];
  193. [metadata setObject:[nestedMetadata copy] forKey:metadataKey];
  194. }
  195. }
  196. }
  197. - (NSDictionary *)updatedMetadata {
  198. NSMutableDictionary *metadataUpdate = [[self dictionaryRepresentation] mutableCopy];
  199. [FIRStorageMetadata removeMatchingMetadata:metadataUpdate oldMetadata:_initialMetadata];
  200. return [metadataUpdate copy];
  201. }
  202. #pragma mark - RFC 3339 conversions
  203. static NSDateFormatter *sRFC3339DateFormatter;
  204. static void setupDateFormatterOnce(void) {
  205. static dispatch_once_t onceToken;
  206. dispatch_once(&onceToken, ^{
  207. sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
  208. NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
  209. [sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
  210. [sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZZZZZ"];
  211. [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
  212. });
  213. }
  214. - (nullable NSDate *)dateFromRFC3339String:(NSString *)dateString {
  215. setupDateFormatterOnce();
  216. NSDate *rfc3339Date = [sRFC3339DateFormatter dateFromString:dateString];
  217. return rfc3339Date;
  218. }
  219. - (nullable NSString *)RFC3339StringFromDate:(NSDate *)date {
  220. setupDateFormatterOnce();
  221. NSString *rfc3339String = [sRFC3339DateFormatter stringFromDate:date];
  222. return rfc3339String;
  223. }
  224. @end