Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 279 lines (239 sloc) 10.931 kB
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
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
5aa1e18 Improved headers from Charles D. H. Williams.
andym authored
9 #import "Sparkle.h"
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
10 #import "SUUpdater.h"
5aa1e18 Improved headers from Charles D. H. Williams.
andym authored
11
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
12 @interface SUUpdater (Private)
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
13 - (NSArray *)feedParameters;
14 - (BOOL)automaticallyUpdates;
49cfd57 @andymatuschak Fixed a confounding bug that somehow slipped in: when an update check…
andymatuschak authored
15 - (BOOL)shouldScheduleUpdateCheck;
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
16 - (void)scheduleNextUpdateCheck;
ebb1f01 @andymatuschak Removed the checkInterval IV and factored out its assignment to a che…
andymatuschak authored
17 - (NSTimeInterval)checkInterval;
b2145b6 @andymatuschak Killed NSURL+Parameters and moved the relevant code into SUUpdater.
andymatuschak authored
18 - (NSURL *)feedURL;
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
19 @end
20
21 @implementation SUUpdater
22
bbedfa1 Reorganized SUUpdater.m; it's in better order now.
andym authored
23 #pragma mark Initialization
24
5b76530 Made a number of fixes to potentially dangerous singleton behavior as…
andym authored
25 static SUUpdater *sharedUpdater = nil;
26
bbedfa1 Reorganized SUUpdater.m; it's in better order now.
andym authored
27 // SUUpdater's a singleton now! And I'm enforcing it!
5b76530 Made a number of fixes to potentially dangerous singleton behavior as…
andym authored
28 // This will probably break the world if you try to write a Sparkle-enabled plugin for a Sparkle-enabled app.
bbedfa1 Reorganized SUUpdater.m; it's in better order now.
andym authored
29 + (SUUpdater *)sharedUpdater
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
30 {
5b76530 Made a number of fixes to potentially dangerous singleton behavior as…
andym authored
31 if (sharedUpdater == nil)
2ec35ae Finished cleaning up singleton stuff in SUUpdater.m
andym authored
32 sharedUpdater = [[[self class] alloc] init];
5b76530 Made a number of fixes to potentially dangerous singleton behavior as…
andym authored
33 return sharedUpdater;
63011aa Beware of falling bricks! Huge refactoring commit #1: cleansing Spark…
andym authored
34 }
35
a7148d9 Numerous minor changes courtesy Sean McBride. Mostly clarifications.
andym authored
36 - (id)init
63011aa Beware of falling bricks! Huge refactoring commit #1: cleansing Spark…
andym authored
37 {
5bf76dd Oops! Committed the last patch with broken code; this code works.
andym authored
38 self = [super init];
2ec35ae Finished cleaning up singleton stuff in SUUpdater.m
andym authored
39 if (sharedUpdater)
40 {
41 [self release];
42 self = sharedUpdater;
43 }
44 else if (self != nil)
5bf76dd Oops! Committed the last patch with broken code; this code works.
andym authored
45 {
2ec35ae Finished cleaning up singleton stuff in SUUpdater.m
andym authored
46 sharedUpdater = self;
5bf76dd Oops! Committed the last patch with broken code; this code works.
andym authored
47 [self setHostBundle:[NSBundle mainBundle]];
48 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidFinishLaunching:) name:NSApplicationDidFinishLaunchingNotification object:NSApp];
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
49 [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey] options:0 context:NULL];
50 [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey] options:0 context:NULL];
87f2893 Fixed warnings for missing newlines at the end of the file.
andym authored
51 }
5e05339 #76 Support for Plug-ins
catlan authored
52 return self;
53 }
54
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
55 - (void)applicationDidFinishLaunching:(NSNotification *)note
56 {
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
57 // If the user has been asked about automatic checks and said no, get out of here.
58 if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKey] &&
59 [[SUUserDefaults standardUserDefaults] boolForKey:SUEnableAutomaticChecksKey] == NO) { return; }
63011aa Beware of falling bricks! Huge refactoring commit #1: cleansing Spark…
andym authored
60
5c32832 @andymatuschak Fixes bug 228446
andymatuschak authored
61 // Does the delegate want to take care of the logic for when we should ask permission to update?
7d8ada5 @andymatuschak Improved delegate names and made SUProbingUpdateDriver use SUUpdater'…
andymatuschak authored
62 if ([delegate respondsToSelector:@selector(shouldPromptForPermissionToCheckForUpdatesToHostBundle)])
5c32832 @andymatuschak Fixes bug 228446
andymatuschak authored
63 {
7d8ada5 @andymatuschak Improved delegate names and made SUProbingUpdateDriver use SUUpdater'…
andymatuschak authored
64 if ([delegate shouldPromptForPermissionToCheckForUpdatesToHostBundle:hostBundle])
5c32832 @andymatuschak Fixes bug 228446
andymatuschak authored
65 [SUUpdatePermissionPrompt promptWithHostBundle:hostBundle delegate:self];
66 }
6f3d124 @andymatuschak Fixing bug 228469:
andymatuschak authored
67 // Has he been asked already? And don't ask if the host has a default value set in its Info.plist.
5c32832 @andymatuschak Fixes bug 228446
andymatuschak authored
68 else if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKey] == nil &&
6f3d124 @andymatuschak Fixing bug 228469:
andymatuschak authored
69 [hostBundle objectForInfoDictionaryKey:SUEnableAutomaticChecksKey] == nil)
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
70 {
6294668 @andymatuschak Sparkle now migrates SUCheckAtStartup preferences from pre-startup-re…
andymatuschak authored
71 if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKeyOld])
72 [[SUUserDefaults standardUserDefaults] setBool:[[SUUserDefaults standardUserDefaults] boolForKey:SUEnableAutomaticChecksKeyOld] forKey:SUEnableAutomaticChecksKey];
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
73 // Now, we don't want to ask the user for permission to do a weird thing on the first launch.
74 // We wait until the second launch.
6294668 @andymatuschak Sparkle now migrates SUCheckAtStartup preferences from pre-startup-re…
andymatuschak authored
75 else if ([[SUUserDefaults standardUserDefaults] boolForKey:SUHasLaunchedBeforeKey] == NO)
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
76 [[SUUserDefaults standardUserDefaults] setBool:YES forKey:SUHasLaunchedBeforeKey];
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
77 else
78 [SUUpdatePermissionPrompt promptWithHostBundle:hostBundle delegate:self];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
79 }
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
80
6f3d124 @andymatuschak Fixing bug 228469:
andymatuschak authored
81 // We check if the user's said they want updates, or they haven't said anything, and the default is set to checking.
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
82 [self scheduleNextUpdateCheck];
8c3960f Finished integrating SparklePlus profiling! Just set SUEnableSystemPr…
andym authored
83 }
84
85 - (void)updatePermissionPromptFinishedWithResult:(SUPermissionPromptResult)result
86 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
87 BOOL automaticallyCheck = (result == SUAutomaticallyCheck);
88 [[SUUserDefaults standardUserDefaults] setBool:automaticallyCheck forKey:SUEnableAutomaticChecksKey];
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
89 [self scheduleNextUpdateCheck];
8c3960f Finished integrating SparklePlus profiling! Just set SUEnableSystemPr…
andym authored
90 }
91
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
92 - (void)scheduleNextUpdateCheck
ebb1f01 @andymatuschak Removed the checkInterval IV and factored out its assignment to a che…
andymatuschak authored
93 {
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
94 @synchronized (checkTimer)
95 {
96 if (checkTimer)
97 {
98 [checkTimer invalidate];
99 checkTimer = nil;
100 }
101 }
102 if (![self shouldScheduleUpdateCheck]) return;
103
8c3960f Finished integrating SparklePlus profiling! Just set SUEnableSystemPr…
andym authored
104 // How long has it been since last we checked for an update?
105 NSDate *lastCheckDate = [[SUUserDefaults standardUserDefaults] objectForKey:SULastCheckTimeKey];
106 if (!lastCheckDate) { lastCheckDate = [NSDate distantPast]; }
107 NSTimeInterval intervalSinceCheck = [[NSDate date] timeIntervalSinceDate:lastCheckDate];
108
109 // Now we want to figure out how long until we check again.
110 NSTimeInterval delayUntilCheck;
ebb1f01 @andymatuschak Removed the checkInterval IV and factored out its assignment to a che…
andymatuschak authored
111 if (intervalSinceCheck < [self checkInterval])
112 delayUntilCheck = ([self checkInterval] - intervalSinceCheck); // It hasn't been long enough.
8c3960f Finished integrating SparklePlus profiling! Just set SUEnableSystemPr…
andym authored
113 else
114 delayUntilCheck = 0; // We're overdue! Run one now.
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
115 checkTimer = [NSTimer scheduledTimerWithTimeInterval:delayUntilCheck target:self selector:@selector(checkForUpdatesInBackground) userInfo:nil repeats:NO];
dc147ec Continued incremental refactoring; things seem to still be holding to…
andym authored
116 }
117
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
118 - (void)checkForUpdatesInBackground
dc147ec Continued incremental refactoring; things seem to still be holding to…
andym authored
119 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
120 [self checkForUpdatesWithDriver:[[[([self automaticallyUpdates] ? [SUAutomaticUpdateDriver class] : [SUScheduledUpdateDriver class]) alloc] init] autorelease]];
dc147ec Continued incremental refactoring; things seem to still be holding to…
andym authored
121 }
122
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
123 - (IBAction)checkForUpdates:sender
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
124 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
125 [self checkForUpdatesWithDriver:[[[SUUserInitiatedUpdateDriver alloc] init] autorelease]];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
126 }
127
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
128 - (void)checkForUpdatesWithDriver:(SUUpdateDriver *)d
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
129 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
130 if ([self updateInProgress]) { return; }
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
131 @synchronized (checkTimer) { if (checkTimer) { [checkTimer invalidate]; checkTimer = nil; } }
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
132
133 driver = [d retain];
134 if ([driver delegate] == nil) { [driver setDelegate:delegate]; }
135 [driver addObserver:self forKeyPath:@"finished" options:0 context:NULL];
b2145b6 @andymatuschak Killed NSURL+Parameters and moved the relevant code into SUUpdater.
andymatuschak authored
136 [driver checkForUpdatesAtURL:[self feedURL] hostBundle:hostBundle];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
137 }
138
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
139 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
140 {
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
141 if (object == driver && [keyPath isEqualToString:@"finished"])
142 {
143 [driver removeObserver:self forKeyPath:@"finished"];
144 [driver release]; driver = nil;
145 [self scheduleNextUpdateCheck];
146 }
147 else if (object == [NSUserDefaultsController sharedUserDefaultsController] && ([keyPath hasSuffix:SUScheduledCheckIntervalKey] || [keyPath hasSuffix:SUEnableAutomaticChecksKey]))
148 {
149 [self updatePreferencesChanged];
150 }
151 else
152 {
153 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
154 }
155 }
156
157 - (void)updatePreferencesChanged
158 {
159 [self scheduleNextUpdateCheck];
1c1bb3a #77 SUIgnoreChecks to ignore update, needed for Sparkle in large depl…
catlan authored
160 }
161
49cfd57 @andymatuschak Fixed a confounding bug that somehow slipped in: when an update check…
andymatuschak authored
162 - (BOOL)shouldScheduleUpdateCheck
163 {
164 // Breaking this down for readability:
165 // If the user says he wants automatic update checks, let's do it.
166 if ([[SUUserDefaults standardUserDefaults] boolForKey:SUEnableAutomaticChecksKey] == YES)
167 return YES;
168 // If the user hasn't said anything, but the developer says we should do it, let's do it.
169 if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKey] == nil &&
170 [[hostBundle objectForInfoDictionaryKey:SUEnableAutomaticChecksKey] boolValue] == YES)
171 return YES;
a55f11d @andymatuschak Fixed 236240
andymatuschak authored
172 return NO; // Otherwise, don't bother.
49cfd57 @andymatuschak Fixed a confounding bug that somehow slipped in: when an update check…
andymatuschak authored
173 }
174
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
175 - (BOOL)automaticallyUpdates
176 {
177 // If the SUAllowsAutomaticUpdatesKey exists and is set to NO, return NO.
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
178 if ([hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] &&
179 [[hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] boolValue] == NO)
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
180 return NO;
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
181
182 // If we're not using DSA signatures, we aren't going to trust any updates automatically.
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
183 if ([[hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue] != YES)
184 return NO;
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
185
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
186 // If there's no setting, or it's set to no, we're not automatically updating.
187 if ([[SUUserDefaults standardUserDefaults] boolForKey:SUAutomaticallyUpdateKey] != YES)
188 return NO;
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
189
6a7c8b3 Substantial changes made to how Sparkle works on startup now. SUCheck…
andym authored
190 return YES; // Otherwise, we're good to go.
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
191 }
192
b2145b6 @andymatuschak Killed NSURL+Parameters and moved the relevant code into SUUpdater.
andymatuschak authored
193 - (NSURL *)_baseFeedURL
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
194 {
b2145b6 @andymatuschak Killed NSURL+Parameters and moved the relevant code into SUUpdater.
andymatuschak authored
195 // 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).
196 NSString *appcastString = [[SUUserDefaults standardUserDefaults] objectForKey:SUFeedURLKey];
197 if (!appcastString)
198 appcastString = [hostBundle objectForInfoDictionaryKey:SUFeedURLKey];
199 if (!appcastString) // Can't find an appcast string!
200 [NSException raise:@"SUNoFeedURL" format:@"You must specify the URL of the appcast as the SUFeedURLKey in either the Info.plist or the user defaults!"];
201 NSCharacterSet* quoteSet = [NSCharacterSet characterSetWithCharactersInString: @"\"\'"]; // Some feed publishers add quotes; strip 'em.
202 return [NSURL URLWithString:[appcastString stringByTrimmingCharactersInSet:quoteSet]] ;
203 }
204
205 - (NSURL *)feedURL
206 {
207 NSURL *baseFeedURL = [self _baseFeedURL];
208
209 // Determine all the parameters we're attaching to the base feed URL.
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
210 BOOL sendingSystemProfile = ([[SUUserDefaults standardUserDefaults] boolForKey:SUSendProfileInfoKey] == YES);
211 NSArray *parameters = [NSArray array];
7d8ada5 @andymatuschak Improved delegate names and made SUProbingUpdateDriver use SUUpdater'…
andymatuschak authored
212 if ([delegate respondsToSelector:@selector(feedParametersForHostBundle:sendingSystemProfile:)])
213 parameters = [parameters arrayByAddingObjectsFromArray:[delegate feedParametersForHostBundle:hostBundle sendingSystemProfile:sendingSystemProfile]];
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
214 if (sendingSystemProfile)
215 parameters = [parameters arrayByAddingObjectsFromArray:[hostBundle systemProfile]];
b2145b6 @andymatuschak Killed NSURL+Parameters and moved the relevant code into SUUpdater.
andymatuschak authored
216 if (parameters == nil || [parameters count] == 0) { return baseFeedURL; }
217
218 // Build up the parameterized URL.
219 NSMutableArray *parameterStrings = [NSMutableArray array];
220 NSEnumerator *profileInfoEnumerator = [parameters objectEnumerator];
221 NSDictionary *currentProfileInfo;
222 while ((currentProfileInfo = [profileInfoEnumerator nextObject]))
223 [parameterStrings addObject:[NSString stringWithFormat:@"%@=%@", [currentProfileInfo objectForKey:@"key"], [currentProfileInfo objectForKey:@"value"]]];
224
225 NSString *appcastStringWithProfile = [NSString stringWithFormat:@"%@?%@", [baseFeedURL absoluteString], [parameterStrings componentsJoinedByString:@"&"]];
226
227 // Clean it up so it's a valid URL
228 return [NSURL URLWithString:[appcastStringWithProfile stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
229 }
230
ebb1f01 @andymatuschak Removed the checkInterval IV and factored out its assignment to a che…
andymatuschak authored
231 - (NSTimeInterval)checkInterval
232 {
233 NSTimeInterval checkInterval = 0;
234 // Find the stored check interval. User defaults override Info.plist.
235 if ([[SUUserDefaults standardUserDefaults] objectForKey:SUScheduledCheckIntervalKey])
236 checkInterval = [[[SUUserDefaults standardUserDefaults] objectForKey:SUScheduledCheckIntervalKey] doubleValue];
237 else if ([hostBundle objectForInfoDictionaryKey:SUScheduledCheckIntervalKey])
238 checkInterval = [[hostBundle objectForInfoDictionaryKey:SUScheduledCheckIntervalKey] doubleValue];
239
240 if (checkInterval < SU_MIN_CHECK_INTERVAL) // This can also mean one that isn't set.
241 checkInterval = SU_DEFAULT_CHECK_INTERVAL;
242 return checkInterval;
243 }
244
bbedfa1 Reorganized SUUpdater.m; it's in better order now.
andym authored
245 - (void)dealloc
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
246 {
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
247 [hostBundle release];
248 [delegate release];
bbedfa1 Reorganized SUUpdater.m; it's in better order now.
andym authored
249 if (checkTimer) { [checkTimer invalidate]; }
250 [super dealloc];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
251 }
252
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
253 - (BOOL)validateMenuItem:(NSMenuItem *)item
66bbbed Restored 10.3.9 compatibility to the system version method.
andym authored
254 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
255 if ([item action] == @selector(checkForUpdates:))
256 return ![self updateInProgress];
257 return YES;
66bbbed Restored 10.3.9 compatibility to the system version method.
andym authored
258 }
259
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
260 - (void)setDelegate:aDelegate
261 {
cd4addb @andymatuschak Fixes 239512
andymatuschak authored
262 delegate = aDelegate;
645a96e Made a good deal more progress integrating the profile checking featu…
andym authored
263 }
264
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
265 - (void)setHostBundle:(NSBundle *)hb
266 {
a5dcf94 @andymatuschak Fixed all setters to eliminate the possibility of accidentally releas…
andymatuschak authored
267 if (hostBundle == hb) return;
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
268 [hostBundle release];
269 hostBundle = [hb retain];
270 [[SUUserDefaults standardUserDefaults] setIdentifier:[hostBundle bundleIdentifier]];
271 }
272
273 - (BOOL)updateInProgress
274 {
275 return driver && ([driver finished] == NO);
276 }
277
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
278 @end
Something went wrong with that request. Please try again.