721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott/*
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * The contents of this file are subject to the terms of the Common Development and
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Distribution License (the License). You may not use this file except in compliance with the
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * License.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott *
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * specific language governing permission and limitations under the License.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott *
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * When distributing Covered Software, include this CDDL Header Notice in each file and include
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Header, with the fields enclosed by brackets [] replaced by your own identifying
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * information: "Portions copyright [year] [name of copyright owner]".
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott *
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Copyright 2016 ForgeRock AS.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott */
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FMDatabase.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRADatabaseConfiguration.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAFMDatabaseFactory.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAFMDatabaseConnectionHelper.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAError.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott/*!
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Responsible for providing access to the Database connection for the caller.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * This includes managing detail around initialising the schema for the database if
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * this is the first time the user has setup the App on their device.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott */
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott@implementation FRAFMDatabaseConnectionHelper {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FRADatabaseConfiguration* _configuration;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FRAFMDatabaseFactory* _factory;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott BOOL initialised;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott- (instancetype)initWithConfiguration:(FRADatabaseConfiguration *)configuration databaseFactory:(FRAFMDatabaseFactory *)factory {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott self = [super init];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (self) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott _configuration = configuration;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott _factory = factory;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott initialised = NO;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return self;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott# pragma --
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott# pragma mark Public functions
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott-(FMDatabase *)getConnectionWithError:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (!initialised && ![self checkDatabaseInitialised:error]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return nil;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return [self internalGetConnectionWithError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott-(void)closeConnectionToDatabase:(FMDatabase *)database {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (database == nil) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Placeholder for any additional cleanup or shutdown calls.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Close the connection.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Closing database connection: %@", database);
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott BOOL result = [database close];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Database closed?: %@", result ? @"YES" : @"NO");
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott# pragma --
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott# pragma mark Internal database management functions
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott/*!
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Internal call to intiailise the database.
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * @return An initialised instance of FMDatabase, or nil if there was an error.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott */
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott-(FMDatabase *)internalGetConnectionWithError:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString* databasePath = [_configuration getDatabasePathWithError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (databasePath == nil) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return nil;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Open the Database
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Database path: %@", databasePath);
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FMDatabase *database = [_factory createDatabaseFor:databasePath withError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (database == nil) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return nil;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (![database open]) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni if (error) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni *error = [FRAError createErrorForLastFailure:database];
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return nil;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Database opened: %@", database);
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return database;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott/*!
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Internal call to check if the database schema has been initialised.
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL.
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @return Whether the database has been initialised.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott */
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott-(BOOL)checkDatabaseInitialised:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FMDatabase* database;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott @try {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott database = [self internalGetConnectionWithError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (database == nil) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return NO;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott int query = [self queryDatabaseSchema:database withError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (query == -1) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return NO;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott } else if (query == 0) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Database is not initialised");
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if ([self initialiseSchema:database withError:error]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott initialised = YES;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott } else {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return NO;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott } else if (query == 1) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott initialised = YES;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return YES;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott @finally {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott [self closeConnectionToDatabase:database];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott/*!
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * A simple check to indicate if we need to setup the database schema or not.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott *
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Note: Three return states required for this function, thus using an int.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott *
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @param database The database.
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * @return -1 if there was an error, 0 if the database is not yet setup and 1 if the database is setup.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott */
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott- (int)queryDatabaseSchema: (FMDatabase *) database withError:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString* query = [FRAFMDatabaseConnectionHelper readSchema:@"init_check" withError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (query == nil) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return -1;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FMResultSet* results = [database executeQuery:query];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (results == nil) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni if (error) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni *error = [FRAError createErrorForLastFailure:database];
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return -1;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSMutableArray *names = [[NSMutableArray alloc] init];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott while ([results next]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott [names addObject:[results stringForColumnIndex:1]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott BOOL result = [names containsObject:@"identity"] &&
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott [names containsObject:@"mechanism"] &&
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott [names containsObject:@"notification"];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Database setup: %@", result ? @"YES" : @"NO");
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return result ? 1 : 0;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott/*!
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Internal function to initialise database schema.
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @param database The database.
7c51d54f23c5581d2cf894f9eafb9798e3febd22Diego Colantoni * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * @throws FRADatabaseException If the statement failed to execute.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott */
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott- (BOOL)initialiseSchema:(FMDatabase *)database withError:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString* schema = [FRAFMDatabaseConnectionHelper readSchema:@"schema" withError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (schema == nil) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return NO;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott BOOL result = [database executeStatements:schema];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (!result) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni if (error) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni *error = [FRAError createErrorForLastFailure:database];
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return NO;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Database setup complete");
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return YES;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#pragma mark --
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#pragma mark file functions
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott+ (NSString *)readSchema:(NSString *)schemaName withError:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *extension = @"sql";
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Locate in App bundle
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *path = [[NSBundle mainBundle] pathForResource:schemaName ofType:extension];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (path == nil) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni if (error) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni *error = [FRAError createErrorForFilePath:schemaName reason:@"Could not find schema file"];
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return nil;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Read contents into String
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *result = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (result == nil) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni if (error) {
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni *error = [FRAError createErrorForFilePath:path reason:@"Could not read contents"];
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return nil;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott }
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott return result;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott}
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott@end