| 1 | /* Copyright (C) 2008-2009 Peter Speck |
|---|
| 2 | * |
|---|
| 3 | * This program is free software: you can redistribute it and/or modify |
|---|
| 4 | * it under the terms of the GNU General Public License as published by |
|---|
| 5 | * the Free Software Foundation, either version 3 of the License, or |
|---|
| 6 | * (at your option) any later version. |
|---|
| 7 | * |
|---|
| 8 | * This program is distributed in the hope that it will be useful, |
|---|
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | * GNU General Public License for more details. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 15 | */ |
|---|
| 16 | |
|---|
| 17 | #import <Security/AuthorizationTags.h> |
|---|
| 18 | #import <SecurityInterface/SFAuthorizationView.h> |
|---|
| 19 | #import <openssl/md5.h> |
|---|
| 20 | #import "Prefs.h" |
|---|
| 21 | #import "GBDebug.h" |
|---|
| 22 | #import "XmlDoc.h" |
|---|
| 23 | #import "Xml.h" |
|---|
| 24 | #import "AlertHelper.h" |
|---|
| 25 | |
|---|
| 26 | static NSString* authHelperPath = @"/Library/PreferencePanes/GlimmerBlocker.prefPane/Contents/MacOS/AuthHelper"; |
|---|
| 27 | |
|---|
| 28 | @interface PrefsHelper : NSObject { |
|---|
| 29 | |
|---|
| 30 | @private |
|---|
| 31 | NSFileManager *_fm; |
|---|
| 32 | NSString* _dirPath; |
|---|
| 33 | NSString* _filePath; |
|---|
| 34 | AlertHelper* _alertHelper; |
|---|
| 35 | } |
|---|
| 36 | - (id)initWithAlertHelper:(AlertHelper*)alertHelper; |
|---|
| 37 | - (void)dealloc; |
|---|
| 38 | |
|---|
| 39 | - (NSString*)filePath; |
|---|
| 40 | - (AlertHelper*)alertHelper; |
|---|
| 41 | |
|---|
| 42 | - (XmlDoc*)load; |
|---|
| 43 | |
|---|
| 44 | @end |
|---|
| 45 | |
|---|
| 46 | @implementation PrefsHelper |
|---|
| 47 | |
|---|
| 48 | - (id)initWithAlertHelper:(AlertHelper*)alertHelper |
|---|
| 49 | { |
|---|
| 50 | if (!(self = [super init])) |
|---|
| 51 | return NULL; |
|---|
| 52 | _alertHelper = [alertHelper retain]; |
|---|
| 53 | _fm = [[NSFileManager defaultManager] retain]; |
|---|
| 54 | _dirPath = @"/Library/GlimmerBlocker"; |
|---|
| 55 | _filePath = [[NSString stringWithFormat:@"%@/PanelSettings.xml", _dirPath] retain]; |
|---|
| 56 | return self; |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | - (void)dealloc |
|---|
| 60 | { |
|---|
| 61 | [_alertHelper release]; |
|---|
| 62 | [_fm release]; |
|---|
| 63 | [_dirPath release]; |
|---|
| 64 | [_filePath release]; |
|---|
| 65 | [super dealloc]; |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | - (NSString*)filePath |
|---|
| 69 | { |
|---|
| 70 | return _filePath; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | - (AlertHelper*)alertHelper |
|---|
| 74 | { |
|---|
| 75 | return _alertHelper; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | - (XmlDoc*)load |
|---|
| 79 | { |
|---|
| 80 | if (![_fm fileExistsAtPath:_filePath]) { |
|---|
| 81 | NSLog(@"GlimmerBlocker preferences file missing: %@", _filePath); |
|---|
| 82 | return NULL; |
|---|
| 83 | } |
|---|
| 84 | NSURL* url = [NSURL fileURLWithPath:_filePath]; |
|---|
| 85 | NSData* data = [NSData dataWithContentsOfURL:url]; |
|---|
| 86 | if (!data) |
|---|
| 87 | return NULL; |
|---|
| 88 | XmlDoc* doc = [[[XmlDoc alloc] initWithURL:url withData:data] autorelease]; |
|---|
| 89 | if ([doc doc] && [doc rootE]) |
|---|
| 90 | return doc; |
|---|
| 91 | NSLog(@"Failed loading data from %@", _filePath); |
|---|
| 92 | return NULL; |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | @end |
|---|
| 96 | |
|---|
| 97 | //============================================================================================================== |
|---|
| 98 | @interface Prefs () |
|---|
| 99 | |
|---|
| 100 | - (id)initWithPrefsHelper:(PrefsHelper*)helper |
|---|
| 101 | withRootElement:(NSXMLElement*)rootE |
|---|
| 102 | withSFAuthorizationView:(SFAuthorizationView*)authView; |
|---|
| 103 | - (BOOL)checkAutoHelperPermissions; |
|---|
| 104 | |
|---|
| 105 | @end |
|---|
| 106 | |
|---|
| 107 | @implementation Prefs |
|---|
| 108 | |
|---|
| 109 | + (Prefs*)prefsWithAlertHelper:(AlertHelper*)alertHelper |
|---|
| 110 | withSFAuthorizationView:(SFAuthorizationView*)authView |
|---|
| 111 | { |
|---|
| 112 | NSAssert(alertHelper, @"alertHelper"); |
|---|
| 113 | NSAssert(authView, @"authView"); |
|---|
| 114 | PrefsHelper* helper = [[[PrefsHelper alloc] initWithAlertHelper:alertHelper] autorelease]; |
|---|
| 115 | if (!helper) |
|---|
| 116 | return NULL; |
|---|
| 117 | XmlDoc* doc = [helper load]; |
|---|
| 118 | NSXMLElement* rootE = doc ? [doc rootE] : [Xml createElementWithName:@"prefs"]; |
|---|
| 119 | return [[[Prefs alloc] initWithPrefsHelper:helper |
|---|
| 120 | withRootElement:rootE |
|---|
| 121 | withSFAuthorizationView:authView] autorelease]; |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | - (id)initWithPrefsHelper:(PrefsHelper*)helper |
|---|
| 125 | withRootElement:(NSXMLElement*)rootE |
|---|
| 126 | withSFAuthorizationView:(SFAuthorizationView*)authView |
|---|
| 127 | { |
|---|
| 128 | if (!(self = [super initWithRootElement:rootE])) |
|---|
| 129 | return NULL; |
|---|
| 130 | _helper = [helper retain]; |
|---|
| 131 | _authView = [authView retain]; |
|---|
| 132 | return self; |
|---|
| 133 | } |
|---|
| 134 | |
|---|
| 135 | - (void)dealloc |
|---|
| 136 | { |
|---|
| 137 | [_helper release]; |
|---|
| 138 | [_authView release]; |
|---|
| 139 | [super dealloc]; |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | - (void)forcedReload |
|---|
| 143 | { |
|---|
| 144 | XmlDoc* doc = [_helper load]; |
|---|
| 145 | if (!doc) { |
|---|
| 146 | NSLog(@"GB prefs can't reload xml panel prefs."); |
|---|
| 147 | return; |
|---|
| 148 | } |
|---|
| 149 | NSXMLElement* rootE = [doc rootE]; |
|---|
| 150 | if (!rootE) { |
|---|
| 151 | NSLog(@"GB prefs can't reload xml panel prefs: got doc without root element"); |
|---|
| 152 | return; |
|---|
| 153 | } |
|---|
| 154 | [self reloadWithRootElement:rootE]; |
|---|
| 155 | DebugNSLog(@"Prefs reloaded."); |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | /* |
|---|
| 159 | - (id)retain |
|---|
| 160 | { |
|---|
| 161 | NSLog(@"#### Prefs retain: %d", [self retainCount]); |
|---|
| 162 | [GBDebug debugDumpStackframe]; |
|---|
| 163 | return [super retain]; |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | - (void)release |
|---|
| 167 | { |
|---|
| 168 | NSLog(@"#### Prefs release: %d", [self retainCount]); |
|---|
| 169 | [GBDebug debugDumpStackframe]; |
|---|
| 170 | [super release]; |
|---|
| 171 | } |
|---|
| 172 | */ |
|---|
| 173 | |
|---|
| 174 | #pragma mark -------------------------------- Globals |
|---|
| 175 | - (AlertHelper*)alertHelper |
|---|
| 176 | { |
|---|
| 177 | return [_helper alertHelper]; |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | #pragma mark -------------------------------- Filters |
|---|
| 181 | |
|---|
| 182 | - (NSXMLElement*)filtersE |
|---|
| 183 | { |
|---|
| 184 | return [Xml singletonElementForXPath:@"filters" forParent:[self rootE]]; |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | - (NSXMLElement*)rulePreviewE |
|---|
| 188 | { |
|---|
| 189 | return [Xml singletonElementForXPath:@"rule-preview" forParent:[self rootE]]; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | - (BOOL)zapRulePreviewElement |
|---|
| 193 | { |
|---|
| 194 | NSArray* a = [[self rootE] elementsForName:@"rule-preview"]; |
|---|
| 195 | if (![a count]) |
|---|
| 196 | return NO; |
|---|
| 197 | for (NSXMLElement* e in a) |
|---|
| 198 | [e detach]; |
|---|
| 199 | [self markAsDirty]; |
|---|
| 200 | return YES; |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | #pragma mark -------------------------------- Save |
|---|
| 204 | |
|---|
| 205 | #ifdef DEBUG_DISABLED |
|---|
| 206 | //#ifdef DEBUG |
|---|
| 207 | #define DirtyPrefsNSLog(fmt, ...) NSLog(fmt, ## __VA_ARGS__) |
|---|
| 208 | #else |
|---|
| 209 | #define DirtyPrefsNSLog(fmt, ...) do { } while (0) |
|---|
| 210 | #endif |
|---|
| 211 | |
|---|
| 212 | - (BOOL)save |
|---|
| 213 | { |
|---|
| 214 | if (![self hasGlimmerAuth]) { |
|---|
| 215 | NSLog(@"Prefs.save without auth. Ignored."); |
|---|
| 216 | return NO; |
|---|
| 217 | } |
|---|
| 218 | if (_dirty) |
|---|
| 219 | DirtyPrefsNSLog(@"Saving dirty prefs, _numberOfRunningHelpers = %d", _numberOfRunningHelpers); |
|---|
| 220 | else |
|---|
| 221 | DirtyPrefsNSLog(@"Saves non-dirty prefs"); |
|---|
| 222 | NSData* fileContents = [self printToData]; |
|---|
| 223 | NSData* md5 = [NSData dataWithBytes:MD5([fileContents bytes], [fileContents length], NULL) length:MD5_DIGEST_LENGTH]; |
|---|
| 224 | NSArray* inputData = [NSArray arrayWithObjects:fileContents, md5, NULL]; |
|---|
| 225 | NSArray* args = [NSArray arrayWithObjects:@"--save-prefs", NULL]; |
|---|
| 226 | int status = [self runAuthHelperWithArgs:(NSArray*)args withInputData:inputData withAuth:YES]; |
|---|
| 227 | if (!status) { |
|---|
| 228 | _dirty = NO; |
|---|
| 229 | DirtyPrefsNSLog(@"Save ok, _numberOfRunningHelpers = %d", _numberOfRunningHelpers); |
|---|
| 230 | return YES; |
|---|
| 231 | } |
|---|
| 232 | DirtyPrefsNSLog(@"Save failed, _numberOfRunningHelpers = %d", _numberOfRunningHelpers); |
|---|
| 233 | if (!_hasWarnedInstallProblem) { |
|---|
| 234 | AlertHelper* ah = [self alertHelper]; |
|---|
| 235 | [ah prepareCriticalAlertWithTitle:@"Failed saving preferences" |
|---|
| 236 | withInformativeText:[_helper filePath]]; |
|---|
| 237 | [ah showPreparedAlert]; |
|---|
| 238 | } |
|---|
| 239 | return NO; |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | - (void)saveIfDirty:(id)sender |
|---|
| 243 | { |
|---|
| 244 | if (_numberOfRunningHelpers) // dunny why I get these nested calls. |
|---|
| 245 | return; |
|---|
| 246 | DirtyPrefsNSLog(@"saveIfDirty: %d, _numberOfRunningHelpers = %d", _dirty, _numberOfRunningHelpers); |
|---|
| 247 | //[GBDebug debugDumpStackframe]; |
|---|
| 248 | if (_dirty) { |
|---|
| 249 | [self save]; |
|---|
| 250 | DirtyPrefsNSLog(@" dirty: %d, _numberOfRunningHelpers = %d", _dirty, _numberOfRunningHelpers); |
|---|
| 251 | } |
|---|
| 252 | } |
|---|
| 253 | |
|---|
| 254 | - (void)markAsDirty |
|---|
| 255 | { |
|---|
| 256 | if (_dirty) |
|---|
| 257 | return; |
|---|
| 258 | DirtyPrefsNSLog(@"Prefs now dirty, _numberOfRunningHelpers = %d", _numberOfRunningHelpers); |
|---|
| 259 | _dirty = YES; |
|---|
| 260 | [self performSelectorOnMainThread:@selector(saveIfDirty:) withObject:self waitUntilDone:NO]; |
|---|
| 261 | } |
|---|
| 262 | |
|---|
| 263 | |
|---|
| 264 | #pragma mark -------------------------------- XPath scalars |
|---|
| 265 | |
|---|
| 266 | - (void)setString:(NSString*)value forXPath:(NSString*)xpath |
|---|
| 267 | { |
|---|
| 268 | NSString* old = [self stringForXPath:xpath]; |
|---|
| 269 | if (![old length] && ![value length]) |
|---|
| 270 | return; |
|---|
| 271 | if (old && value && [old isEqual:value]) |
|---|
| 272 | return; |
|---|
| 273 | DebugNSLog(@"Changes global pref (%@): '%@' -> '%@'", xpath, old, value); |
|---|
| 274 | [super setString:value forXPath:xpath]; |
|---|
| 275 | } |
|---|
| 276 | |
|---|
| 277 | #pragma mark -------------------------------- Auth |
|---|
| 278 | |
|---|
| 279 | - (AuthorizationRef)authRef |
|---|
| 280 | { |
|---|
| 281 | if (!_hasGlimmerAuth) |
|---|
| 282 | return NULL; |
|---|
| 283 | SFAuthorization* auth = [_authView authorization]; |
|---|
| 284 | if (!auth) { |
|---|
| 285 | DebugNSLog(@"Has no SFAuthorization"); |
|---|
| 286 | return NULL; |
|---|
| 287 | } |
|---|
| 288 | return [auth authorizationRef]; |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | - (void)notifyUpdatedAuthorizationState |
|---|
| 292 | { |
|---|
| 293 | _hasGlimmerAuth = YES; // so [self authRef] doesn't short-circuit to NULL |
|---|
| 294 | AuthorizationRef authRef = [self authRef]; |
|---|
| 295 | AuthorizationItem myItems = {glimmerAuthString, 0, NULL, 0}; |
|---|
| 296 | AuthorizationRights myRights = {1, &myItems}; |
|---|
| 297 | AuthorizationRights *authorizedRights = NULL; |
|---|
| 298 | OSStatus stat = AuthorizationCopyRights(authRef, &myRights, NULL, |
|---|
| 299 | kAuthorizationFlagDefaults, |
|---|
| 300 | &authorizedRights); |
|---|
| 301 | if (authorizedRights) |
|---|
| 302 | AuthorizationFreeItemSet(authorizedRights); |
|---|
| 303 | DebugNSLog(@"AuthorizationCopyRights returned %d", stat); |
|---|
| 304 | _hasGlimmerAuth = (stat == errAuthorizationSuccess); |
|---|
| 305 | [_authInExternalForm release]; |
|---|
| 306 | _authInExternalForm = NULL; |
|---|
| 307 | if (!_hasGlimmerAuth) |
|---|
| 308 | return; |
|---|
| 309 | AuthorizationExternalForm extAuth; |
|---|
| 310 | stat = AuthorizationMakeExternalForm(authRef, &extAuth); |
|---|
| 311 | if (stat == errAuthorizationSuccess) { |
|---|
| 312 | _authInExternalForm = [[NSData dataWithBytes:&extAuth length:sizeof(extAuth)] retain]; |
|---|
| 313 | } else { |
|---|
| 314 | NSLog(@"AuthorizationMakeExternalForm failed (%ld) after AuthorizationCopyRights returned success", (long)stat); |
|---|
| 315 | _hasGlimmerAuth = NO; |
|---|
| 316 | } |
|---|
| 317 | if (_hasGlimmerAuth) { |
|---|
| 318 | _hasWarnedInstallProblem = NO; |
|---|
| 319 | [self checkAutoHelperPermissions]; |
|---|
| 320 | } |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | - (BOOL)hasGlimmerAuth |
|---|
| 324 | { |
|---|
| 325 | return _hasGlimmerAuth; |
|---|
| 326 | } |
|---|
| 327 | |
|---|
| 328 | - (NSData*)authExternalForm |
|---|
| 329 | { |
|---|
| 330 | return _authInExternalForm; |
|---|
| 331 | } |
|---|
| 332 | |
|---|
| 333 | - (BOOL)has:(int)value withMask:(int)mask withKey:(NSString*)key inDict:(NSDictionary*)dict |
|---|
| 334 | { |
|---|
| 335 | NSObject* obj = [dict objectForKey:key]; |
|---|
| 336 | if (!obj || ![obj isKindOfClass:[NSNumber class]]) |
|---|
| 337 | return NO; |
|---|
| 338 | NSNumber* n = (NSNumber*)obj; |
|---|
| 339 | BOOL ok = ([n intValue] & mask) == value; |
|---|
| 340 | if (!ok) |
|---|
| 341 | DebugNSLog(@"Missing file attr: %@ = %d, expected %d with mask %d", key, [n intValue], value, mask); |
|---|
| 342 | return ok; |
|---|
| 343 | } |
|---|
| 344 | |
|---|
| 345 | - (BOOL)checkAutoHelperPermissions |
|---|
| 346 | { |
|---|
| 347 | NSDictionary* attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:authHelperPath error:NULL]; |
|---|
| 348 | if (![self has:06555 withMask:06555 withKey:NSFilePosixPermissions inDict:attrs] |
|---|
| 349 | || ![self has:0 withMask:-1 withKey:NSFileOwnerAccountID inDict:attrs]) { |
|---|
| 350 | if ([_authView authorizationState] == SFAuthorizationViewUnlockedState) |
|---|
| 351 | NSLog(@"GB Deauthorizes due to invalid AuthHelper permissions/owner."); |
|---|
| 352 | [_authView deauthorize:self]; |
|---|
| 353 | if (_hasWarnedInstallProblem) |
|---|
| 354 | return NO; |
|---|
| 355 | if (![[_helper alertHelper] canDisplayAlert]) |
|---|
| 356 | return NO; |
|---|
| 357 | _hasWarnedInstallProblem = YES; |
|---|
| 358 | AlertHelper* ah = [self alertHelper]; |
|---|
| 359 | [ah prepareCriticalAlertWithTitle:@"GlimmerBlocker needs to be reinstalled" |
|---|
| 360 | withInformativeText:@"Reason: The authentication tool does not have the proper permission assigned."]; |
|---|
| 361 | [ah showPreparedAlert]; |
|---|
| 362 | return NO; |
|---|
| 363 | } |
|---|
| 364 | _hasWarnedInstallProblem = NO; |
|---|
| 365 | return YES; |
|---|
| 366 | } |
|---|
| 367 | |
|---|
| 368 | - (void)writeData:(NSData*)data toFileHandle:(NSFileHandle*)handle |
|---|
| 369 | { |
|---|
| 370 | long long len = [data length]; |
|---|
| 371 | NSAssert(sizeof(len) == 8, @"longlong != 64"); |
|---|
| 372 | NSMutableData* tmp = [NSMutableData dataWithCapacity:sizeof(len)]; |
|---|
| 373 | [tmp appendBytes:&len length:sizeof(len)]; |
|---|
| 374 | [handle writeData:tmp]; |
|---|
| 375 | [handle writeData:data]; |
|---|
| 376 | } |
|---|
| 377 | |
|---|
| 378 | - (int)runAuthHelperWithArgs:(NSArray*)args |
|---|
| 379 | withInputData:(NSArray*)inputData // array of NSData |
|---|
| 380 | withAuth:(BOOL)withAuth |
|---|
| 381 | { |
|---|
| 382 | return [self runAuthHelperWithArgs:args |
|---|
| 383 | withInputData:inputData |
|---|
| 384 | withOutputData:NULL |
|---|
| 385 | withAuth:withAuth]; |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | - (int)runAuthHelperWithArgs:(NSArray*)args |
|---|
| 389 | withInputData:(NSArray*)inputData |
|---|
| 390 | withOutputData:(NSMutableData*)outputData |
|---|
| 391 | withAuth:(BOOL)withAuth |
|---|
| 392 | { |
|---|
| 393 | NSAssert([args count], @"Must have --command"); |
|---|
| 394 | if (withAuth && ![self hasGlimmerAuth]) { |
|---|
| 395 | NSLog(@"Can't run helper tool: required auth, but doesn't have it: %@", [args objectAtIndex:0]); |
|---|
| 396 | return -1; |
|---|
| 397 | } |
|---|
| 398 | /* |
|---|
| 399 | For some reason [writeHandle closeFile] creates a NSUncaughtSystemExceptionException |
|---|
| 400 | (it jumps to 0xffffffff) |
|---|
| 401 | if the tool exits with an error before reading the input. |
|---|
| 402 | */ |
|---|
| 403 | if (![self checkAutoHelperPermissions]) |
|---|
| 404 | return -1; |
|---|
| 405 | [self retain]; |
|---|
| 406 | _numberOfRunningHelpers++; |
|---|
| 407 | NSData* authData = withAuth ? [self authExternalForm] : NULL; |
|---|
| 408 | NSAssert(!withAuth || authData, @"Auth"); |
|---|
| 409 | [args retain]; |
|---|
| 410 | NSTask* task = [[NSTask alloc] init]; |
|---|
| 411 | int status = 0; |
|---|
| 412 | NSPipe *writePipe = NULL; |
|---|
| 413 | NSFileHandle *writeHandle = NULL; |
|---|
| 414 | NSPipe *readPipe = NULL; |
|---|
| 415 | NSFileHandle *readHandle = NULL; |
|---|
| 416 | @try { |
|---|
| 417 | [task setLaunchPath:authHelperPath]; |
|---|
| 418 | [task setArguments:args]; |
|---|
| 419 | [task setCurrentDirectoryPath:@"/tmp"]; |
|---|
| 420 | if (outputData) { |
|---|
| 421 | readPipe = [[NSPipe pipe] retain]; |
|---|
| 422 | readHandle = [[readPipe fileHandleForReading] retain]; |
|---|
| 423 | [task setStandardOutput:readPipe]; |
|---|
| 424 | } |
|---|
| 425 | if (withAuth || inputData) { |
|---|
| 426 | writePipe = [[NSPipe pipe] retain]; |
|---|
| 427 | writeHandle = [[writePipe fileHandleForWriting] retain]; |
|---|
| 428 | [task setStandardInput:writePipe]; |
|---|
| 429 | [task launch]; |
|---|
| 430 | if (withAuth) |
|---|
| 431 | [self writeData:authData toFileHandle:writeHandle]; |
|---|
| 432 | if (inputData) { |
|---|
| 433 | for (NSData* data in inputData) |
|---|
| 434 | [self writeData:data toFileHandle:writeHandle]; |
|---|
| 435 | } |
|---|
| 436 | [writeHandle closeFile]; |
|---|
| 437 | } else { |
|---|
| 438 | [task launch]; |
|---|
| 439 | } |
|---|
| 440 | while (outputData) { |
|---|
| 441 | NSData* data = [readHandle availableData]; |
|---|
| 442 | if (!data || ![data length]) |
|---|
| 443 | break; |
|---|
| 444 | [outputData appendData:data]; |
|---|
| 445 | } |
|---|
| 446 | [task waitUntilExit]; |
|---|
| 447 | status = [task terminationStatus]; |
|---|
| 448 | //DebugNSLog(@"task retains: %d", [task retainCount]); |
|---|
| 449 | } |
|---|
| 450 | @catch (NSException* ex) { |
|---|
| 451 | status = -1; |
|---|
| 452 | NSLog(@"AuthHelper failed with exception: %@", [ex name]); |
|---|
| 453 | [GBDebug debugDumpStackframe]; |
|---|
| 454 | } |
|---|
| 455 | if ([task isRunning]) |
|---|
| 456 | [task terminate]; |
|---|
| 457 | [task release]; |
|---|
| 458 | [writeHandle release]; |
|---|
| 459 | [writePipe release]; |
|---|
| 460 | [readHandle release]; |
|---|
| 461 | [readPipe release]; |
|---|
| 462 | [args release]; |
|---|
| 463 | _numberOfRunningHelpers--; |
|---|
| 464 | if (status) |
|---|
| 465 | DebugNSLog(@"Status from auto-helper %@: %d", [args objectAtIndex:0], status); |
|---|
| 466 | [self release]; |
|---|
| 467 | return status; |
|---|
| 468 | } |
|---|
| 469 | |
|---|
| 470 | @end |
|---|