// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "keystone_glue.h" @interface KeystoneGlue(Private) // Called periodically to announce activity by pinging the Keystone server. - (void)markActive:(NSTimer*)timer; @end // Provide declarations of the Keystone registration bits needed here. From // KSRegistration.h. typedef enum { kKSPathExistenceChecker } KSExistenceCheckerType; NSString *KSRegistrationCheckForUpdateNotification = @"KSRegistrationCheckForUpdateNotification"; NSString *KSRegistrationStatusKey = @"Status"; NSString *KSRegistrationVersionKey = @"Version"; NSString *KSRegistrationStartUpdateNotification = @"KSRegistrationStartUpdateNotification"; NSString *KSUpdateCheckSuccessfulKey = @"CheckSuccessful"; NSString *KSUpdateCheckSuccessfullyInstalledKey = @"SuccessfullyInstalled"; @interface KSRegistration : NSObject + (id)registrationWithProductID:(NSString*)productID; - (BOOL)registerWithVersion:(NSString*)version existenceCheckerType:(KSExistenceCheckerType)xctype existenceCheckerString:(NSString*)xc serverURLString:(NSString*)serverURLString; - (void)setActive; - (void)checkForUpdate; - (void)startUpdate; @end @implementation KeystoneGlue + (id)defaultKeystoneGlue { // TODO(jrg): rename this file to .mm so I can use C++ and // make this type a base::SingletonObjC. static KeystoneGlue* sDefaultKeystoneGlue = nil; // leaked if (sDefaultKeystoneGlue == nil) { sDefaultKeystoneGlue = [[KeystoneGlue alloc] init]; [sDefaultKeystoneGlue loadParameters]; if (![sDefaultKeystoneGlue loadKeystoneRegistration]) { [sDefaultKeystoneGlue release]; sDefaultKeystoneGlue = nil; } } return sDefaultKeystoneGlue; } - (void)dealloc { [url_ release]; [productID_ release]; [version_ release]; [registration_ release]; [super dealloc]; } - (NSDictionary*)infoDictionary { return [[NSBundle mainBundle] infoDictionary]; } - (void)loadParameters { NSDictionary* infoDictionary = [self infoDictionary]; NSString* url = [infoDictionary objectForKey:@"KSUpdateURL"]; NSString* product = [infoDictionary objectForKey:@"KSProductID"]; if (product == nil) { product = [[NSBundle mainBundle] bundleIdentifier]; } NSString* version = [infoDictionary objectForKey:@"KSVersion"]; if (!product || !url || !version) { // If parameters required for Keystone are missing, don't use it. return; } url_ = [url retain]; productID_ = [product retain]; version_ = [version retain]; } - (BOOL)loadKeystoneRegistration { if (!productID_ || !url_ || !version_) return NO; // Load the KeystoneRegistration framework bundle. NSBundle* mainBundle = [NSBundle mainBundle]; NSString* ksrPath = [[mainBundle privateFrameworksPath] stringByAppendingPathComponent:@"KeystoneRegistration.framework"]; NSBundle* ksrBundle = [NSBundle bundleWithPath:ksrPath]; [ksrBundle load]; // Harness the KSRegistration class. Class ksrClass = [ksrBundle classNamed:@"KSRegistration"]; KSRegistration* ksr = [ksrClass registrationWithProductID:productID_]; if (!ksr) return NO; registration_ = [ksr retain]; return YES; } - (void)registerWithKeystone { [registration_ registerWithVersion:version_ existenceCheckerType:kKSPathExistenceChecker existenceCheckerString:[[NSBundle mainBundle] bundlePath] serverURLString:url_]; // Mark an active RIGHT NOW; don't wait an hour for the first one. [registration_ setActive]; // Set up hourly activity pings. timer_ = [NSTimer scheduledTimerWithTimeInterval:60 * 60 // One hour target:self selector:@selector(markActive:) userInfo:registration_ repeats:YES]; } - (void)stopTimer { [timer_ invalidate]; } - (void)markActive:(NSTimer*)timer { KSRegistration* ksr = [timer userInfo]; [ksr setActive]; } - (void)checkComplete:(NSNotification *)notification { NSDictionary *userInfo = [notification userInfo]; BOOL updatesAvailable = [[userInfo objectForKey:KSRegistrationStatusKey] boolValue]; NSString *latestVersion = [userInfo objectForKey:KSRegistrationVersionKey]; [checkTarget_ upToDateCheckCompleted:updatesAvailable latestVersion:latestVersion]; [checkTarget_ release]; checkTarget_ = nil; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center removeObserver:self name:KSRegistrationCheckForUpdateNotification object:nil]; } - (BOOL)checkForUpdate:(NSObject*)target { if (registration_ == nil) return NO; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(checkComplete:) name:KSRegistrationCheckForUpdateNotification object:nil]; checkTarget_ = [target retain]; [registration_ checkForUpdate]; return YES; } - (void)startUpdateComplete:(NSNotification *)notification { NSDictionary *userInfo = [notification userInfo]; BOOL checkSuccessful = [[userInfo objectForKey:KSUpdateCheckSuccessfulKey] boolValue]; int installs = [[userInfo objectForKey:KSUpdateCheckSuccessfullyInstalledKey] intValue]; [startTarget_ updateCompleted:checkSuccessful installs:installs]; [startTarget_ release]; startTarget_ = nil; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center removeObserver:self name:KSRegistrationStartUpdateNotification object:nil]; } - (BOOL)startUpdate:(NSObject*)target { if (registration_ == nil) return NO; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(startUpdateComplete:) name:KSRegistrationStartUpdateNotification object:nil]; startTarget_ = [target retain]; [registration_ startUpdate]; return YES; } @end