/Source/EmUpAppController.m

http://google-email-uploader-mac.googlecode.com/ · Objective C · 274 lines · 171 code · 53 blank · 50 comment · 34 complexity · 694cdff7759e057a124c6a09cfb26eae MD5 · raw file

  1. /* Copyright (c) 2009 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. //
  16. // EmUpAppController.m
  17. //
  18. #import "GTMHTTPFetcherLogging.h"
  19. #import "EmUpAppController.h"
  20. #import "EmUpWindowController.h"
  21. @interface EmUpAppController (PrivateMethods)
  22. - (void)checkVersion;
  23. @end
  24. @implementation EmUpAppController
  25. - (void)applicationWillFinishLaunching:(NSNotification *)notifcation {
  26. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  27. [windowController showWindow:self];
  28. [self checkVersion];
  29. }
  30. - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
  31. return YES;
  32. }
  33. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
  34. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  35. return [windowController canAppQuitNow] ? NSTerminateNow : NSTerminateLater;
  36. }
  37. - (void)applicationWillTerminate:(NSNotification *)note {
  38. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  39. [windowController deleteImportedEntourageArchive];
  40. }
  41. #pragma mark -
  42. - (IBAction)showHelp:(id)sender {
  43. NSString *urlStr = @"http://code.google.com/p/google-email-uploader-mac/";
  44. NSURL *url = [NSURL URLWithString:urlStr];
  45. [[NSWorkspace sharedWorkspace] openURL:url];
  46. }
  47. - (IBAction)showReleaseNotes:(id)sender {
  48. NSString *urlStr = @"http://google-email-uploader-mac.googlecode.com/svn/trunk/Source/ReleaseNotes.txt";
  49. NSURL *url = [NSURL URLWithString:urlStr];
  50. [[NSWorkspace sharedWorkspace] openURL:url];
  51. }
  52. - (IBAction)importFromEntourage:(id)sender {
  53. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  54. [windowController importRGEArchiveFromEntourage:sender];
  55. }
  56. - (IBAction)addMailboxes:(id)sender {
  57. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  58. [windowController addMailboxes:sender];
  59. }
  60. - (IBAction)reloadMailboxes:(id)sender {
  61. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  62. [windowController reloadMailboxesClicked:sender];
  63. }
  64. - (IBAction)loggingCheckboxClicked:(id)sender {
  65. // toggle the menu item's checkmark
  66. [loggingMenuItem_ setState:![loggingMenuItem_ state]];
  67. [GTMHTTPFetcher setLoggingEnabled:[loggingMenuItem_ state]];
  68. }
  69. - (IBAction)simulateUploadsClicked:(id)sender {
  70. [simulateUploadsMenuItem_ setState:![simulateUploadsMenuItem_ state]];
  71. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  72. [windowController setSimulateUploads:[simulateUploadsMenuItem_ state]];
  73. }
  74. #pragma mark -
  75. // we'll check the version in our plist against the plist on the open
  76. // source site
  77. //
  78. // for testing, use
  79. // defaults write com.google.EmailUploader ForceUpdate "1"
  80. //
  81. - (void)checkVersion {
  82. NSString *const kLastCheckDateKey = @"LastVersionCheck";
  83. NSString *const kForceUpdateKey = @"ForceUpdate";
  84. NSString *const kSkipUpdateCheckKey = @"SkipUpdateCheck";
  85. // determine if we've checked in the last 24 hours (or if the the preferences
  86. // to force or skip an update are set)
  87. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  88. BOOL shouldSkipUpdate = [defaults boolForKey:kSkipUpdateCheckKey];
  89. if (shouldSkipUpdate) return;
  90. NSDate *lastCheckDate = [defaults objectForKey:kLastCheckDateKey];
  91. BOOL shouldForceUpdate = [defaults boolForKey:kForceUpdateKey];
  92. if (lastCheckDate && !shouldForceUpdate) {
  93. // if the time since the last check is under a day, bail
  94. NSTimeInterval interval = - [lastCheckDate timeIntervalSinceNow];
  95. if (interval < 24 * 60 * 60) {
  96. return;
  97. }
  98. }
  99. // set the last check date to now
  100. [defaults setObject:[NSDate date]
  101. forKey:kLastCheckDateKey];
  102. // URL of our plist file in the sources online
  103. NSString *urlStr = @"http://google-email-uploader-mac.googlecode.com/svn/trunk/Source/LatestVersion.plist";
  104. GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithURLString:urlStr];
  105. [fetcher setComment:@"plist fetch"];
  106. [fetcher beginFetchWithDelegate:self
  107. didFinishSelector:@selector(plistFetcher:finishedWithData:error:)];
  108. [fetcher setUserData:[NSNumber numberWithBool:shouldForceUpdate]];
  109. }
  110. - (NSString *)appVersionForCurrentSystemVersionWithMap:(NSDictionary *)versionMap {
  111. // using a dictionary mapping system version ranges to app versions, like
  112. // -10.4.11 : 1.1.2
  113. // 10.5-10.5.8 : 1.1.3
  114. // 10.6- : 1.1.4
  115. // find the latest app version for the current system
  116. //
  117. // note: the system ranges should have no holes or overlapping system
  118. // versions, and should not be order-dependent
  119. SInt32 systemMajor = 0, systemMinor = 0, systemRelease = 0;
  120. (void) Gestalt(gestaltSystemVersionMajor, &systemMajor);
  121. (void) Gestalt(gestaltSystemVersionMinor, &systemMinor);
  122. (void) Gestalt(gestaltSystemVersionBugFix, &systemRelease);
  123. NSString *systemVersion = [NSString stringWithFormat:@"%d.%d.%d",
  124. (int)systemMajor, (int)systemMinor, (int)systemRelease];
  125. for (NSString *versionRange in versionMap) {
  126. NSString *lowSystemVersion = nil;
  127. NSString *dash = nil;
  128. NSString *highSystemVersion = nil;
  129. NSComparisonResult comp1, comp2;
  130. // parse "low", "low-", "low-high", or "-high"
  131. NSScanner *scanner = [NSScanner scannerWithString:versionRange];
  132. [scanner scanUpToString:@"-" intoString:&lowSystemVersion];
  133. [scanner scanString:@"-" intoString:&dash];
  134. [scanner scanUpToString:@"\r" intoString:&highSystemVersion];
  135. BOOL doesMatchLow = YES;
  136. BOOL doesMatchHigh = YES;
  137. if (lowSystemVersion) {
  138. comp1 = [GDataUtilities compareVersion:lowSystemVersion
  139. toVersion:systemVersion];
  140. doesMatchLow = (comp1 == NSOrderedSame
  141. || (dash != nil && comp1 == NSOrderedAscending));
  142. }
  143. if (highSystemVersion) {
  144. comp2 = [GDataUtilities compareVersion:highSystemVersion
  145. toVersion:systemVersion];
  146. doesMatchHigh = (comp2 == NSOrderedSame
  147. || (dash != nil && comp2 == NSOrderedDescending));
  148. }
  149. if (doesMatchLow && doesMatchHigh) {
  150. NSString *result = [versionMap objectForKey:versionRange];
  151. return result;
  152. }
  153. }
  154. return nil;
  155. }
  156. - (void)plistFetcher:(GTMHTTPFetcher *)fetcher
  157. finishedWithData:(NSData *)data
  158. error:(NSError *)error {
  159. if (error) {
  160. // nothing to do but report this on the console
  161. NSLog(@"unable to fetch plist at %@, %@", [[fetcher mutableRequest] URL], error);
  162. return;
  163. }
  164. // convert the returns data to a plist dictionary
  165. NSString *errorStr = nil;
  166. NSDictionary *plist;
  167. plist = [NSPropertyListSerialization propertyListFromData:data
  168. mutabilityOption:NSPropertyListImmutable
  169. format:NULL
  170. errorDescription:&errorStr];
  171. if ([plist isKindOfClass:[NSDictionary class]]) {
  172. // get the map of system versions to app versions, and step through the
  173. // system version ranges to find the latest app version for this system
  174. NSString *latestVersion;
  175. NSDictionary *versionMap = [plist objectForKey:@"SystemToVersionMap"];
  176. if (versionMap) {
  177. // new, with the map
  178. latestVersion = [self appVersionForCurrentSystemVersionWithMap:versionMap];
  179. } else {
  180. // old, without the map
  181. latestVersion = [plist objectForKey:@"CFBundleShortVersionString"];
  182. }
  183. // compare the short version string in this bundle to the one from the
  184. // map
  185. NSString *thisVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
  186. NSComparisonResult result = [GDataUtilities compareVersion:thisVersion
  187. toVersion:latestVersion];
  188. BOOL shouldForceUpdate = [[fetcher userData] boolValue];
  189. if (result != NSOrderedAscending && !shouldForceUpdate) {
  190. // we're current; do nothing
  191. } else {
  192. // show the user the "update now?" dialog
  193. EmUpWindowController* windowController = [EmUpWindowController sharedEmUpWindowController];
  194. NSString *title = NSLocalizedString(@"UpdateAvailable", nil);
  195. NSString *msg = NSLocalizedString(@"UpdateAvailableMsg", nil);
  196. NSString *updateBtn = NSLocalizedString(@"UpdateButton", nil); // "Update Now"
  197. NSString *dontUpdateBtn = NSLocalizedString(@"DontUpdateButton", nil); // "Don't Update"
  198. NSString *releaseNotesBtn = NSLocalizedString(@"ReleaseNotesButton", nil); // "Release Notes"
  199. NSBeginAlertSheet(title, updateBtn, dontUpdateBtn, releaseNotesBtn,
  200. [windowController window], self,
  201. @selector(updateSheetDidEnd:returnCode:contextInfo:),
  202. nil, nil, msg, thisVersion, latestVersion);
  203. }
  204. } else {
  205. NSLog(@"unable to parse plist, %@", errorStr);
  206. }
  207. }
  208. - (void)updateSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
  209. NSString *urlStr = nil;
  210. if (returnCode == NSAlertDefaultReturn) {
  211. // downloads page
  212. urlStr = @"https://code.google.com/p/google-email-uploader-mac/downloads/list";
  213. } else if (returnCode == NSAlertOtherReturn) {
  214. // release notes file in the source tree
  215. urlStr = @"http://code.google.com/p/google-email-uploader-mac/source/browse/trunk/Source/ReleaseNotes.txt";
  216. }
  217. if (urlStr) {
  218. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlStr]];
  219. }
  220. }
  221. @end