/*
* $Id: STUpdater.m 227 2011-08-14 12:17:49Z stuart $
*
* SafariTabs
* http://stuconnolly.com/projects/safaritabs/
*
* Copyright (c) 2010 Stuart Connolly. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import "STUpdater.h"
#import "STUtilities.h"
#import "STConstants.h"
#import "STSafariPlugin.h"
#import "STUpdaterProtocol.h"
const CGFloat STDownloadTimeout = 60.0f;
@interface STUpdater ()
- (void)_compareVersions;
- (void)_extractUpdateInformation;
- (void)_notifyDelegateOfUpdateError;
- (void)_notifyDelegateOfDownloadError;
- (NSComparisonResult
)_compareVersion
:(NSString *)versionA toVersion
:(NSString *)versionB;
@end
@implementation STUpdater
/*!
* Checks for SafariTabs updates by downloading the contents of the XML update file on the server.
*
* @param delegate The delegate object that is to be used when checking for and downloading updates.
* @param inBackground Indicates whether the check should table place in the background (i.e. no user interaction).
*/
- (void)checkForUpdatesUsingDelegate:(id)delegate inBackground:(BOOL)inBackground
{
_delegate = delegate;
_inBackground = inBackground;
NSURLRequest *urlRequst
= [NSURLRequest requestWithURL
:[NSURL URLWithString
:[[STPluginBundle
() infoDictionary
] objectForKey
:STUpdateURL
]] cachePolicy
:NSURLRequestReloadIgnoringCacheData timeoutInterval
:60.0];
_urlConnection
= [[NSURLConnection alloc
] initWithRequest
:urlRequst delegate
:self
];
if (_urlConnection) {
}
else {
[self _notifyDelegateOfUpdateError];
}
}
/*!
* Downloads the updated version of SafariTabs from the server.
*/
- (void)downloadUpdate
{
NSURLRequest *urlRequst
= [NSURLRequest requestWithURL
:[NSURL URLWithString
:_downloadURL
] cachePolicy
:NSURLRequestReloadIgnoringCacheData timeoutInterval
:STDownloadTimeout
];
_urlDownload
= [[NSURLDownload alloc
] initWithRequest
:urlRequst delegate
:self
];
if (!_urlDownload) {
[self _notifyDelegateOfDownloadError];
}
}
#pragma mark -
#pragma mark NSURLConnection Delegate Methods
{
// Reset data length
[_infoPlistData setLength:0];
}
{
// Append received data
[_infoPlistData appendData:data];
}
{
[connection release];
[_infoPlistData release];
}
{
NSPropertyListFormat format;
[connection release];
_versionPlist
= [NSPropertyListSerialization propertyListFromData
:_infoPlistData mutabilityOption
:NSPropertyListImmutable format
:&format errorDescription
:&error
];
[self _extractUpdateInformation];
[self _compareVersions];
}
else {
[self _notifyDelegateOfUpdateError];
}
}
#pragma mark -
#pragma mark NSURLDownload Delegate Methods
{
[download setDestination:[[self _downloadDestination] stringByAppendingPathComponent:filename] allowOverwrite:YES];
}
{
[download release];
}
{
[download release];
if ([_delegate respondsToSelector:@selector(updaterDownloadComplete:)]) {
[_delegate updaterDownloadComplete:self];
}
}
/*!
* Extracts the current version of the plugin and the version from the downloaded from the XML and parses them
* as integers.
*/
- (void)_extractUpdateInformation
{
_currentVersionString = [[STPluginBundle() infoDictionary] objectForKey:@"CFBundleShortVersionString"];
_plistVersionString = [_versionPlist objectForKey:STUserVersionKey];
_downloadURL = [_versionPlist objectForKey:STDownloadURLKey];
if (!_downloadURL) {
[self _notifyDelegateOfUpdateError];
}
}
/*!
* Compares the two plugin versions that were extracted using the extractUpdateInformation: method.
*/
- (void)_compareVersions
{
if ([self _compareVersion:_currentVersionString toVersion:_plistVersionString] == NSOrderedAscending) {
if ([_delegate respondsToSelector:@selector(updater:newVersionAvailable:)]) {
[_delegate updater:self newVersionAvailable:_plistVersionString];
}
}
else {
if ([_delegate respondsToSelector:@selector(updaterNoNewVersionAvailable:)] && (!_inBackground)) {
[_delegate updaterNoNewVersionAvailable:self];
}
}
}
/*!
* Notifies the delegate that an error occurred whilst trying to check for updates.
*/
- (void)_notifyDelegateOfUpdateError
{
if ([_delegate respondsToSelector:@selector(updaterCheckForUpdateError:)] && (!_inBackground)) {
[_delegate updaterCheckForUpdateError:self];
}
_delegate = nil;
}
/*!
* Notifies the delegate that an error occurred whilst trying to download the updated version.
*/
- (void)_notifyDelegateOfDownloadError
{
if ([_delegate respondsToSelector:@selector(updaterDownloadUpdateError:)]) {
[_delegate updaterDownloadUpdateError:self];
}
_delegate = nil;
}
/*!
* Determines the download destination by searching for the user's Desktop path. A temporary directory is used
* if it is not found.
*
* @return The download path
*/
{
NSArray *paths
= NSSearchPathForDirectoriesInDomains
(NSDesktopDirectory, NSUserDomainMask,
YES);
return ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
}
/*!
* Compares the two supplied version strings.
*
* @param versionA The first version string of the comparison
* @param versionB The second version string of the comparison
*
* @return An integer (one of NSComparisonResult constants) indicating the result of the comparison.
*/
- (NSComparisonResult
)_compareVersion
:(NSString *)versionA toVersion
:(NSString *)versionB
{
NSUInteger i;
NSArray *versionAComponents
= [versionA componentsSeparatedByString
:STVersionNumberSeparator
];
NSArray *versionBComponents
= [versionB componentsSeparatedByString
:STVersionNumberSeparator
];
NSUInteger aCount = [versionAComponents count];
NSUInteger bCount = [versionBComponents count];
if (aCount > bCount) {
return NSOrderedDescending;
}
else if (bCount > aCount) {
return NSOrderedAscending;
}
// Compare each of the individual version components
for (i = 0; i < [versionAComponents count]; i++)
{
NSInteger intA = [[versionAComponents objectAtIndex:i] integerValue];
NSInteger intB = [[versionBComponents objectAtIndex:i] integerValue];
if (intB > intA) {
return NSOrderedAscending;
}
else if (intB < intA) {
return NSOrderedDescending;
}
}
// If we get here the versions are the same
return NSOrderedSame;
}
@end