|
|
/* * Copyright 2019 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
#import "FIRInstanceIDStore.h"
#import "FIRInstanceIDCheckinPreferences.h" #import "FIRInstanceIDCheckinStore.h" #import "FIRInstanceIDConstants.h" #import "FIRInstanceIDLogger.h" #import "FIRInstanceIDTokenStore.h" #import "FIRInstanceIDVersionUtilities.h"
// NOTE: These values should be in sync with what InstanceID saves in as. static NSString *const kCheckinFileName = @"g-checkin";
// APNS token (use the old key value i.e. with prefix GMS) static NSString *const kFIRInstanceIDLibraryVersion = @"GMSInstanceID-version";
@interface FIRInstanceIDStore ()
@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinStore *checkinStore; @property(nonatomic, readwrite, strong) FIRInstanceIDTokenStore *tokenStore;
@end
@implementation FIRInstanceIDStore
- (instancetype)initWithDelegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate { FIRInstanceIDCheckinStore *checkinStore = [[FIRInstanceIDCheckinStore alloc] initWithCheckinPlistFileName:kCheckinFileName subDirectoryName:kFIRInstanceIDSubDirectoryName];
FIRInstanceIDTokenStore *tokenStore = [FIRInstanceIDTokenStore defaultStore];
return [self initWithCheckinStore:checkinStore tokenStore:tokenStore delegate:delegate]; }
- (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore tokenStore:(FIRInstanceIDTokenStore *)tokenStore delegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate { self = [super init]; if (self) { _checkinStore = checkinStore; _tokenStore = tokenStore; _delegate = delegate; [self resetCredentialsIfNeeded]; } return self; }
#pragma mark - Upgrades
+ (BOOL)hasSubDirectory:(NSString *)subDirectoryName { NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; BOOL isDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath isDirectory:&isDirectory]) { return NO; } else if (!isDirectory) { return NO; } return YES; }
+ (NSSearchPathDirectory)supportedDirectory { #if TARGET_OS_TV return NSCachesDirectory; #else return NSApplicationSupportDirectory; #endif }
+ (NSString *)pathForSupportSubDirectory:(NSString *)subDirectoryName { NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); NSString *dirPath = directoryPaths.lastObject; NSArray *components = @[ dirPath, subDirectoryName ]; return [NSString pathWithComponents:components]; }
+ (BOOL)createSubDirectory:(NSString *)subDirectoryName { NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; BOOL hasSubDirectory;
if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath isDirectory:&hasSubDirectory]) { NSError *error; [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error]; if (error) { FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore000, @"Cannot create directory %@, error: %@", subDirectoryPath, error); return NO; } } else { if (!hasSubDirectory) { FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore001, @"Found file instead of directory at %@", subDirectoryPath); return NO; } } return YES; }
+ (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error { if ([self hasSubDirectory:subDirectoryName]) { NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; BOOL isDirectory; if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath isDirectory:&isDirectory]) { return [[NSFileManager defaultManager] removeItemAtPath:subDirectoryPath error:error]; } } return YES; }
/** * Reset the keychain preferences if the app had been deleted earlier and then reinstalled. * Keychain preferences are not cleared in the above scenario so explicitly clear them. * * In case of an iCloud backup and restore the Keychain preferences should already be empty * since the Keychain items are marked with `*BackupThisDeviceOnly`. */ - (void)resetCredentialsIfNeeded { BOOL checkinPlistExists = [self.checkinStore hasCheckinPlist]; // Checkin info existed in backup excluded plist. Should not be a fresh install. if (checkinPlistExists) { return; }
// Resets checkin in keychain if a fresh install. // Keychain can still exist even if app is uninstalled. FIRInstanceIDCheckinPreferences *oldCheckinPreferences = [self.checkinStore cachedCheckinPreferences];
if (oldCheckinPreferences) { [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) { if (!error) { FIRInstanceIDLoggerDebug( kFIRInstanceIDMessageCodeStore002, @"Removed cached checkin preferences from Keychain because this is a fresh install."); } else { FIRInstanceIDLoggerError( kFIRInstanceIDMessageCodeStore003, @"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error); } if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) { FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006, @"App reset detected. Will delete server registrations."); // We don't really need to delete old FCM tokens created via IID auth tokens since // those tokens are already hashed by APNS token as the has so creating a new // token should automatically delete the old-token. [self.delegate store:self didDeleteFCMScopedTokensForCheckin:oldCheckinPreferences]; } else { FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore009, @"App reset detected but no valid checkin auth preferences found." @" Will not delete server registrations."); } }]; } }
#pragma mark - Get
- (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope { // TODO(chliangGoogle): If we don't have the token plist we should delete all the tokens from // the keychain. This is because not having the plist signifies a backup and restore operation. // In case the keychain has any tokens these would now be stale and therefore should be // deleted. if (![authorizedEntity length] || ![scope length]) { return nil; } FIRInstanceIDTokenInfo *info = [self.tokenStore tokenInfoWithAuthorizedEntity:authorizedEntity scope:scope]; return info; }
- (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos { return [self.tokenStore cachedTokenInfos]; }
#pragma mark - Save
- (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo handler:(void (^)(NSError *error))handler { [self.tokenStore saveTokenInfo:tokenInfo handler:handler]; }
#pragma mark - Delete
- (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope { if (![authorizedEntity length] || ![scope length]) { FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore012, @"Will not delete token with invalid entity: %@, scope: %@", authorizedEntity, scope); return; } [self.tokenStore removeTokenWithAuthorizedEntity:authorizedEntity scope:scope]; }
- (void)removeAllCachedTokensWithHandler:(void (^)(NSError *error))handler { [self.tokenStore removeAllTokensWithHandler:handler]; }
#pragma mark - FIRInstanceIDCheckinCache protocol
- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences handler:(void (^)(NSError *error))handler { [self.checkinStore saveCheckinPreferences:preferences handler:handler]; }
- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences { return [self.checkinStore cachedCheckinPreferences]; }
- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *))handler { [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) { if (handler) { handler(error); } }]; }
@end
|