/* * Grouch.app Copyright (C) 2006 Andy Sveikauskas * ------------------------------------------------------------------------ * This program is free software under the GNU General Public License * -- * Flap, flap. That's the noise that penguins make. * * This class is a special kind of socket that speaks AOL's "flap" * and feeds incoming server garbage into SNAC handler classes. * * Lots of ugly things happen here, I'm afraid. */ #import #import #import #import #import #import #import #import #import #include @implementation OscarFlap + (OscarFlap*)socketForHost:(NSString*)name withCredentials:(id)c andSnacHandler:(OscarSnacHandler*)snac forClient:(OscarClient*)cli withRunLoop:(NSRunLoop*)rloop { OscarFlap *r = [OscarFlap new]; NS_DURING [r initForHost:name withCredentials:c andSnacHandler:snac forClient:cli withRunLoop:rloop]; NS_HANDLER [r release]; [localException raise]; NS_ENDHANDLER return r; } + (OscarFlap*)socketForHost:(NSString*)name atPort:(int)port withCredentials:(id)c andSnacHandler:(OscarSnacHandler*)snac forClient:(OscarClient*)cli withRunLoop:(NSRunLoop*)rloop { OscarFlap *r = [OscarFlap new]; NS_DURING [r initForHost:name atPort:port withCredentials:c andSnacHandler:snac forClient:cli withRunLoop:rloop]; NS_HANDLER [r release]; [localException raise]; NS_ENDHANDLER return r; } - init { [super init]; inSeq = outSeq = 0; gotSeq = NO; creds = nil; snacs = nil; [self setKeepAlive:60*2]; // Random return self; } - (void)dealloc { if( creds ) [(NSObject*)creds release]; if( snacs ) [snacs release]; [super dealloc]; } - initForHost:(NSString*)name atPort:(int)port withCredentials:(id)c andSnacHandler:(OscarSnacHandler*)snac forClient:(OscarClient*)cli withRunLoop:(NSRunLoop*)rloop { [(NSObject*)(creds=c) retain]; [snacs=snac retain]; oscar = cli; return [super initForHost:name atPort:port withRunLoop:rloop]; } - initForHost:(NSString*)name withCredentials:(id)c andSnacHandler:(OscarSnacHandler*)snac forClient:(OscarClient*)cli withRunLoop:(NSRunLoop*)rloop { NSRange colon = [name rangeOfString:@":"]; if( colon.location != NSNotFound ) { NSString *newName, *port; id r = nil; colon.length = colon.location; colon.location = 0; newName = [name substringWithRange:colon]; port = [name substringFromIndex:colon.length+1]; r = [self initForHost:newName atPort:[port intValue] withCredentials:c andSnacHandler:snac forClient:cli withRunLoop:rloop]; return r; } else return [self initForHost:name atPort:5190 withCredentials:c andSnacHandler:snac forClient:cli withRunLoop:rloop]; } - (void)write:(OscarBuffer*)buf withType:(OscarFlapType)type { void *a, *b; size_t al, bl; OscarBuffer *header = [OscarBuffer new]; [[[header addByte:'*'] addByte:type] addInt16:outSeq++]; [buf createHeapBuffer:&b withLength:&bl]; [header addInt16:bl]; [header createHeapBuffer:&a withLength:&al]; [self writeData:a withLength:al]; free(a); [self writeData:b withLength:bl]; free(b); [header release]; } - (void)write:(OscarBuffer*)buf { [self write:buf withType:FLAP_DATA]; } - (void)handleSnacForClient:(OscarFlap*)sock ofFamily:(int)fam andType:(int)type withFlags:(int)flags andTag:tag buffer:(OscarIncomingSnac*)buf { [snacs handleSnacForClient:sock ofFamily:fam andType:type withFlags:flags andTag:tag buffer:buf]; } - (OscarSnacHandler*)snacHandler { return snacs; } - (OscarClient*)client { return oscar; } - (void)setClient:(OscarClient*)client { oscar = client; } - (void)keepAlive:sock { OscarBuffer *buf = [OscarBuffer new]; [self write:buf withType:FLAP_KEEPALIVE]; [buf release]; } #define FLAP_HEADER_SIZE 6 #define FLAP_MAGIC '*' - (void)bytesIn:sock atBuffer:(const void*)buf withLength:(size_t)len { while( len > FLAP_HEADER_SIZE ) { const char *p = buf; unsigned int tp, seq, l; if( len < FLAP_HEADER_SIZE ) break; tp = p[1]; seq = oscar_int16_get(p+2); l = oscar_int16_get(p+4); if( *p != FLAP_MAGIC ) { bad: [self close]; return; } if( len < l+FLAP_HEADER_SIZE ) break; if( !gotSeq ) { inSeq = seq; gotSeq = YES; } else if( (++inSeq%65536) != seq ) goto bad; [[self retain] autorelease]; [self handleIncomingData:tp atBuffer:p+FLAP_HEADER_SIZE withLength: l]; l += FLAP_HEADER_SIZE; [self removeBytesFromInputBuffer:l]; len -= l; } } - (void)handleIncomingData:(int)typ atBuffer:(const void*)buf withLength:(size_t)len { switch(typ) { case FLAP_SIGNON: { OscarBuffer *b = [OscarBuffer new]; [[b addInt16:0] addInt16:1]; [self sendingCookie]; [creds writeCredentials:b]; [(NSObject*)creds release]; creds = nil; [self write:b withType:FLAP_SIGNON]; [b release]; break; } case FLAP_DATA: if( len >= SNAC_SIZE ) { OscarIncomingSnac *snac = [OscarIncomingSnac snacAtBuffer:buf withLength:len]; [self handleSnacForClient:self ofFamily:[snac family] andType:[snac type] withFlags:[snac flags] andTag:[OscarCache getTag:[snac tagID]] buffer:snac]; [snac release]; } break; case FLAP_KEEPALIVE: break; case FLAP_SIGNOFF: { OscarTlvListIn *data; data = [OscarTlvListIn listFromBuffer:buf andLength:len andTLVs:NULL]; [self handleSignoffFlap:data]; [data release]; } default: [self close]; } } - (void)handleSignoffFlap:(OscarTlvListIn*)buf { } - (void)sendingCookie { } - (void)welcome { } @end #import #import #import #import #import // XXX this is an ugly hack @implementation OscarDetachedFlapInitializer : NSObject + go:(OscarFlap*)flap host:(NSString*)_host port:(int)_port creds:_creds snac:(OscarSnacHandler*)_snac client:(OscarClient*)_cli loop:(NSRunLoop*)_loop fatal:(BOOL)_fatal { OscarDetachedFlapInitializer *r = [OscarDetachedFlapInitializer new]; r->host = _host; r->port = _port; r->creds = _creds; r->snac = _snac; r->cli = _cli; r->loop = [[GrouchRunLoopHack alloc] initForRunLoop:_loop]; r->fatal = _fatal; [r go:flap]; return self; } + go:(OscarFlap*)flap host:(NSString*)_str creds:_creds snac:(OscarSnacHandler*)_snac client:(OscarClient*)_cli loop:(NSRunLoop*)_loop fatal:(BOOL)_fatal { return [OscarDetachedFlapInitializer go:flap host:_str port:-1 creds:_creds snac:_snac client:_cli loop:_loop fatal:_fatal]; } - (void)go:(OscarFlap*)flap { usingThreading = YES; [(NSObject*)creds retain]; [host retain]; #ifndef NO_THREADING NS_DURING [NSThread detachNewThreadSelector:@selector(activate:) toTarget:self withObject:flap]; NS_HANDLER NSLog(@"Warning: threading not supported"); #endif usingThreading = NO; [loop release]; loop = [NSRunLoop currentRunLoop]; [self activate:flap]; #ifndef NO_THREADING NS_ENDHANDLER #endif } - (void)activate:(OscarFlap*)flap { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NS_DURING if( port < 0 ) [flap initForHost:host withCredentials:creds andSnacHandler:snac forClient:cli withRunLoop:loop]; else [flap initForHost:host atPort:port withCredentials:creds andSnacHandler:snac forClient:cli withRunLoop:loop]; NS_HANDLER if( [[localException name] isEqual:[GrouchException socketException]] ) { NSObject *target = (id)[cli getUI]; NSString *error = [localException reason]; SEL sel = @selector(error:fatal:); NSInvocation *invok = [ NSInvocation invocationWithMethodSignature: [target methodSignatureForSelector:sel] ]; [invok setTarget:target]; [invok setSelector:sel]; [invok setArgument:&error atIndex:2]; [invok setArgument:&fatal atIndex:3]; if( [loop isKindOfClass:[GrouchRunLoopHack class]] ) { [(GrouchRunLoopHack*)loop addInvocation:invok withArguments:error]; [(GrouchRunLoopHack*)loop invalidate]; loop = nil; } else [invok invoke]; } else { [pool release]; [(NSObject*)creds release]; [host release]; [localException raise]; } NS_ENDHANDLER [pool release]; [(NSObject*)creds release]; [host release]; { BOOL thread = usingThreading; [self release]; if( thread ) [NSThread exit]; } } @end