PageRenderTime 44ms CodeModel.GetById 9ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

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