You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
7.2 KiB

  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRInstanceIDBackupExcludedPlist.h"
  17. #import "FIRInstanceIDLogger.h"
  18. typedef enum : NSUInteger {
  19. FIRInstanceIDPlistDirectoryUnknown,
  20. FIRInstanceIDPlistDirectoryDocuments,
  21. FIRInstanceIDPlistDirectoryApplicationSupport,
  22. } FIRInstanceIDPlistDirectory;
  23. @interface FIRInstanceIDBackupExcludedPlist ()
  24. @property(nonatomic, readwrite, copy) NSString *fileName;
  25. @property(nonatomic, readwrite, copy) NSString *subDirectoryName;
  26. @property(nonatomic, readwrite, assign) BOOL fileInStandardDirectory;
  27. @property(nonatomic, readwrite, strong) NSDictionary *cachedPlistContents;
  28. @end
  29. @implementation FIRInstanceIDBackupExcludedPlist
  30. - (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory {
  31. self = [super init];
  32. if (self) {
  33. _fileName = [fileName copy];
  34. _subDirectoryName = [subDirectory copy];
  35. #if TARGET_OS_IOS
  36. _fileInStandardDirectory = [self moveToApplicationSupportSubDirectory:subDirectory];
  37. #else
  38. // For tvOS and macOS, we never store the content in document folder, so
  39. // the migration is unnecessary.
  40. _fileInStandardDirectory = YES;
  41. #endif
  42. }
  43. return self;
  44. }
  45. - (BOOL)writeDictionary:(NSDictionary *)dict error:(NSError **)error {
  46. NSString *path = [self plistPathInDirectory:[self plistDirectory]];
  47. if (![dict writeToFile:path atomically:YES]) {
  48. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist000,
  49. @"Failed to write to %@.plist", self.fileName);
  50. return NO;
  51. }
  52. // Successfully wrote contents -- change the in-memory contents
  53. self.cachedPlistContents = [dict copy];
  54. NSURL *URL = [NSURL fileURLWithPath:path];
  55. if (error) {
  56. *error = nil;
  57. }
  58. NSDictionary *preferences = [URL resourceValuesForKeys:@[ NSURLIsExcludedFromBackupKey ]
  59. error:error];
  60. if ([preferences[NSURLIsExcludedFromBackupKey] boolValue]) {
  61. return YES;
  62. }
  63. BOOL success = [URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:error];
  64. if (!success) {
  65. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist001,
  66. @"Error excluding %@ from backup, %@", [URL lastPathComponent],
  67. error ? *error : @"");
  68. }
  69. return success;
  70. }
  71. - (BOOL)deleteFile:(NSError **)error {
  72. BOOL success = YES;
  73. NSString *path = [self plistPathInDirectory:[self plistDirectory]];
  74. if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
  75. success = [[NSFileManager defaultManager] removeItemAtPath:path error:error];
  76. }
  77. // remove the in-memory contents
  78. self.cachedPlistContents = nil;
  79. return success;
  80. }
  81. - (NSDictionary *)contentAsDictionary {
  82. if (!self.cachedPlistContents) {
  83. NSString *path = [self plistPathInDirectory:[self plistDirectory]];
  84. if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
  85. self.cachedPlistContents = [[NSDictionary alloc] initWithContentsOfFile:path];
  86. }
  87. }
  88. return self.cachedPlistContents;
  89. }
  90. - (BOOL)moveToApplicationSupportSubDirectory:(NSString *)subDirectoryName {
  91. NSArray *directoryPaths =
  92. NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
  93. // This only going to happen inside iOS so it is an applicationSupportDirectory.
  94. NSString *applicationSupportDirPath = directoryPaths.lastObject;
  95. NSArray *components = @[ applicationSupportDirPath, subDirectoryName ];
  96. NSString *subDirectoryPath = [NSString pathWithComponents:components];
  97. BOOL hasSubDirectory;
  98. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  99. isDirectory:&hasSubDirectory]) {
  100. // Cannot move to non-existent directory
  101. return NO;
  102. }
  103. if ([self doesFileExistInDirectory:FIRInstanceIDPlistDirectoryDocuments]) {
  104. NSString *oldPlistPath = [self plistPathInDirectory:FIRInstanceIDPlistDirectoryDocuments];
  105. NSString *newPlistPath =
  106. [self plistPathInDirectory:FIRInstanceIDPlistDirectoryApplicationSupport];
  107. if ([self doesFileExistInDirectory:FIRInstanceIDPlistDirectoryApplicationSupport]) {
  108. // File exists in both Documents and ApplicationSupport
  109. return NO;
  110. }
  111. NSError *moveError;
  112. if (![[NSFileManager defaultManager] moveItemAtPath:oldPlistPath
  113. toPath:newPlistPath
  114. error:&moveError]) {
  115. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist002,
  116. @"Failed to move file %@ from %@ to %@. Error: %@", self.fileName,
  117. oldPlistPath, newPlistPath, moveError);
  118. return NO;
  119. }
  120. }
  121. // We moved the file if it existed, otherwise we didn't need to do anything
  122. return YES;
  123. }
  124. - (BOOL)doesFileExist {
  125. return [self doesFileExistInDirectory:[self plistDirectory]];
  126. }
  127. #pragma mark - Private
  128. - (FIRInstanceIDPlistDirectory)plistDirectory {
  129. if (_fileInStandardDirectory) {
  130. return FIRInstanceIDPlistDirectoryApplicationSupport;
  131. } else {
  132. return FIRInstanceIDPlistDirectoryDocuments;
  133. };
  134. }
  135. - (NSString *)plistPathInDirectory:(FIRInstanceIDPlistDirectory)directory {
  136. return [self pathWithName:self.fileName inDirectory:directory];
  137. }
  138. - (NSString *)pathWithName:(NSString *)plistName
  139. inDirectory:(FIRInstanceIDPlistDirectory)directory {
  140. NSArray *directoryPaths;
  141. NSArray *components = @[];
  142. NSString *plistNameWithExtension = [NSString stringWithFormat:@"%@.plist", plistName];
  143. switch (directory) {
  144. case FIRInstanceIDPlistDirectoryDocuments:
  145. directoryPaths =
  146. NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  147. components = @[ directoryPaths.lastObject, plistNameWithExtension ];
  148. break;
  149. case FIRInstanceIDPlistDirectoryApplicationSupport:
  150. directoryPaths =
  151. NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
  152. components = @[ directoryPaths.lastObject, _subDirectoryName, plistNameWithExtension ];
  153. break;
  154. default:
  155. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlistInvalidPlistEnum,
  156. @"Invalid plist directory type: %lu", (unsigned long)directory);
  157. NSAssert(NO, @"Invalid plist directory type: %lu", (unsigned long)directory);
  158. break;
  159. }
  160. return [NSString pathWithComponents:components];
  161. }
  162. - (BOOL)doesFileExistInDirectory:(FIRInstanceIDPlistDirectory)directory {
  163. NSString *path = [self plistPathInDirectory:directory];
  164. return [[NSFileManager defaultManager] fileExistsAtPath:path];
  165. }
  166. - (NSSearchPathDirectory)supportedDirectory {
  167. #if TARGET_OS_TV
  168. return NSCachesDirectory;
  169. #else
  170. return NSApplicationSupportDirectory;
  171. #endif
  172. }
  173. @end