Без опису

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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 "FIRInstanceIDStore.h"
  17. #import "FIRInstanceIDCheckinPreferences.h"
  18. #import "FIRInstanceIDCheckinStore.h"
  19. #import "FIRInstanceIDConstants.h"
  20. #import "FIRInstanceIDLogger.h"
  21. #import "FIRInstanceIDTokenStore.h"
  22. #import "FIRInstanceIDVersionUtilities.h"
  23. // NOTE: These values should be in sync with what InstanceID saves in as.
  24. static NSString *const kCheckinFileName = @"g-checkin";
  25. // APNS token (use the old key value i.e. with prefix GMS)
  26. static NSString *const kFIRInstanceIDLibraryVersion = @"GMSInstanceID-version";
  27. @interface FIRInstanceIDStore ()
  28. @property(nonatomic, readwrite, strong) FIRInstanceIDCheckinStore *checkinStore;
  29. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenStore *tokenStore;
  30. @end
  31. @implementation FIRInstanceIDStore
  32. - (instancetype)initWithDelegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate {
  33. FIRInstanceIDCheckinStore *checkinStore = [[FIRInstanceIDCheckinStore alloc]
  34. initWithCheckinPlistFileName:kCheckinFileName
  35. subDirectoryName:kFIRInstanceIDSubDirectoryName];
  36. FIRInstanceIDTokenStore *tokenStore = [FIRInstanceIDTokenStore defaultStore];
  37. return [self initWithCheckinStore:checkinStore tokenStore:tokenStore delegate:delegate];
  38. }
  39. - (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore
  40. tokenStore:(FIRInstanceIDTokenStore *)tokenStore
  41. delegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate {
  42. self = [super init];
  43. if (self) {
  44. _checkinStore = checkinStore;
  45. _tokenStore = tokenStore;
  46. _delegate = delegate;
  47. [self resetCredentialsIfNeeded];
  48. }
  49. return self;
  50. }
  51. #pragma mark - Upgrades
  52. + (BOOL)hasSubDirectory:(NSString *)subDirectoryName {
  53. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  54. BOOL isDirectory;
  55. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  56. isDirectory:&isDirectory]) {
  57. return NO;
  58. } else if (!isDirectory) {
  59. return NO;
  60. }
  61. return YES;
  62. }
  63. + (NSSearchPathDirectory)supportedDirectory {
  64. #if TARGET_OS_TV
  65. return NSCachesDirectory;
  66. #else
  67. return NSApplicationSupportDirectory;
  68. #endif
  69. }
  70. + (NSString *)pathForSupportSubDirectory:(NSString *)subDirectoryName {
  71. NSArray *directoryPaths =
  72. NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
  73. NSString *dirPath = directoryPaths.lastObject;
  74. NSArray *components = @[ dirPath, subDirectoryName ];
  75. return [NSString pathWithComponents:components];
  76. }
  77. + (BOOL)createSubDirectory:(NSString *)subDirectoryName {
  78. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  79. BOOL hasSubDirectory;
  80. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  81. isDirectory:&hasSubDirectory]) {
  82. NSError *error;
  83. [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath
  84. withIntermediateDirectories:YES
  85. attributes:nil
  86. error:&error];
  87. if (error) {
  88. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore000,
  89. @"Cannot create directory %@, error: %@", subDirectoryPath, error);
  90. return NO;
  91. }
  92. } else {
  93. if (!hasSubDirectory) {
  94. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore001,
  95. @"Found file instead of directory at %@", subDirectoryPath);
  96. return NO;
  97. }
  98. }
  99. return YES;
  100. }
  101. + (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error {
  102. if ([self hasSubDirectory:subDirectoryName]) {
  103. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  104. BOOL isDirectory;
  105. if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  106. isDirectory:&isDirectory]) {
  107. return [[NSFileManager defaultManager] removeItemAtPath:subDirectoryPath error:error];
  108. }
  109. }
  110. return YES;
  111. }
  112. /**
  113. * Reset the keychain preferences if the app had been deleted earlier and then reinstalled.
  114. * Keychain preferences are not cleared in the above scenario so explicitly clear them.
  115. *
  116. * In case of an iCloud backup and restore the Keychain preferences should already be empty
  117. * since the Keychain items are marked with `*BackupThisDeviceOnly`.
  118. */
  119. - (void)resetCredentialsIfNeeded {
  120. BOOL checkinPlistExists = [self.checkinStore hasCheckinPlist];
  121. // Checkin info existed in backup excluded plist. Should not be a fresh install.
  122. if (checkinPlistExists) {
  123. // FCM user can still have the old version of checkin, migration should only happen once.
  124. [self.checkinStore migrateCheckinItemIfNeeded];
  125. return;
  126. }
  127. // reset checkin in keychain if a fresh install.
  128. // set the old checkin preferences to unregister pre-registered tokens
  129. FIRInstanceIDCheckinPreferences *oldCheckinPreferences =
  130. [self.checkinStore cachedCheckinPreferences];
  131. if (oldCheckinPreferences) {
  132. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  133. if (!error) {
  134. FIRInstanceIDLoggerDebug(
  135. kFIRInstanceIDMessageCodeStore002,
  136. @"Removed cached checkin preferences from Keychain because this is a fresh install.");
  137. } else {
  138. FIRInstanceIDLoggerError(
  139. kFIRInstanceIDMessageCodeStore003,
  140. @"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error);
  141. }
  142. if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) {
  143. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006,
  144. @"App reset detected. Will delete server registrations.");
  145. // We don't really need to delete old FCM tokens created via IID auth tokens since
  146. // those tokens are already hashed by APNS token as the has so creating a new
  147. // token should automatically delete the old-token.
  148. [self.delegate store:self didDeleteFCMScopedTokensForCheckin:oldCheckinPreferences];
  149. } else {
  150. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore009,
  151. @"App reset detected but no valid checkin auth preferences found."
  152. @" Will not delete server registrations.");
  153. }
  154. }];
  155. }
  156. }
  157. #pragma mark - Get
  158. - (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity
  159. scope:(NSString *)scope {
  160. // TODO(chliangGoogle): If we don't have the token plist we should delete all the tokens from
  161. // the keychain. This is because not having the plist signifies a backup and restore operation.
  162. // In case the keychain has any tokens these would now be stale and therefore should be
  163. // deleted.
  164. if (![authorizedEntity length] || ![scope length]) {
  165. return nil;
  166. }
  167. FIRInstanceIDTokenInfo *info = [self.tokenStore tokenInfoWithAuthorizedEntity:authorizedEntity
  168. scope:scope];
  169. return info;
  170. }
  171. - (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos {
  172. return [self.tokenStore cachedTokenInfos];
  173. }
  174. #pragma mark - Save
  175. - (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo
  176. handler:(void (^)(NSError *error))handler {
  177. [self.tokenStore saveTokenInfo:tokenInfo handler:handler];
  178. }
  179. #pragma mark - Delete
  180. - (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope {
  181. if (![authorizedEntity length] || ![scope length]) {
  182. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore012,
  183. @"Will not delete token with invalid entity: %@, scope: %@",
  184. authorizedEntity, scope);
  185. return;
  186. }
  187. [self.tokenStore removeTokenWithAuthorizedEntity:authorizedEntity scope:scope];
  188. }
  189. - (void)removeAllCachedTokensWithHandler:(void (^)(NSError *error))handler {
  190. [self.tokenStore removeAllTokensWithHandler:handler];
  191. }
  192. #pragma mark - FIRInstanceIDCheckinCache protocol
  193. - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
  194. handler:(void (^)(NSError *error))handler {
  195. [self.checkinStore saveCheckinPreferences:preferences handler:handler];
  196. }
  197. - (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
  198. return [self.checkinStore cachedCheckinPreferences];
  199. }
  200. - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *))handler {
  201. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  202. if (handler) {
  203. handler(error);
  204. }
  205. }];
  206. }
  207. @end