/core/externals/update-engine/Core/KSPlistServer.m

http://macfuse.googlecode.com/ · Objective C · 217 lines · 130 code · 44 blank · 43 comment · 27 complexity · 7c8c93e7fedda280557fb337934aa69c MD5 · raw file

  1. // Copyright 2008 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. #import "KSPlistServer.h"
  15. #import "KSTicket.h"
  16. #import "GTMLogger.h"
  17. #import "KSUpdateInfo.h"
  18. @interface KSPlistServer (PrivateMethods)
  19. // Returns YES if the specified rule's Predicate evaluates to YES.
  20. - (BOOL)shouldApplyRule:(NSDictionary *)rule;
  21. // Returns a KSUpdateInfo instance that was created from the data in |rule|.
  22. - (KSUpdateInfo *)updateInfoForRule:(NSDictionary *)rule;
  23. @end
  24. @implementation KSPlistServer
  25. + (id)serverWithURL:(NSURL *)url {
  26. return [[[self alloc] initWithURL:url params:nil] autorelease];
  27. }
  28. - (id)initWithURL:(NSURL *)url params:(NSDictionary *)params
  29. engine:(KSUpdateEngine *)engine {
  30. if ((self = [super initWithURL:url params:params engine:engine])) {
  31. systemVersion_ = [[NSDictionary alloc] initWithContentsOfFile:
  32. @"/System/Library/CoreServices/SystemVersion.plist"];
  33. if (systemVersion_ == nil) {
  34. // COV_NF_START
  35. GTMLoggerError(@"Failed to read SystemVersion.plist, bailing.");
  36. [self release];
  37. return nil;
  38. // COV_NF_END
  39. }
  40. }
  41. return self;
  42. }
  43. - (void)dealloc {
  44. [tickets_ release];
  45. [systemVersion_ release];
  46. [super dealloc];
  47. }
  48. - (NSArray *)tickets {
  49. return tickets_;
  50. }
  51. //
  52. // Implementations of abstract methods from KSServer superclass
  53. //
  54. - (NSArray *)requestsForTickets:(NSArray *)tickets {
  55. // Retain the tickets array so that we can get the tickets in the
  56. // updateInfosForResposne:data: method.
  57. [tickets_ autorelease];
  58. tickets_ = [tickets copy];
  59. NSURLRequest *request = nil;
  60. request = [NSURLRequest requestWithURL:[self url]
  61. cachePolicy:NSURLRequestReloadIgnoringCacheData
  62. timeoutInterval:60];
  63. // Returns a trivial NSURLRequest to do a GET of the URL
  64. return [NSArray arrayWithObject:request];
  65. }
  66. - (NSArray *)updateInfosForResponse:(NSURLResponse *)response
  67. data:(NSData *)data
  68. outOfBandData:(NSDictionary **)oob {
  69. // We don't use |response|
  70. if (data == nil)
  71. return nil;
  72. // We don't return out-of-band data.
  73. if (oob) *oob = nil;
  74. // Decode the response |data| into a plist
  75. NSString *body = [[[NSString alloc]
  76. initWithData:data
  77. encoding:NSUTF8StringEncoding] autorelease];
  78. NSDictionary *plist = nil;
  79. @try {
  80. // This method can throw if |body| isn't a valid plist
  81. plist = [body propertyList];
  82. }
  83. @catch (id ex) {
  84. GTMLoggerError(@"Failed to parse response into plist: %@", ex);
  85. return nil;
  86. }
  87. // Array that we'll return
  88. NSMutableArray *updateInfos = [NSMutableArray array];
  89. // Walk through the array of "Rules" in the response plist, and create
  90. // KSUpdateInfos as necessary.
  91. NSDictionary *rule = nil;
  92. NSEnumerator *ruleEnumerator = [[plist objectForKey:@"Rules"]
  93. objectEnumerator];
  94. while ((rule = [ruleEnumerator nextObject])) {
  95. if ([self shouldApplyRule:rule]) {
  96. KSUpdateInfo *ui = [self updateInfoForRule:rule];
  97. if (ui) [updateInfos addObject:ui];
  98. }
  99. }
  100. return [updateInfos count] > 0 ? updateInfos : nil;
  101. }
  102. - (NSString *)prettyPrintResponse:(NSURLResponse *)response
  103. data:(NSData *)data {
  104. return [[[NSString alloc] initWithData:data
  105. encoding:NSUTF8StringEncoding] autorelease];
  106. }
  107. @end
  108. @implementation KSPlistServer (PrivateMethods)
  109. - (BOOL)shouldApplyRule:(NSDictionary *)rule {
  110. NSString *productID = [rule objectForKey:@"ProductID"];
  111. NSString *predicateString = [rule objectForKey:@"Predicate"];
  112. if (productID == nil || predicateString == nil)
  113. return NO;
  114. // Find the ticket with this rule's product ID.
  115. KSTicket *ticket = [[[self tickets] filteredArrayUsingPredicate:
  116. [NSPredicate predicateWithFormat:
  117. @"productID == %@", productID]] lastObject];
  118. if (ticket == nil)
  119. return NO;
  120. NSPredicate *predicate = nil;
  121. NSDictionary *predicateTarget = nil;
  122. BOOL matches = NO;
  123. @try {
  124. // This predicate stuff must be done in a try/catch because we're creating
  125. // the predicate from data supplied by the plist we fetched, and it may be
  126. // invalid for whatever reason.
  127. predicate = [NSPredicate predicateWithFormat:predicateString];
  128. // Create a dictionary with some useful info about the current OS and ticket
  129. // for the product in question. The rule's "Predicate" will be able to look
  130. // at this object to determine of an update is necessary.
  131. predicateTarget = [NSDictionary dictionaryWithObjectsAndKeys:
  132. systemVersion_, @"SystemVersion",
  133. ticket, @"Ticket",
  134. nil];
  135. matches = [predicate evaluateWithObject:predicateTarget];
  136. }
  137. @catch (id ex) {
  138. GTMLoggerError(@"Caught exception evaluating predicate for %@: %@",
  139. productID, ex);
  140. }
  141. return matches;
  142. }
  143. // Returns a KSUpdateInfo instance with all needed keys (see KSUpdateInfo.h).
  144. - (KSUpdateInfo *)updateInfoForRule:(NSDictionary *)rule {
  145. if (rule == nil) return nil;
  146. // Pre-populate our KSUpdateInfo with the whole |rule|
  147. NSMutableDictionary *updateInfo = [[rule mutableCopy] autorelease];
  148. // Turn "ProductID" into kServerProductID
  149. NSString *pid = [updateInfo objectForKey:@"ProductID"];
  150. if (pid == nil) goto invalid_rule;
  151. [updateInfo setObject:pid forKey:kServerProductID];
  152. // Turn "codebase" into kServerCodebaseURL, and make the value an NSURL
  153. NSString *codebase = [updateInfo objectForKey:@"Codebase"];
  154. if (codebase == nil) goto invalid_rule;
  155. NSURL *url = [NSURL URLWithString:codebase];
  156. if (url == nil) goto invalid_rule;
  157. [updateInfo setObject:url forKey:kServerCodebaseURL];
  158. // Turn "size" into kServerCodeSize, and make it an NSNumber (int)
  159. NSString *sizeString = [updateInfo objectForKey:@"Size"];
  160. if (sizeString == nil) goto invalid_rule;
  161. int size = [sizeString intValue];
  162. [updateInfo setObject:[NSNumber numberWithInt:size]
  163. forKey:kServerCodeSize];
  164. // Turn "hash" into kServerCodeHash
  165. NSString *hash = [updateInfo objectForKey:@"Hash"];
  166. if (hash == nil) goto invalid_rule;
  167. [updateInfo setObject:hash forKey:kServerCodeHash];
  168. return updateInfo;
  169. invalid_rule:
  170. GTMLoggerError(@"Can't create KSUpdateInfo from invalid rule %@", rule);
  171. return nil;
  172. }
  173. @end