// // P4Connection.m // P4ObjectLayer // // Created by Michael Bishop on 4/8/11. // Copyright 2011 Numerical Garden, LLC. All rights reserved. // #import "P4Connection.h" #import "P4Response.h" #import "P4Port.h" #import "NGAUtilities.h" #import "P4ErrorCodes.h" #import "P4Keychain.h" @interface P4Connection () + (NSMutableDictionary*)connections; - (id)initWithPortString:(NSString*)portstring user:(NSString*)username; @property (nonatomic,readwrite) BOOL needsNewPassword; @property (nonatomic,readonly) NSString * password; @property (nonatomic,readwrite) BOOL requiresTickets; @end @implementation P4Connection @synthesize portString = _portstring , username = _username , needsNewPassword = _needsNewPassword , requiresTickets = _requiresTickets ; - (id)init { return [self initWithPortString:nil user:nil]; } - (id)initWithPortString:(NSString*)portstring user:(NSString*)username { if ((self = [super init]) == nil) return nil; _portstring = [portstring retain]; _username = [username retain]; return self; } DEALLOC( RELEASE(_portstring); RELEASE(_username); ) SINGLETON_ACCESSOR_IMPLEMENTATION(NSMutableDictionary, connections) +(P4Connection*)connectionWithPortString:(NSString*)portstring user:(NSString*)username { NSObject * key = [NSArray arrayWithObjects:portstring, username, nil]; NSMutableDictionary* connections = [self connections]; @synchronized (connections) { id connection = [connections objectForKey:key]; if (!connection) { connection = [[[P4Connection alloc] initWithPortString:portstring user:username] autorelease]; [connections setObject:connection forKey:key]; } return connection; } return nil; } -(BOOL)argumentsRequireAPassword:(NSArray*)arguments { if ( [[arguments objectAtIndex:0] isEqualToString:@"info"] ) return NO; if ( [[arguments objectAtIndex:0] isEqualToString:@"users"] ) return NO; return YES; } -(NSString*)password { return [[P4Keychain sharedKeychain] passwordForUser:self.username port:self.portString]; } -(P4Response*)runArguments:(NSArray*)arguments withContext:(NSDictionary*)context content:(NSString *)content { NSMutableDictionary * newContext = [NSMutableDictionary dictionaryWithDictionary:context]; if ( self.username ) [newContext setObject:self.username forKey:kP4ConnectionUserContextKey]; if ( !self.requiresTickets && self.password ) [newContext setObject:self.password forKey:kP4ConnectionPasswordContextKey]; return [self.p4port runArguments:arguments withContext:newContext content:content]; } -(BOOL)runArguments:(NSArray*)arguments withContext:(NSDictionary*)context updateBlock:(UpdateBlock)update completionBlock:(void(^)(P4Response*))completion { return [self runArguments:arguments withContext:context content:nil updateBlock:update completionBlock:completion]; } -(BOOL)runArguments:(NSArray*)arguments withContext:(NSDictionary*)context content:(NSString*)content updateBlock:(UpdateBlock)update completionBlock:(void(^)(P4Response*))completion { NSMutableDictionary * newContext = [NSMutableDictionary dictionaryWithDictionary:context]; if ( self.username ) [newContext setObject:self.username forKey:kP4ConnectionUserContextKey]; if ( !self.requiresTickets && self.password ) [newContext setObject:self.password forKey:kP4ConnectionPasswordContextKey]; return [self.p4port runArguments:arguments withContext:newContext content:content updateBlock:update completionBlock:^(P4Response*response) { if ( (response.error.code == kP4ErrorTicketExpired || response.error.code == kP4ErrorLoginNeeded) && [P4ErrorDomain isEqualToString:response.error.domain] ) { self.requiresTickets = YES; [newContext removeObjectForKey:kP4ConnectionPasswordContextKey]; [self.p4port runArguments:[NSArray arrayWithObject:@"login"] withContext:newContext content:self.password updateBlock:nil completionBlock:^(P4Response * loginResponse) { if ( loginResponse.error && (response.error.code == kP4ErrorTicketExpired || response.error.code == kP4ErrorLoginNeeded) && [P4ErrorDomain isEqualToString:loginResponse.error.domain]) { self.needsNewPassword = YES; completion(response); return; } self.needsNewPassword = NO; [self runArguments:arguments withContext:context updateBlock:update completionBlock:completion]; }]; return; } if ( response.error.code == kP4ErrorInvalidPassword && [P4ErrorDomain isEqualToString:response.error.domain] ) { self.needsNewPassword = YES; } if ( [self argumentsRequireAPassword:arguments] && !response.error ) self.needsNewPassword = NO; completion(response); }]; } -(P4Port*)p4port { return [P4Port portWithPortString:self.portString]; } @end