FIRStorageMetadata.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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:_path];
  57. NSString *fullPath = [NSString stringWithFormat:kFIRStorageFullPathFormat, _bucket, path];
  58. components.percentEncodedPath = fullPath;
  59. components.query = [NSString stringWithFormat:@"alt=media&token=%@", token];
  60. [downloadURLArray insertObject:[components URL] atIndex:idx];
  61. }];
  62. _downloadURLs = downloadURLArray;
  63. }
  64. }
  65. return self;
  66. }
  67. #pragma mark - NSObject overrides
  68. - (instancetype)copyWithZone:(NSZone *)zone {
  69. FIRStorageMetadata *clone =
  70. [[[self class] allocWithZone:zone] initWithDictionary:[self dictionaryRepresentation]];
  71. clone.initialMetadata = [self.initialMetadata copy];
  72. return clone;
  73. }
  74. - (BOOL)isEqual:(id)object {
  75. if (self == object) {
  76. return YES;
  77. }
  78. if (![object isKindOfClass:[FIRStorageMetadata class]]) {
  79. return NO;
  80. }
  81. BOOL isEqualObject = [self isEqualToFIRStorageMetadata:(FIRStorageMetadata *)object];
  82. return isEqualObject;
  83. }
  84. - (BOOL)isEqualToFIRStorageMetadata:(FIRStorageMetadata *)metadata {
  85. return [[self dictionaryRepresentation] isEqualToDictionary:[metadata dictionaryRepresentation]];
  86. }
  87. - (NSUInteger)hash {
  88. NSUInteger hash = [[self dictionaryRepresentation] hash];
  89. return hash;
  90. }
  91. - (NSString *)description {
  92. NSDictionary *metadataDictionary = [self dictionaryRepresentation];
  93. return [NSString stringWithFormat:@"%@ %p: %@", [self class], self, metadataDictionary];
  94. }
  95. #pragma mark - Public methods
  96. - (NSDictionary *)dictionaryRepresentation {
  97. NSMutableDictionary *metadataDictionary = [[NSMutableDictionary alloc] initWithCapacity:13];
  98. if (_bucket) {
  99. metadataDictionary[kFIRStorageMetadataBucket] = _bucket;
  100. }
  101. if (_cacheControl) {
  102. metadataDictionary[kFIRStorageMetadataCacheControl] = _cacheControl;
  103. }
  104. if (_contentDisposition) {
  105. metadataDictionary[kFIRStorageMetadataContentDisposition] = _contentDisposition;
  106. }
  107. if (_contentEncoding) {
  108. metadataDictionary[kFIRStorageMetadataContentEncoding] = _contentEncoding;
  109. }
  110. if (_contentLanguage) {
  111. metadataDictionary[kFIRStorageMetadataContentLanguage] = _contentLanguage;
  112. }
  113. if (_contentType) {
  114. metadataDictionary[kFIRStorageMetadataContentType] = _contentType;
  115. }
  116. if (_md5Hash) {
  117. metadataDictionary[kFIRStorageMetadataMd5Hash] = _md5Hash;
  118. }
  119. if (_customMetadata) {
  120. metadataDictionary[kFIRStorageMetadataCustomMetadata] = _customMetadata;
  121. }
  122. if (_downloadURLs) {
  123. NSMutableArray *downloadTokens = [[NSMutableArray alloc] init];
  124. [_downloadURLs
  125. enumerateObjectsUsingBlock:^(NSURL *_Nonnull URL, NSUInteger idx, BOOL *_Nonnull stop) {
  126. NSArray *queryItems = [URL.query componentsSeparatedByString:@"&"];
  127. [queryItems enumerateObjectsUsingBlock:^(NSString *queryString, NSUInteger idx,
  128. BOOL *_Nonnull stop) {
  129. NSString *key;
  130. NSString *value;
  131. NSScanner *scanner = [NSScanner scannerWithString:queryString];
  132. [scanner scanUpToString:@"=" intoString:&key];
  133. [scanner scanString:@"=" intoString:NULL];
  134. [scanner scanUpToString:@"\n" intoString:&value];
  135. if ([key isEqual:@"token"]) {
  136. [downloadTokens addObject:value];
  137. *stop = YES;
  138. }
  139. }];
  140. }];
  141. NSString *downloadTokenString = [downloadTokens componentsJoinedByString:@","];
  142. metadataDictionary[kFIRStorageMetadataDownloadTokens] = downloadTokenString;
  143. }
  144. if (_generation) {
  145. NSString *generationString = [NSString stringWithFormat:@"%lld", _generation];
  146. metadataDictionary[kFIRStorageMetadataGeneration] = generationString;
  147. }
  148. if (_metageneration) {
  149. NSString *metagenerationString = [NSString stringWithFormat:@"%lld", _metageneration];
  150. metadataDictionary[kFIRStorageMetadataMetageneration] = metagenerationString;
  151. }
  152. if (_timeCreated) {
  153. metadataDictionary[kFIRStorageMetadataTimeCreated] = [self RFC3339StringFromDate:_timeCreated];
  154. }
  155. if (_updated) {
  156. metadataDictionary[kFIRStorageMetadataUpdated] = [self RFC3339StringFromDate:_updated];
  157. }
  158. if (_path) {
  159. metadataDictionary[kFIRStorageMetadataName] = _path;
  160. }
  161. if (_size) {
  162. metadataDictionary[kFIRStorageMetadataSize] = [NSNumber numberWithLongLong:_size];
  163. }
  164. return [metadataDictionary copy];
  165. }
  166. - (BOOL)isFile {
  167. return _type == FIRStorageMetadataTypeFile;
  168. }
  169. - (BOOL)isFolder {
  170. return _type == FIRStorageMetadataTypeFolder;
  171. }
  172. - (nullable NSURL *)downloadURL {
  173. return [_downloadURLs firstObject];
  174. }
  175. #pragma mark - Private methods
  176. + (void)removeMatchingMetadata:(NSMutableDictionary *)metadata
  177. oldMetadata:(NSDictionary *)oldMetadata {
  178. for (NSString *metadataKey in [oldMetadata allKeys]) {
  179. id oldValue = [oldMetadata objectForKey:metadataKey];
  180. id newValue = [metadata objectForKey:metadataKey];
  181. if (oldValue && !newValue) {
  182. [metadata setObject:[NSNull null] forKey:metadataKey];
  183. } else if ([oldValue isKindOfClass:[NSString class]] &&
  184. [newValue isKindOfClass:[NSString class]]) {
  185. if ([oldValue isEqualToString:newValue]) {
  186. [metadata removeObjectForKey:metadataKey];
  187. }
  188. } else if ([oldValue isKindOfClass:[NSDictionary class]] &&
  189. [newValue isKindOfClass:[NSDictionary class]]) {
  190. NSMutableDictionary *nestedMetadata = [newValue mutableCopy];
  191. [self removeMatchingMetadata:nestedMetadata oldMetadata:oldValue];
  192. [metadata setObject:[nestedMetadata copy] forKey:metadataKey];
  193. }
  194. }
  195. }
  196. - (NSDictionary *)updatedMetadata {
  197. NSMutableDictionary *metadataUpdate = [[self dictionaryRepresentation] mutableCopy];
  198. [FIRStorageMetadata removeMatchingMetadata:metadataUpdate oldMetadata:_initialMetadata];
  199. return [metadataUpdate copy];
  200. }
  201. #pragma mark - RFC 3339 conversions
  202. static NSDateFormatter *sRFC3339DateFormatter;
  203. static void setupDateFormatterOnce(void) {
  204. static dispatch_once_t onceToken;
  205. dispatch_once(&onceToken, ^{
  206. sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
  207. NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
  208. [sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
  209. [sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZZZZZ"];
  210. [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
  211. });
  212. }
  213. - (nullable NSDate *)dateFromRFC3339String:(NSString *)dateString {
  214. setupDateFormatterOnce();
  215. NSDate *rfc3339Date = [sRFC3339DateFormatter dateFromString:dateString];
  216. return rfc3339Date;
  217. }
  218. - (nullable NSString *)RFC3339StringFromDate:(NSDate *)date {
  219. setupDateFormatterOnce();
  220. NSString *rfc3339String = [sRFC3339DateFormatter stringFromDate:date];
  221. return rfc3339String;
  222. }
  223. @end