~andymatuschak/sparkle/main

1 by andym
Holy restructuring, batman! Watch out for falling folders.
1
//
2
//  SUUpdater.m
3
//  Sparkle
4
//
5
//  Created by Andy Matuschak on 1/4/06.
6
//  Copyright 2006 Andy Matuschak. All rights reserved.
7
//
8
9
#import "SUUpdater.h"
59 by andym
Improved headers from Charles D. H. Williams.
10
242.1.12 by Andy Matuschak
Fixes 243884
11
#import "SUHost.h"
12
#import "SUUpdatePermissionPrompt.h"
13
14
#import "SUAutomaticUpdateDriver.h"
15
#import "SUProbingUpdateDriver.h"
16
#import "SUUserInitiatedUpdateDriver.h"
17
#import "SUScheduledUpdateDriver.h"
18
1 by andym
Holy restructuring, batman! Watch out for falling folders.
19
@interface SUUpdater (Private)
264 by Andy Matuschak
Fixes 253145
20
- initForBundle:(NSBundle *)bundle;
326 by Andy Matuschak
Fixes Bug #312381: "Bundles can't ask for permission to update; developers have to hard-code YES for SUEnableAutomaticChecks"
21
- (void)startUpdateCycle;
242.1.12 by Andy Matuschak
Fixes 243884
22
- (void)checkForUpdatesWithDriver:(SUUpdateDriver *)updateDriver;
286 by Andy Matuschak
Fixes 258547
23
- (BOOL)automaticallyDownloadsUpdates;
197 by Andy Matuschak
Fixed 236240
24
- (void)scheduleNextUpdateCheck;
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
25
- (void)registerAsObserver;
26
- (void)unregisterAsObserver;
27
- (void)updateDriverDidFinish:(NSNotification *)note;
28
- initForBundle:(NSBundle *)bundle;
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
29
- (NSURL *)parameterizedFeedURL;
1 by andym
Holy restructuring, batman! Watch out for falling folders.
30
@end
31
32
@implementation SUUpdater
33
46 by andym
Reorganized SUUpdater.m; it's in better order now.
34
#pragma mark Initialization
35
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
36
static NSMutableDictionary *sharedUpdaters = nil;
37
static NSString *SUUpdaterDefaultsObservationContext = @"SUUpdaterDefaultsObservationContext";
81 by andym
Made a number of fixes to potentially dangerous singleton behavior as suggested by Christiaan Hofman.
38
46 by andym
Reorganized SUUpdater.m; it's in better order now.
39
+ (SUUpdater *)sharedUpdater
40
{
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
41
	return [self updaterForBundle:[NSBundle mainBundle]];
42
}
43
44
// SUUpdater has a singleton for each bundle. We use the fact that NSBundle instances are also singletons, so we can use them as keys. If you don't trust that you can also use the identifier as key
45
+ (SUUpdater *)updaterForBundle:(NSBundle *)bundle
46
{
47
    if (bundle == nil) bundle = [NSBundle mainBundle];
48
	id updater = [sharedUpdaters objectForKey:[NSValue valueWithNonretainedObject:bundle]];
49
	if (updater == nil)
264 by Andy Matuschak
Fixes 253145
50
		updater = [[[self class] alloc] initForBundle:bundle];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
51
	return updater;
52
}
53
54
// This is the designated initializer for SUUpdater, important for subclasses
55
- initForBundle:(NSBundle *)bundle
1 by andym
Holy restructuring, batman! Watch out for falling folders.
56
{
82 by andym
Oops! Committed the last patch with broken code; this code works.
57
	self = [super init];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
58
    if (bundle == nil) bundle = [NSBundle mainBundle];
59
	id updater = [sharedUpdaters objectForKey:[NSValue valueWithNonretainedObject:bundle]];
60
    if (updater)
83 by andym
Finished cleaning up singleton stuff in SUUpdater.m
61
	{
62
		[self release];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
63
		self = [updater retain];
83 by andym
Finished cleaning up singleton stuff in SUUpdater.m
64
	}
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
65
	else if (self)
83 by andym
Finished cleaning up singleton stuff in SUUpdater.m
66
	{
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
67
		if (sharedUpdaters == nil)
68
            sharedUpdaters = [[NSMutableDictionary alloc] init];
69
        [sharedUpdaters setObject:self forKey:[NSValue valueWithNonretainedObject:bundle]];
70
        host = [[SUHost alloc] initWithBundle:bundle];
71
        [self registerAsObserver];
326 by Andy Matuschak
Fixes Bug #312381: "Bundles can't ask for permission to update; developers have to hard-code YES for SUEnableAutomaticChecks"
72
        // This runs the permission prompt if needed, but never before the app has finished launching because the runloop won't run before that
73
        [self performSelector:@selector(startUpdateCycle) withObject:nil afterDelay:0];
62 by andym
Fixed warnings for missing newlines at the end of the file.
74
	}
43 by andym
Beware of falling bricks! Huge refactoring commit #1: cleansing Sparkle of the plague that was SUUtilities.
75
	return self;
1 by andym
Holy restructuring, batman! Watch out for falling folders.
76
}
77
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
78
// This will be used when the updater is instantiated in a nib such as MainMenu
79
- (id)init
80
{
81
    return [self initForBundle:[NSBundle mainBundle]];
82
}
83
294 by Andy Matuschak
Partial fix for 245277: basic descriptions for some Sparkle classes. Thanks to Christiaan Hofman for the patch.
84
- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [host bundlePath]]; }
85
326 by Andy Matuschak
Fixes Bug #312381: "Bundles can't ask for permission to update; developers have to hard-code YES for SUEnableAutomaticChecks"
86
- (void)startUpdateCycle
1 by andym
Holy restructuring, batman! Watch out for falling folders.
87
{
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
88
    BOOL shouldPrompt = NO;
89
    
90
	// If the user has been asked about automatic checks, don't bother prompting
91
	if ([host objectForUserDefaultsKey:SUEnableAutomaticChecksKey])
92
    {
93
        shouldPrompt = NO;
94
    }
95
    // Does the delegate want to take care of the logic for when we should ask permission to update?
242.1.4 by Andy Matuschak
Fixes 244857
96
    else if ([delegate respondsToSelector:@selector(updaterShouldPromptForPermissionToCheckForUpdates:)])
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
97
    {
242.1.4 by Andy Matuschak
Fixes 244857
98
        shouldPrompt = [delegate updaterShouldPromptForPermissionToCheckForUpdates:self];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
99
    }	
100
    // Has he been asked already? And don't ask if the host has a default value set in its Info.plist.
285 by Andy Matuschak
Fixes 245723
101
    else if ([host objectForKey:SUEnableAutomaticChecksKey] == nil)
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
102
    {
103
        if ([host objectForUserDefaultsKey:SUEnableAutomaticChecksKeyOld])
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
104
            [self setAutomaticallyChecksForUpdates:[host boolForUserDefaultsKey:SUEnableAutomaticChecksKeyOld]];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
105
        // Now, we don't want to ask the user for permission to do a weird thing on the first launch.
106
        // We wait until the second launch.
107
        else if ([host boolForUserDefaultsKey:SUHasLaunchedBeforeKey] == NO)
108
            [host setBool:YES forUserDefaultsKey:SUHasLaunchedBeforeKey];
109
        else
110
            shouldPrompt = YES;
111
    }
112
    
113
    if (shouldPrompt)
114
    {
282 by Andy Matuschak
Fixes 251968
115
		NSArray *profileInfo = [host systemProfile];
116
		if ([delegate respondsToSelector:@selector(feedParametersForUpdater:sendingSystemProfile:)])
286 by Andy Matuschak
Fixes 258547
117
			profileInfo = [profileInfo arrayByAddingObjectsFromArray:[delegate feedParametersForUpdater:self sendingSystemProfile:[self sendsSystemProfile]]];		
282 by Andy Matuschak
Fixes 251968
118
        [SUUpdatePermissionPrompt promptWithHost:host systemProfile:profileInfo delegate:self];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
119
        // We start the update checks and register as observer for changes after the prompt finishes
44 by andym
Substantial changes made to how Sparkle works on startup now. SUCheckAtStartup is dead, as are checks on startup. Now Sparkle asks if it can check for updates automatically and does so according to the scheduled check interval, which defaults to a day. Additionally -- THIS IS IMPORTANT -- Sparkle won't prompt the user on the first launch. It waits until the second to improve the first-use experience.
120
	}
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
121
    else 
122
    {
123
        // We check if the user's said they want updates, or they haven't said anything, and the default is set to checking.
124
        [self scheduleNextUpdateCheck];
125
    }
90 by andym
Finished integrating SparklePlus profiling! Just set SUEnableSystemProfiling to YES in your info.plist. The Test App has this on by default so you all can see.
126
}
127
128
- (void)updatePermissionPromptFinishedWithResult:(SUPermissionPromptResult)result
129
{
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
130
	[self setAutomaticallyChecksForUpdates:(result == SUAutomaticallyCheck)];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
131
    // Schedule checks, but make sure we ignore the delayed call from KVO
242.1.8 by Andy Matuschak
Fixes 245991
132
	[self resetUpdateCycle];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
133
}
134
135
- (void)updateDriverDidFinish:(NSNotification *)note
136
{
137
	if ([note object] == driver && [driver finished])
138
	{
139
		[driver release]; driver = nil;
140
		[self scheduleNextUpdateCheck];
141
    }
90 by andym
Finished integrating SparklePlus profiling! Just set SUEnableSystemProfiling to YES in your info.plist. The Test App has this on by default so you all can see.
142
}
143
295 by Andy Matuschak
Fixes 259062
144
- (NSDate *)lastUpdateCheckDate
291.1.1 by Peter Speck
Provide getter for SULastCheckTimeKey
145
{
146
	return [host objectForUserDefaultsKey:SULastCheckTimeKey];
147
}
148
197 by Andy Matuschak
Fixed 236240
149
- (void)scheduleNextUpdateCheck
145 by Andy Matuschak
Removed the checkInterval IV and factored out its assignment to a checkInterval method in SUUpdater. Now when future updates are scheduled after an initial one exits, they'll use the current interval.
150
{	
215 by Andy Matuschak
Removed unnecessary @synchronized's.
151
	if (checkTimer)
197 by Andy Matuschak
Fixed 236240
152
	{
215 by Andy Matuschak
Removed unnecessary @synchronized's.
153
		[checkTimer invalidate];
154
		checkTimer = nil;
197 by Andy Matuschak
Fixed 236240
155
	}
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
156
	if (![self automaticallyChecksForUpdates]) return;
197 by Andy Matuschak
Fixed 236240
157
	
90 by andym
Finished integrating SparklePlus profiling! Just set SUEnableSystemProfiling to YES in your info.plist. The Test App has this on by default so you all can see.
158
	// How long has it been since last we checked for an update?
291.1.1 by Peter Speck
Provide getter for SULastCheckTimeKey
159
	NSDate *lastCheckDate = [self lastUpdateCheckDate];
90 by andym
Finished integrating SparklePlus profiling! Just set SUEnableSystemProfiling to YES in your info.plist. The Test App has this on by default so you all can see.
160
	if (!lastCheckDate) { lastCheckDate = [NSDate distantPast]; }
161
	NSTimeInterval intervalSinceCheck = [[NSDate date] timeIntervalSinceDate:lastCheckDate];
162
	
163
	// Now we want to figure out how long until we check again.
287 by Andy Matuschak
Fixes 258529 and 257967
164
	NSTimeInterval delayUntilCheck, updateCheckInterval = [self updateCheckInterval];
165
	if (updateCheckInterval < SU_MIN_CHECK_INTERVAL)
166
		updateCheckInterval = SU_MIN_CHECK_INTERVAL;
167
	if (intervalSinceCheck < updateCheckInterval)
168
		delayUntilCheck = (updateCheckInterval - intervalSinceCheck); // It hasn't been long enough.
90 by andym
Finished integrating SparklePlus profiling! Just set SUEnableSystemProfiling to YES in your info.plist. The Test App has this on by default so you all can see.
169
	else
170
		delayUntilCheck = 0; // We're overdue! Run one now.
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
171
	checkTimer = [NSTimer scheduledTimerWithTimeInterval:delayUntilCheck target:self selector:@selector(checkForUpdatesInBackground) userInfo:nil repeats:NO];
1 by andym
Holy restructuring, batman! Watch out for falling folders.
172
}
173
174
- (void)checkForUpdatesInBackground
175
{
286 by Andy Matuschak
Fixes 258547
176
	[self checkForUpdatesWithDriver:[[[([self automaticallyDownloadsUpdates] ? [SUAutomaticUpdateDriver class] : [SUScheduledUpdateDriver class]) alloc] initWithUpdater:self] autorelease]];
1 by andym
Holy restructuring, batman! Watch out for falling folders.
177
}
178
179
- (IBAction)checkForUpdates:sender
180
{
281 by Andy Matuschak
No longer fragile-ly using class method singletons to get to the updater's delegate from within the update driver. Update drivers now have an initWithUpdater: method which I use instead.
181
	[self checkForUpdatesWithDriver:[[[SUUserInitiatedUpdateDriver alloc] initWithUpdater:self] autorelease]];
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
182
}
183
242.1.12 by Andy Matuschak
Fixes 243884
184
- (void)checkForUpdateInformation
185
{
281 by Andy Matuschak
No longer fragile-ly using class method singletons to get to the updater's delegate from within the update driver. Update drivers now have an initWithUpdater: method which I use instead.
186
	[self checkForUpdatesWithDriver:[[[SUProbingUpdateDriver alloc] initWithUpdater:self] autorelease]];
242.1.12 by Andy Matuschak
Fixes 243884
187
}
188
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
189
- (void)checkForUpdatesWithDriver:(SUUpdateDriver *)d
190
{
191
	if ([self updateInProgress]) { return; }
215 by Andy Matuschak
Removed unnecessary @synchronized's.
192
	if (checkTimer) { [checkTimer invalidate]; checkTimer = nil; }
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
193
		
295 by Andy Matuschak
Fixes 259062
194
	[self willChangeValueForKey:@"lastUpdateCheckDate"];
195
	[host setObject:[NSDate date] forUserDefaultsKey:SULastCheckTimeKey];
196
	[self didChangeValueForKey:@"lastUpdateCheckDate"];
197
	
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
198
	driver = [d retain];
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
199
	[driver checkForUpdatesAtURL:[self parameterizedFeedURL] host:host];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
200
}
201
202
- (void)registerAsObserver
203
{
204
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDriverDidFinish:) name:SUUpdateDriverFinishedNotification object:nil];
205
    // No sense observing the shared NSUserDefaultsController when we're not updating the main bundle.
206
    if ([host bundle] != [NSBundle mainBundle]) return;
207
    [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey] options:0 context:SUUpdaterDefaultsObservationContext];
208
    [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey] options:0 context:SUUpdaterDefaultsObservationContext];
209
}
210
211
- (void)unregisterAsObserver
212
{
213
	[[NSNotificationCenter defaultCenter] removeObserver:self];
214
    // Removing self as a KVO observer if no observer was registered leads to an NSException. But we don't care.
215
	@try
216
	{
217
		[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey]];
218
		[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey]];
219
	}
220
	@catch (NSException *e) { }
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
221
}
222
223
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
224
{
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
225
	if (context == SUUpdaterDefaultsObservationContext)
226
    {
227
        // Allow a small delay, because perhaps the user or developer wants to change both preferences. This allows the developer to interpret a zero check interval as a sign to disable automatic checking.
228
        // Or we may get this from the developer and from our own KVO observation, this will effectively coalesce them.
242.1.8 by Andy Matuschak
Fixes 245991
229
        [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil];
277 by Andy Matuschak
Fixes 255399
230
        [self performSelector:@selector(resetUpdateCycle) withObject:nil afterDelay:1];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
231
    }
232
    else
233
    {
234
    	[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
235
    }
197 by Andy Matuschak
Fixed 236240
236
}
237
242.1.8 by Andy Matuschak
Fixes 245991
238
- (void)resetUpdateCycle
197 by Andy Matuschak
Fixed 236240
239
{
242.1.8 by Andy Matuschak
Fixes 245991
240
    [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil];
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
241
    [self scheduleNextUpdateCheck];
21 by catlan
#77 SUIgnoreChecks to ignore update, needed for Sparkle in large deployments
242
}
243
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
244
- (void)setAutomaticallyChecksForUpdates:(BOOL)automaticallyCheckForUpdates
245
{
246
	[host setBool:automaticallyCheckForUpdates forUserDefaultsKey:SUEnableAutomaticChecksKey];
287 by Andy Matuschak
Fixes 258529 and 257967
247
	// Hack to support backwards compatibility with older Sparkle versions, which supported
248
	// disabling updates by setting the check interval to 0.
249
    if (automaticallyCheckForUpdates && [self updateCheckInterval] == 0)
250
        [self setUpdateCheckInterval:SU_DEFAULT_CHECK_INTERVAL];
251
    [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil];
252
	// Provide a small delay in case multiple preferences are being updated simultaneously.
253
    [self performSelector:@selector(resetUpdateCycle) withObject:nil afterDelay:1];
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
254
}
255
256
- (BOOL)automaticallyChecksForUpdates
143 by Andy Matuschak
Fixed a confounding bug that somehow slipped in: when an update check was aborted, I was calling -automaticallyUpdates to determine whether I should reschedule another update check.
257
{
287 by Andy Matuschak
Fixes 258529 and 257967
258
	// Don't automatically update when the check interval is 0, to be compatible with 1.1 settings.
259
    if ([self updateCheckInterval] == 0)
260
        return NO;	
286 by Andy Matuschak
Fixes 258547
261
	return [host boolForKey:SUEnableAutomaticChecksKey];
262
}
263
264
- (void)setAutomaticallyDownloadsUpdates:(BOOL)automaticallyUpdates
265
{
266
	[host setBool:automaticallyUpdates forUserDefaultsKey:SUAutomaticallyUpdateKey];
267
}
268
269
- (BOOL)automaticallyDownloadsUpdates
46 by andym
Reorganized SUUpdater.m; it's in better order now.
270
{
271
	// If the SUAllowsAutomaticUpdatesKey exists and is set to NO, return NO.
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
272
	if ([host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] && [host boolForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] == NO)
46 by andym
Reorganized SUUpdater.m; it's in better order now.
273
		return NO;
274
	
286 by Andy Matuschak
Fixes 258547
275
	// Otherwise, automatically downloading updates is allowed. Does the user want it?
276
	return [host boolForUserDefaultsKey:SUAutomaticallyUpdateKey];
46 by andym
Reorganized SUUpdater.m; it's in better order now.
277
}
278
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
279
- (void)setFeedURL:(NSURL *)feedURL
280
{
281
	[host setObject:[feedURL absoluteString] forUserDefaultsKey:SUFeedURLKey];
282
}
283
284
- (NSURL *)feedURL
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
285
{
286
	// A value in the user defaults overrides one in the Info.plist (so preferences panels can be created wherein users choose between beta / release feeds).
285 by Andy Matuschak
Fixes 245723
287
	NSString *appcastString = [host objectForKey:SUFeedURLKey];
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
288
	if (!appcastString) // Can't find an appcast string!
289
		[NSException raise:@"SUNoFeedURL" format:@"You must specify the URL of the appcast as the SUFeedURLKey in either the Info.plist or the user defaults!"];
290
	NSCharacterSet* quoteSet = [NSCharacterSet characterSetWithCharactersInString: @"\"\'"]; // Some feed publishers add quotes; strip 'em.
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
291
	return [NSURL URLWithString:[appcastString stringByTrimmingCharactersInSet:quoteSet]];
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
292
}
293
286 by Andy Matuschak
Fixes 258547
294
- (void)setSendsSystemProfile:(BOOL)sendsSystemProfile
295
{
296
	[host setBool:sendsSystemProfile forUserDefaultsKey:SUSendProfileInfoKey];
297
}
298
299
- (BOOL)sendsSystemProfile
300
{
301
	return [host boolForUserDefaultsKey:SUSendProfileInfoKey];
282 by Andy Matuschak
Fixes 251968
302
}
303
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
304
- (NSURL *)parameterizedFeedURL
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
305
{
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
306
	NSURL *baseFeedURL = [self feedURL];
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
307
	
308
	// Determine all the parameters we're attaching to the base feed URL.
286 by Andy Matuschak
Fixes 258547
309
	BOOL sendingSystemProfile = [self sendsSystemProfile];
265.1.1 by David Smith
A cleaned up and tested version of the one-submit-per-week code
310
268 by Andy Matuschak
As discussed in https://bugs.launchpad.net/sparkle/+bug/243884/comments/11, this patch makes it so that system profiling is only submitted to the server once per week. This will normalize clients' users: those which check daily won't count much more than those which check weekly. Thanks to David Smith for the patch.
311
	// Let's only send the system profiling information once per week at most, so we normalize daily-checkers vs. biweekly-checkers and the such.
265.1.1 by David Smith
A cleaned up and tested version of the one-submit-per-week code
312
	NSDate *lastSubmitDate = [host objectForUserDefaultsKey:SULastProfileSubmitDateKey];
313
	if(!lastSubmitDate)
314
	    lastSubmitDate = [NSDate distantPast];
268 by Andy Matuschak
As discussed in https://bugs.launchpad.net/sparkle/+bug/243884/comments/11, this patch makes it so that system profiling is only submitted to the server once per week. This will normalize clients' users: those which check daily won't count much more than those which check weekly. Thanks to David Smith for the patch.
315
	const NSTimeInterval oneWeek = 60 * 60 * 24 * 7;
316
	sendingSystemProfile &= (-[lastSubmitDate timeIntervalSinceNow] >= oneWeek);
265.1.1 by David Smith
A cleaned up and tested version of the one-submit-per-week code
317
77 by andym
Made a good deal more progress integrating the profile checking features. It should actually work now.
318
	NSArray *parameters = [NSArray array];
242.1.4 by Andy Matuschak
Fixes 244857
319
	if ([delegate respondsToSelector:@selector(feedParametersForUpdater:sendingSystemProfile:)])
320
		parameters = [parameters arrayByAddingObjectsFromArray:[delegate feedParametersForUpdater:self sendingSystemProfile:sendingSystemProfile]];
77 by andym
Made a good deal more progress integrating the profile checking features. It should actually work now.
321
	if (sendingSystemProfile)
268 by Andy Matuschak
As discussed in https://bugs.launchpad.net/sparkle/+bug/243884/comments/11, this patch makes it so that system profiling is only submitted to the server once per week. This will normalize clients' users: those which check daily won't count much more than those which check weekly. Thanks to David Smith for the patch.
322
	{
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
323
		parameters = [parameters arrayByAddingObjectsFromArray:[host systemProfile]];
268 by Andy Matuschak
As discussed in https://bugs.launchpad.net/sparkle/+bug/243884/comments/11, this patch makes it so that system profiling is only submitted to the server once per week. This will normalize clients' users: those which check daily won't count much more than those which check weekly. Thanks to David Smith for the patch.
324
		[host setObject:[NSDate date] forUserDefaultsKey:SULastProfileSubmitDateKey];
325
	}
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
326
	if (parameters == nil || [parameters count] == 0) { return baseFeedURL; }
327
	
328
	// Build up the parameterized URL.
329
	NSMutableArray *parameterStrings = [NSMutableArray array];
330
	NSEnumerator *profileInfoEnumerator = [parameters objectEnumerator];
331
	NSDictionary *currentProfileInfo;
332
	while ((currentProfileInfo = [profileInfoEnumerator nextObject]))
333
		[parameterStrings addObject:[NSString stringWithFormat:@"%@=%@", [currentProfileInfo objectForKey:@"key"], [currentProfileInfo objectForKey:@"value"]]];
334
	
288 by Andy Matuschak
Fixes 258492
335
	NSString *separatorCharacter = @"?";
336
	if ([baseFeedURL query])
337
		separatorCharacter = @"&"; // In case the URL is already http://foo.org/baz.xml?bat=4
290 by Andy Matuschak
Fixed a stupid mistake that prevented my fix for parameterized URLs from actually functioning.
338
	NSString *appcastStringWithProfile = [NSString stringWithFormat:@"%@%@%@", [baseFeedURL absoluteString], separatorCharacter, [parameterStrings componentsJoinedByString:@"&"]];
212 by Andy Matuschak
Killed NSURL+Parameters and moved the relevant code into SUUpdater.
339
	
340
	// Clean it up so it's a valid URL
341
	return [NSURL URLWithString:[appcastStringWithProfile stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
77 by andym
Made a good deal more progress integrating the profile checking features. It should actually work now.
342
}
343
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
344
- (void)setUpdateCheckInterval:(NSTimeInterval)updateCheckInterval
345
{
346
	[host setObject:[NSNumber numberWithDouble:updateCheckInterval] forUserDefaultsKey:SUScheduledCheckIntervalKey];
287 by Andy Matuschak
Fixes 258529 and 257967
347
	if (updateCheckInterval == 0) // For compatibility with 1.1's settings.
348
		[self setAutomaticallyChecksForUpdates:NO];
349
	[[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil];
350
	
351
	// Provide a small delay in case multiple preferences are being updated simultaneously.
352
	[self performSelector:@selector(resetUpdateCycle) withObject:nil afterDelay:1];
242.1.14 by Andy Matuschak
The Sparkle preference keys are now declared private / deprecated / allowed to change without me receiving hate email. However! SUUpdater is now KVC-compliant for:
353
}
354
355
- (NSTimeInterval)updateCheckInterval
145 by Andy Matuschak
Removed the checkInterval IV and factored out its assignment to a checkInterval method in SUUpdater. Now when future updates are scheduled after an initial one exits, they'll use the current interval.
356
{
357
	// Find the stored check interval. User defaults override Info.plist.
287 by Andy Matuschak
Fixes 258529 and 257967
358
	NSNumber *intervalValue = [host objectForKey:SUScheduledCheckIntervalKey];
359
	if (intervalValue)
360
		return [intervalValue doubleValue];
361
	else
362
		return SU_DEFAULT_CHECK_INTERVAL;
145 by Andy Matuschak
Removed the checkInterval IV and factored out its assignment to a checkInterval method in SUUpdater. Now when future updates are scheduled after an initial one exits, they'll use the current interval.
363
}
364
46 by andym
Reorganized SUUpdater.m; it's in better order now.
365
- (void)dealloc
1 by andym
Holy restructuring, batman! Watch out for falling folders.
366
{
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
367
	[self unregisterAsObserver];
368
	[host release];
46 by andym
Reorganized SUUpdater.m; it's in better order now.
369
	if (checkTimer) { [checkTimer invalidate]; }
370
	[super dealloc];
1 by andym
Holy restructuring, batman! Watch out for falling folders.
371
}
372
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
373
- (BOOL)validateMenuItem:(NSMenuItem *)item
71 by andym
Restored 10.3.9 compatibility to the system version method.
374
{
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
375
	if ([item action] == @selector(checkForUpdates:))
376
		return ![self updateInProgress];
377
	return YES;
71 by andym
Restored 10.3.9 compatibility to the system version method.
378
}
379
77 by andym
Made a good deal more progress integrating the profile checking features. It should actually work now.
380
- (void)setDelegate:aDelegate
381
{
184 by Andy Matuschak
Fixes 239512
382
	delegate = aDelegate;
77 by andym
Made a good deal more progress integrating the profile checking features. It should actually work now.
383
}
384
116 by andym
Touched practically every line of code in a super-monster-awesome refactoring. Please read:
385
- (BOOL)updateInProgress
386
{
387
	return driver && ([driver finished] == NO);
388
}
389
242.1.4 by Andy Matuschak
Fixes 244857
390
- delegate { return delegate; }
391
- (NSBundle *)hostBundle { return [host bundle]; }
242.1.1 by Andy Matuschak
Beginnings of insane SUHost-based refactoring to get rid of NSBundle+Sparkle. More super-unstable refactorings to come...
392
1 by andym
Holy restructuring, batman! Watch out for falling folders.
393
@end