1/* 2 * Copyright (c) 2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24// **************************************************************************** 25// cclparser.m 26// libccl 27// 28// Created by kevine on 3/1/06. 29// Copyright 2006-7 Apple, Inc. All rights reserved. 30// ***************************************************************************** 31 32// #define SC_SCHEMA_DECLARATION(k,q) extern NSString * k; // ?? 33#import "cclparser.h" 34 35 36// names that would otherwise cause ambiguity are expanded 37#define kPersonalityExpanded @"CCLNameExpanded" 38#define kPathExpanded @"CCLPathExpanded" 39// localized by BTSA and network preferences 40 41 42@implementation CCLParser 43 44+ (CCLParser*) createCCLParser 45{ 46 CCLParser* retVal= [[CCLParser alloc] init]; 47 48 return retVal; 49} 50 51/****************************************************************************** 52* see cclparser.h for a description of the class variables 53******************************************************************************/ 54- (id) init 55{ 56 self = [super init]; 57 mBundleData = [[NSMutableDictionary alloc] init]; 58 mBundlesProcessed = [[NSMutableDictionary alloc] init]; 59 mFlatOverrides = [[NSMutableDictionary alloc] init]; 60 mTypeFilter = nil; 61 62 return self; 63} 64 65// **************************************************************************************************** 66- (void) dealloc 67{ 68 [self setTypeFilter:nil]; 69 [mFlatOverrides release]; 70 [mBundlesProcessed release]; 71 [mBundleData release]; 72 [super dealloc]; 73 74 return; 75} 76 77- (void)finalize 78{ 79 [super finalize]; 80} 81 82- (void)setTypeFilter:(NSSet*)desiredConnectTypes 83{ 84 [desiredConnectTypes retain]; 85 [mTypeFilter release]; 86 87 mTypeFilter = desiredConnectTypes; 88} 89 90 91// **************************************************************************************************** 92- (NSMutableDictionary*)buildBaseModelDict:(NSDictionary*)matchEntry 93 path:(NSString*)cclPath personality:(NSString*)personality 94{ 95 NSMutableDictionary* rval = NULL; 96 NSMutableDictionary* baseDict = [NSMutableDictionary dictionaryWithCapacity:5]; 97 id connectType= [matchEntry objectForKey: (id)kCCLConnectTypeKey]; 98 id cclVars= [matchEntry objectForKey: (id)kCCLParametersKey]; 99 id gprsCaps = [matchEntry objectForKey: (id)kCCLGPRSCapabilitiesKey]; 100 101 if (!baseDict || 102 ![connectType isKindOfClass: [NSString class]] || 103 ![cclVars isKindOfClass: [NSDictionary class]]) 104 goto finish; 105 106 [baseDict setValue:cclPath forKey:(id)kSCPropNetModemConnectionScript]; 107 [baseDict setValue:personality forKey:(id)kSCPropNetModemConnectionPersonality]; 108 [baseDict setValue:connectType forKey:(id)kCCLConnectTypeKey]; 109 [baseDict setValue:cclVars forKey:(id)kCCLParametersKey]; 110 111 if ([connectType isEqualTo:(id)kCCLConnectGPRS]) { 112 if (![gprsCaps isKindOfClass:[NSDictionary class]]) 113 goto finish; 114 [baseDict setValue:gprsCaps forKey:(id)kCCLGPRSCapabilitiesKey]; 115 } 116 117 rval = baseDict; 118 119finish: 120 return rval; 121} 122 123// **************************************************************************************************** 124- (BOOL) parseMatchEntry:(NSDictionary*)matchEntry path:(NSString*)cclPath personality:(NSString*)personality mergeDict:(NSMutableDictionary*)mergeDict 125{ 126 BOOL retVal= NO; 127 NSMutableDictionary *firstModelDict = nil; 128 NSArray *supersedesList; 129 130 NSArray* deviceNameList= [matchEntry objectForKey: (id)kCCLDeviceNamesKey]; 131 if(deviceNameList!= NULL && [deviceNameList isKindOfClass: [NSArray class]]) { 132 NSEnumerator* deviceEnum= [deviceNameList objectEnumerator]; 133 NSDictionary* curEntry= [deviceEnum nextObject]; 134 135 retVal= YES; 136 while(curEntry!= NULL) { 137 BOOL success = NO; 138 139 if([curEntry isKindOfClass: [NSDictionary class]]) { 140 NSString* venName= [curEntry objectForKey: (id)kCCLVendorKey]; 141 NSString* modName= [curEntry objectForKey: (id)kCCLModelKey]; 142 143 if([venName isKindOfClass: [NSString class]] && 144 [modName isKindOfClass: [NSString class]]) { 145 NSMutableDictionary *modelDict; 146 147 // Since each UI dict has its own model/vendor keys, 148 // UIs need a separate dictionary for each pair 149 modelDict= [self buildBaseModelDict:matchEntry 150 path:cclPath personality:personality]; 151 [modelDict setObject: modName forKey:(id)kCCLModelKey]; 152 [modelDict setObject: venName forKey:(id)kCCLVendorKey]; 153 154 NSMutableArray* venList= [mergeDict objectForKey: venName]; 155 if(venList!= NULL) { 156 [venList addObject: modelDict]; 157 } else { 158 venList= [NSMutableArray arrayWithObject: modelDict]; 159 [mergeDict setObject: venList forKey: venName]; 160 } 161 success = YES; 162 163 // stash for overrides below 164 if (!firstModelDict) 165 firstModelDict = modelDict; 166 } 167 } 168 retVal&= success; 169 curEntry= [deviceEnum nextObject]; 170 } 171 } 172 173 // for the overrides, we use firstModelDict captured above 174 if (firstModelDict) { 175 // .ccl bundles implicitly supersede flat scripts of the same basename 176 NSString *oldName = [cclPath lastPathComponent]; 177 if ([[oldName pathExtension] isEqual:(id)kCCLFileExtension]) 178 oldName = [oldName stringByDeletingPathExtension]; 179 // if a bundle has multiple personalities, implicit override -> first 180 if (![mFlatOverrides objectForKey:oldName]) 181 [mFlatOverrides setObject:firstModelDict forKey:oldName]; 182 /* all personalities one foo.ccl will all implicitly supersede foo 183 else 184 NSLog(@"WARNING: %@/%@ also claims to supersede %@", 185 cclPath, personality, oldName); 186 */ 187 188 // if this personality overrides anything else, 189 // add one description to mFlatOverrides 190 supersedesList = [matchEntry objectForKey:(id)kCCLSupersedesKey]; 191 if (supersedesList && [supersedesList isKindOfClass:[NSArray class]]) { 192 NSEnumerator *e = [supersedesList objectEnumerator]; 193 id flatscript; 194 195 while ((flatscript = [e nextObject])) { 196 if ([flatscript isKindOfClass:[NSString class]]) 197 [mFlatOverrides setObject:firstModelDict forKey:flatscript]; 198 else 199 NSLog(@"%@'s Supersedes list: unintelligible object %@", 200 cclPath, flatscript); 201 } 202 } 203 } 204 205 return retVal; 206} 207 208// **************************************************************************************************** 209- (BOOL) mergePersonalityDict: (NSDictionary*) mergeDict 210{ 211 NSEnumerator* mergeVenEnum = [mergeDict keyEnumerator]; 212 NSString* venName; 213 214 // walk list of vendors in the merge dictionary, 215 // adding to existing lists in mBundleData 216 while((venName = [mergeVenEnum nextObject])) { 217 NSArray* mergeModels = [mergeDict objectForKey:venName]; 218 NSMutableArray* existingModels = [mBundleData objectForKey:venName]; 219 220 if(existingModels) 221 [existingModels addObjectsFromArray:mergeModels]; 222 else 223 [mBundleData setObject:mergeModels forKey:venName]; 224 } 225 return YES; 226} 227 228// **************************************************************************** 229- (BOOL)processFlatCCL:(NSString*)path named:(NSString*)name 230{ 231 NSMutableDictionary *modelDict; 232 NSMutableArray *otherVendor; 233 234 // someday we could validate or auto-name, but flat CCLs are fading 235 // (mutable for "cleanupMatchingModels") 236 modelDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: 237 (id)kCCLConnectDialup, (id)kCCLConnectTypeKey, 238 name, (id)kCCLModelKey, 239 (id)kCCLOtherVendorName, (id)kCCLVendorKey, 240 path, (id)kSCPropNetModemConnectionScript, 241 NULL]; 242 if (!modelDict) return NO; 243 244 // add to existing 'Other' vendor if it exists; else create 245 otherVendor = [mBundleData objectForKey:(id)kCCLOtherVendorName]; 246 if (otherVendor) { 247 [otherVendor addObject:modelDict]; 248 } else { 249 if(!(otherVendor = [NSMutableArray arrayWithObject:modelDict])) 250 return NO; 251 [mBundleData setObject:otherVendor forKey:(id)kCCLOtherVendorName]; 252 } 253 254 return YES; 255} 256 257// **************************************************************************************************** 258- (BOOL) processCCLBundle:(NSString*)cclPath 259{ 260 BOOL retVal= NO; 261 NSDictionary* infoDict= [[NSBundle bundleWithPath: cclPath] infoDictionary]; 262 NSNumber *verNum; 263 264 // check for verNum since broken plist doesn't lead to nULL infoDict 265 if([infoDict isKindOfClass:[NSDictionary class]] && 266 (verNum = [infoDict objectForKey:(id)kCCLVersionKey])) 267 { 268 NSString *cfbundleID = [infoDict objectForKey:(id)kCFBundleIdentifierKey]; 269 NSString *opath; 270 271 // ignore duplicate bundles 272 // log if the duplication doesn't involve /S/L/Modem Scripts 273 if ((opath = [mBundlesProcessed objectForKey:cfbundleID])) { 274 if (!([opath hasPrefix:@"/System/Library"] || 275 [cclPath hasPrefix:@"/System/Library"])) 276 NSLog(@"%@ appears to be a duplicate of %@ (id = %@); ignoring", 277 cclPath, opath, cfbundleID); 278 return YES; 279 } else { 280 // add the bundle ID 281 [mBundlesProcessed setObject:cclPath forKey:(id)cfbundleID]; 282 } 283 284 if([verNum isKindOfClass: [NSNumber class]] && [verNum isEqualToNumber: 285 [NSNumber numberWithInt: kCCLBundleVersion]]) 286 { //Versions match, let the fun continue... 287 NSDictionary* personalityList= [infoDict objectForKey: (id)kCCLPersonalitiesKey]; 288 if(personalityList!= NULL && [personalityList isKindOfClass: [NSDictionary class]]) 289 { 290 NSMutableDictionary* mergeDict= [NSMutableDictionary dictionaryWithCapacity: [personalityList count]]; 291 NSEnumerator* personalityKeyEnum= [personalityList keyEnumerator]; 292 NSString* personalityKey= [personalityKeyEnum nextObject]; 293 294 retVal= YES; 295 while((personalityKey!= NULL) && retVal) 296 { 297 NSDictionary* personalityEntry= [personalityList objectForKey: personalityKey]; 298 if([personalityEntry isKindOfClass: [NSDictionary class]]) { 299 BOOL interested; 300 301 if (!mTypeFilter) { 302 interested = YES; 303 } else { 304 NSString *connectType = [personalityEntry objectForKey:(id)kCCLConnectTypeKey]; 305 // see if this personality's type matches 306 interested = [mTypeFilter containsObject:connectType]; 307 } 308 309 if (interested) 310 retVal= [self parseMatchEntry: personalityEntry path: cclPath personality: personalityKey mergeDict: mergeDict]; 311 } 312 personalityKey= [personalityKeyEnum nextObject]; 313 } 314 315 if(retVal) 316 retVal= [self mergePersonalityDict: mergeDict]; 317 } else 318 NSLog(@"skipping %@: trouble extracting personalities",cclPath); 319 } else 320 NSLog(@"skipping %@: incompatible CCL version number: %@", 321 cclPath, verNum); 322 } else 323 NSLog(@"skipping %@: malformed bundle dictionary", cclPath); 324 325 return retVal; 326} 327 328// **************************************************************************************************** 329- (BOOL) processFolder:(NSString*)folderPath 330{ 331 BOOL retVal= YES; 332 NSFileManager* fileMan= [NSFileManager defaultManager]; 333 NSDirectoryEnumerator* folderEnum= [fileMan enumeratorAtPath:folderPath]; 334 NSString *curFileName, *displayName; 335 336 while((curFileName = [folderEnum nextObject])) { 337 NSString* filePath; 338 BOOL isDir = NO, exists; 339 340 filePath = [folderPath stringByAppendingPathComponent:curFileName]; 341 if (![fileMan fileExistsAtPath:filePath isDirectory:&isDir]) { 342 NSLog(@"Warning: %@ doesn't seem to exist", filePath); 343 continue; 344 } 345 346 // if it's a new-fangled .ccl bundle, process appropriately 347 if (isDir) { 348 if ([[curFileName pathExtension] isEqualToString:(id)kCCLFileExtension]){ 349 [folderEnum skipDescendents]; // don't descend into bundle 350 retVal&= [self processCCLBundle: filePath]; 351 } else { 352 // ignore .directories 353 if ([[filePath lastPathComponent] hasPrefix:@"."]) 354 [folderEnum skipDescendents]; 355 } 356 } else { 357 // (note: we always process flat CCLs b/c we don't know their type) 358 // handle as a flat file 359 CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:filePath]; 360 LSItemInfoRecord info; 361 OSStatus errn; 362 363 // 4152940 requests invisibility info in NSFileManager 364 errn = LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info); 365 if (errn) { 366 NSLog(@"skipping %@: error %d getting item info",filePath,errn); 367 continue; 368 } 369 370 // must be visible and flat 371 if ((info.flags & (kLSItemInfoIsPlainFile | kLSItemInfoIsSymlink)) 372 && !(info.flags & kLSItemInfoIsInvisible)) { 373 // use display name of file 374 displayName = [fileMan displayNameAtPath:filePath]; 375 retVal&= [self processFlatCCL:filePath named:displayName]; 376 } 377 } 378 } 379 380 return retVal; 381} 382 383 384// **************************************************************************************************** 385- (void) cleanupNameWithPersonality: (NSMutableDictionary*) modelDict 386{ 387 if([modelDict objectForKey: kPersonalityExpanded]== NULL) 388 { 389 NSString* modelName= [modelDict objectForKey: (id)kCCLModelKey]; 390 NSString* modelPersonality= [modelDict objectForKey: (id)kSCPropNetModemConnectionPersonality]; 391 392 if (modelName && modelPersonality) { 393 NSString* newModelName= [NSString stringWithFormat: @"%@, %@", modelName, modelPersonality]; 394 [modelDict setObject: newModelName forKey: (id)kCCLModelKey]; 395 [modelDict setObject: [NSNull null] forKey: kPersonalityExpanded]; 396 } 397 } 398} 399 400// **************************************************************************************************** 401- (void) cleanupNameWithCCLName: (NSMutableDictionary*) modelDict 402{ 403 if([modelDict objectForKey: kPathExpanded]== NULL) 404 { 405 NSString *modelName = [modelDict objectForKey: (id)kCCLModelKey]; 406 NSString *cScript = [modelDict objectForKey:(id)kSCPropNetModemConnectionScript]; 407 NSString *cclName = [cScript lastPathComponent]; 408 if ([modelName isEqual:cclName]) { 409 NSLog(@"a second copy of %@ is installed at %@?", cclName, cScript); 410 cclName = cScript; 411 } 412 413 if (![modelName isEqual:cclName]) { 414 NSString* newModelName= [NSString stringWithFormat: @"%@, %@", modelName, cclName]; 415 [modelDict setObject: newModelName forKey: (id)kCCLModelKey]; 416 [modelDict setObject: [NSNull null] forKey: kPathExpanded]; 417 } 418 } 419} 420 421// **************************************************************************************************** 422- (bool) cleanupMatchingModels: (NSDictionary*) curModelDict modelArray: (NSArray*) modelArray changeSEL: (SEL) selector 423{ 424 bool retVal= NO; 425 NSMutableArray* matchList= [NSMutableArray array]; 426 NSString* matchModelName= [curModelDict objectForKey: (id)kCCLModelKey]; 427 NSEnumerator* modelEnum= [modelArray objectEnumerator]; 428 NSDictionary* curDict= [modelEnum nextObject]; 429 while(curDict!= NULL) 430 { 431 if(curDict!= curModelDict) 432 { 433 NSString* curModelName= [curDict objectForKey: (id)kCCLModelKey]; 434 if([matchModelName isEqualToString: curModelName]) 435 { 436 [matchList addObject: curDict]; 437 } 438 439 } 440 curDict= [modelEnum nextObject]; 441 } 442 if([matchList count]>0) 443 { 444 [matchList addObject: curModelDict]; 445 NSEnumerator* modelEnum= [matchList objectEnumerator]; 446 NSMutableDictionary* curDict= [modelEnum nextObject]; 447 while(curDict!= NULL) 448 { 449 [self performSelector: selector withObject: curDict]; 450 curDict= [modelEnum nextObject]; 451 } 452 retVal= YES; 453 } 454 return retVal; 455} 456 457// **************************************************************************************************** 458- (void)cleanupDuplicates 459{ 460 NSEnumerator *keyEnum; 461 NSString *curVenName, *modName; 462 NSMutableArray *persList; 463 unsigned persCount, i; 464 465 // first remove overridden flat CCLs (message -> nil == no-op) 466 // remove from mBundleData/Other any names in mFlatOverrides 467 persList = [mBundleData objectForKey:kCCLOtherVendorName]; 468 persCount = [persList count]; 469 for (i = 0; i < persCount; i++) { 470 modName = [[persList objectAtIndex:i] objectForKey:(id)kCCLModelKey]; 471 if ([mFlatOverrides objectForKey:modName]) { 472 [persList removeObjectAtIndex:i]; 473 i--; persCount--; // since everything slid 474 } 475 } 476 // and if nothing's left, remove the Other vendor entirely 477 if (persList && [persList count] == 0) 478 [mBundleData removeObjectForKey:kCCLOtherVendorName]; 479 480 // check each model list against itself (XX use NSOrderedSet?) 481 keyEnum = [mBundleData keyEnumerator]; 482 while(curVenName= [keyEnum nextObject]) { 483 NSArray* modelArray= [mBundleData objectForKey: curVenName]; 484 NSEnumerator* modelEnum= [modelArray objectEnumerator]; 485 NSMutableDictionary* curModelDict; 486 while((curModelDict = [modelEnum nextObject])) 487 if([self cleanupMatchingModels: curModelDict modelArray: modelArray changeSEL: @selector(cleanupNameWithCCLName:)]) 488 [self cleanupMatchingModels: curModelDict modelArray: modelArray changeSEL: @selector(cleanupNameWithPersonality:)]; 489 } 490} 491 492// **************************************************************************************************** 493- (NSArray*)copyVendorList 494{ 495 NSMutableArray *vendors; 496 497 // get mutable copy of all vendors 498 vendors = [[mBundleData allKeys] mutableCopy]; 499 500 // remove 'Other'; sort; and append 501 [vendors removeObject:(id)kCCLOtherVendorName]; 502 [vendors sortUsingSelector:@selector(caseInsensitiveCompare:)]; 503 if ([mBundleData objectForKey:(id)kCCLOtherVendorName]) 504 [vendors addObject:(id)kCCLOtherVendorName]; 505 506 return vendors; 507} 508 509// **************************************************************************************************** 510- (NSArray*)getModelListForVendor: (NSString*) vendor 511{ 512 NSMutableArray *models = [mBundleData objectForKey:vendor]; 513 NSSortDescriptor* sortd = [[NSSortDescriptor alloc] 514 initWithKey:(id)kCCLModelKey ascending:TRUE 515 selector:@selector(caseInsensitiveCompare:)]; 516 517 // might have sorted it before, but this doesn't happen often and 518 // the sort should be fast if already sorted 519 [models sortUsingDescriptors:[NSArray arrayWithObject:sortd]]; 520 [sortd release]; 521 522 return models; 523} 524 525// **************************************************************************************************** 526- (void) clearParser 527{ 528 [mBundleData removeAllObjects]; 529 [mBundlesProcessed removeAllObjects]; 530 [mFlatOverrides removeAllObjects]; 531} 532 533/******************************************************************************* 534* mergeCCLPersonality:withDeviceConfiguration: takes a "modem" dict that would 535* have come from SystemConfiguration (e.g. containing phone number and such) 536* and sets various keys in a new dictionary based on data in the personality. 537* Some key/value pairs are just copied (connection script and personality 538* within the script bundle) or removed as appropriate, but GPRS APN and CID 539* are expressed as "preferred" (aka "default") in the personality dict while 540* they are hard values in the device dictionary. If the user has already 541* chosen APN or CID for this personality, we don't copy over the preferred 542* values. But if the user is choosing the personality for the first time, 543* we do copy the values over. The UI can then use them to populate the UI. 544* 545* We return a new NSMutableDictionary so that the UI can call us repeatedly 546* with different personalities and the the same deviceConfiguration 547* dictionary (from SysConfig) without any defaults from one personality 548* polluting the UI when the user chooses another personality that perhaps 549* doesn't have any defaults. Only when the user saves the configuration 550* should we ever see any of the things we inserted feed back to us. 551*******************************************************************************/ 552 553- (NSMutableDictionary*)mergeCCLPersonality:(NSDictionary*)personality withDeviceConfiguration:(NSDictionary*)deviceConfiguration; 554{ 555 NSMutableDictionary *rval = [deviceConfiguration mutableCopy]; 556 NSString *mScript, *pScript, *mPers; 557 NSString *pName, *pType, *pVendor; 558 BOOL newPersonality; 559 560 // see whether the user changed personalities 561 mScript = [rval objectForKey:(id)kSCPropNetModemConnectionScript]; 562 pScript = [personality objectForKey:(id)kSCPropNetModemConnectionScript]; 563 mPers = [rval objectForKey:(id)kSCPropNetModemConnectionPersonality]; 564 pName = [personality objectForKey:(id)kSCPropNetModemConnectionPersonality]; 565 newPersonality = (![pScript isEqual:mScript] || ![pName isEqual:mPers]); 566 // XX any other checking of current modem dictionary needed? 567 568 // copy vendor, model, script, personality (only model+script for flat CCL) 569 pVendor = [personality objectForKey:(id)kCCLVendorKey]; 570 if (pVendor) 571 [rval setObject:pVendor forKey:(id)kCCLVendorKey]; 572 else 573 [rval removeObjectForKey:(id)kCCLVendorKey]; 574 [rval setObject:[personality objectForKey:(id)kCCLModelKey] 575 forKey:(id)kCCLModelKey]; 576 [rval setObject:pScript forKey:(id)kSCPropNetModemConnectionScript]; 577 if (pName) 578 [rval setObject:pName forKey:(id)kSCPropNetModemConnectionPersonality]; 579 else 580 [rval removeObjectForKey:(id)kSCPropNetModemConnectionPersonality]; 581 582 // check to see if APN or CID need to be populated / updated 583 // - if GPRS and not set; set to preferred 584 // - see below for Preferred CID logic 585 pType = [personality objectForKey:(id)kCCLConnectTypeKey]; 586 if ([pType isEqual:(id)kCCLConnectGPRS]) { 587 NSDictionary *cclParms = 588 [personality objectForKey:(id)kCCLParametersKey]; 589 NSString *savedAPN = 590 [rval objectForKey:(id)kSCPropNetModemAccessPointName]; 591 NSString *preferredAPN = 592 [cclParms objectForKey:(id)kCCLPreferredAPNKey]; 593 NSString *savedCID = 594 [rval objectForKey:(id)kSCPropNetModemDeviceContextID]; 595 NSNumber *preferredCID = 596 [cclParms objectForKey:(id)kCCLPreferredCIDKey]; 597 BOOL safeCIDs = [[[personality objectForKey:(id)kCCLGPRSCapabilitiesKey] 598 objectForKey:(id)kCCLIndependentCIDs] boolValue]; 599 // if !safeCIDs, perhaps we could set the preferred to max[- 1?]? 600 // (we already insert values) 601 602 // A saved CID should be overwritten if changing personalities and 603 // the new personality isn't known to be safe for the old CID 604 // or if there is a new preferred CID for the new personality. 605 // If there is no preferred CID, the old CID should be removed if 606 // CIDs are unsafe. In other words, leave a CID alone only if 607 // - it was already selected for this personality 608 // - it is known to be safe for this personality && no preferred 609 // unset, no preferred -> leave alone 610 // unset, preferred -> set to preferred 611 // set, !new personality -> leave alone 612 // set, new personality, preferred -> set to preferred 613 // set, new, !preferred, safe -> leave alone 614 // set, new, !preferred, unsafe -> clear 615 if (!savedCID || newPersonality) { 616 if (preferredCID) { 617 [rval setObject:[preferredCID stringValue] 618 forKey:(id)kSCPropNetModemDeviceContextID]; 619 } else if (savedCID && !safeCIDs) { 620 [rval removeObjectForKey:(id)kSCPropNetModemDeviceContextID]; 621 } 622 } 623 if (preferredAPN && (!savedAPN || newPersonality)) 624 [rval setObject:preferredAPN forKey:(id)kSCPropNetModemAccessPointName]; 625 } else { 626 // clear any existing APN/CID 627 [rval removeObjectForKey:(id)kSCPropNetModemAccessPointName]; 628 [rval removeObjectForKey:(id)kSCPropNetModemDeviceContextID]; 629 } 630 631 return rval; 632} 633 634/******************************************************************************* 635* -upgradeDeviceConfiguration takes a system configuration modem dictionary 636* that doesn't have a device/vendor pair and adds one. It may or may not 637* do smart things like use keys in the CCL bundles to tell it which 638* personalities are equivalent to old flat scripts. :) 639*******************************************************************************/ 640- (NSMutableDictionary*)upgradeDeviceConfiguration:(NSDictionary*)deviceConf 641{ 642 NSMutableDictionary *rval = nil; 643 NSString *vendor, *model, *cScript, *csName, *persName = nil; 644 NSArray *persList; 645 NSEnumerator *e; 646 NSDictionary *pers; 647 BOOL knownModel = NO; 648 649 650 // use the kSCProp constants since these are SC dicts 651 csName = [[deviceConf objectForKey:(id)kSCPropNetModemConnectionScript] lastPathComponent]; 652 653 // let's see if there's anything here (we'll validate below) 654 vendor = [deviceConf objectForKey:(id)kSCPropNetModemDeviceVendor]; 655 model = [deviceConf objectForKey:(id)kSCPropNetModemDeviceModel]; 656 657 if (model && vendor) { 658 persList = [mBundleData objectForKey:vendor]; 659 e = [persList objectEnumerator]; 660 while ((pers = [e nextObject])) { 661 if ([[pers objectForKey:(id)kCCLModelKey] isEqual:model]) { 662 knownModel = YES; 663 break; 664 } 665 } 666 } else { 667 // maybe overridden (implicitly or otherwise) 668 if ((pers = [mFlatOverrides objectForKey:csName])) { 669 vendor = [pers objectForKey:(id)kCCLVendorKey]; 670 model = [pers objectForKey:(id)kCCLModelKey]; 671 persName = [pers objectForKey:(id)kSCPropNetModemConnectionPersonality]; 672 knownModel = YES; 673 } else { 674 // fall back to trying "other"/<connectionscript> 675 vendor = kCCLOtherVendorName; 676 if ([[csName pathExtension] isEqual:(id)kCCLFileExtension]) 677 csName = [csName stringByDeletingPathExtension]; 678 model = csName; 679 680 // validate with 'other' vendor list 681 persList = [mBundleData objectForKey:vendor]; 682 e = [persList objectEnumerator]; 683 while ((pers = [e nextObject])) { 684 if ([[pers objectForKey:(id)kCCLModelKey] isEqual:model]) { 685 knownModel = YES; 686 break; 687 } 688 } 689 } 690 } 691 692 if (knownModel) { 693 // yay; go ahead and create result dictionary 694 rval = [deviceConf mutableCopy]; 695 [rval setObject:vendor forKey:(id)kSCPropNetModemDeviceVendor]; 696 [rval setObject:model forKey:(id)kSCPropNetModemDeviceModel]; 697 cScript = [pers objectForKey:(id)kSCPropNetModemConnectionScript]; 698 if (cScript) 699 [rval setObject:cScript forKey:(id)kSCPropNetModemConnectionScript]; 700 if (persName) 701 [rval setObject:persName forKey:(id)kSCPropNetModemConnectionPersonality]; 702 } 703 704 return rval; // still nil if the vendor/model didn't work out 705} 706 707@end 708