/* * Grouch.app Copyright (C) 2006 Andy Sveikauskas * ------------------------------------------------------------------------ * This program is free software under the GNU General Public License * -- * This class implements the commands for the BOS service. The BOS service * is where most of the action is. However, some some of what goes on * there is also devided by SNAC families. * * These other points of interest are: * * OscarErrorHandler error messages! fun fun! * OscarServiceHandler mostly handshake stuff * OscarLookupHandler profiles, away messages * OscarListHandler buddies signing in and out * OscarMessageHandler IMs (see also OscarCapHandler) * OscarSsiHandler service side buddy list management * OscarChatNavHandler chat room info * OscarChatHandler chat room messages */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import @interface OscarSnac00010002Bos : OscarSnac00010002Base - (void)pre:(OscarFlap*)flap; @end @implementation OscarSnac00010002Bos - (void)pre:(OscarFlap*)flap { // Ask for buddy list. OscarBuffer *buf = [OscarBuffer snacWithFamily:0x13 andType:0x04 andFlags:0 andTag:nil]; [flap write:buf]; [buf release]; // Set rate limit to something sane. buf = [OscarBuffer snacWithFamily:0x04 andType:0x02 andFlags:0 andTag:nil]; [buf addInt16:0]; // Channel [buf addInt32:3|8]; // Flags [buf addInt16:8000]; // msg len (highest allowed without err) [buf addInt16:999]; // warn [buf addInt16:999]; // warn [buf addInt16:0]; // delay between messages in [buf addInt16:0]; // don't know what this means [flap write:buf]; [buf release]; } @end // Service redirect @interface OscarSnac10000005 : NSObject - (void)handleSnacForClient:(OscarFlap*)flap ofFamily:(int)family andType:(int)type withFlags:(int)flags andTag:tag buffer:(OscarIncomingSnac*)buf; - (void)handleRedirect:(NSString*)host forFamily:(int)family withCookie:(OscarAuthCookie*)cookie andTag:tag andFlap:(OscarFlap*)flap; @end @implementation OscarSnac10000005 - (void)handleSnacForClient:(OscarFlap*)flap ofFamily:(int)family andType:(int)type withFlags:(int)flags andTag:tag buffer:(OscarIncomingSnac*)buf { [buf skipBytes:8]; // XXX why? OscarTlvListIn *tlv = [buf readTLVs]; NSString *host; int serv; OscarAuthCookie *cookie; if( !(buf=[tlv getTLV:0x0d]) ) return; serv = [buf readInt16]; if( !(buf=[tlv getTLV:0x05]) ) return; host = [buf readRawString]; cookie = [OscarAuthCookie cookieFromTLV:tlv]; if( !cookie ) return; [self handleRedirect:host forFamily:serv withCookie:cookie andTag:tag andFlap:flap]; } - (void)handleRedirect:(NSString*)host forFamily:(int)family withCookie:(OscarAuthCookie*)cookie andTag:tag andFlap:(OscarFlap*)flap { switch( family ) { case 0x0d: // ChatNav [[flap client] gotChatNav:host withCookie:cookie]; break; case 0x0e: // Chat [[flap client] gotChannel:tag atHost:host withCookie:cookie]; break; } } @end @interface OscarBosServiceHandler : OscarServiceHandler - init; @end @implementation OscarBosServiceHandler - init { [super init]; [self addHandler:[OscarSnac00010002Bos new] forType:0x02]; [self addHandler:[OscarSnac10000005 new] forType:0x05]; return self; } @end @interface OscarBosSnacHandler : OscarSnacHandler + get; - init; @end @implementation OscarBosSnacHandler + get { static id r = nil; if( r ) return r; else return r = [self new]; } - init { OscarSnacFamilyHandler *h; [super init]; [self addFamily:[OscarBosServiceHandler new]]; [self addFamily:[OscarLookupHandler get]]; [self addFamily:[OscarListHandler get]]; [self addFamily:[OscarMessageHandler get]]; [self addFamily:[OscarSsiHandler get]]; /* * We want these version numbers but we don't want the actual * SNAC handlers here, since they expect a different type of socket. */ { OscarSnacFamilyHandler *handlers[2], **p = handlers; int n = sizeof(handlers)/sizeof(void*); handlers[0] = [OscarChatNavHandler get]; handlers[1] = [OscarChatHandler get]; while( n-- ) { h = [OscarSnacFamilyHandler new]; [h initForFamily:[*p family] andVersion:[*p version] andDLL:[*p dll]]; ++p; [self addFamily:h]; } } /* * BOS wants one of these. We don't handle its messages. */ h = [OscarSnacFamilyHandler new]; [h initForFamily:0x09 andVersion:0x01 andDLL:0x0629]; [self addFamily:h]; h = [OscarSnacFamilyHandler new]; [h initForFamily:0x0a andVersion:0x01 andDLL:0x0629]; return self; } @end @implementation OscarBos + bosForHost:(NSString*)host withCredentials:(id)cred andClient:(OscarClient*)cli { OscarBos *r = [OscarBos new]; if( [r initForHost:host withCredentials:cred andClient:cli] ) return r; else { [r release]; return nil; } } - init { [super init]; away = profile = nil; return self; } - (void)dealloc { if( away ) [away release]; if( profile ) [profile release]; [super dealloc]; } - initForHost:(NSString*)host withCredentials:(id)cred andClient:(OscarClient*)cli { [OscarDetachedFlapInitializer go:self host:host creds:cred snac:[OscarBosSnacHandler get] client:cli loop:[NSRunLoop currentRunLoop] fatal:YES]; return self; } - (void)sendingCookie { OscarClient *cli = [self client]; [[cli getUI] statusMessage:[GrouchString getString:@"oscar-cookie" withBundle:[NSBundle bundleForClass:[self class]]]]; } -(void)sendMessage:(NSString*)msg to:(NSString*)user withFlags:(GrouchMessageFlags)f { OscarClient *cli = [self client]; OscarBuffer *outb; static int ok = 0; if( (f&GrouchMessageReflect) ) { id u = [[cli getUI] getUser:user]; [u message:msg withFlags:f]; } outb = [OscarBuffer snacWithFamily:0x04 andType:0x06 andFlags:0 andTag:nil]; [outb addInt32:time(NULL)]; [outb addInt32:++ok]; [outb addInt16:1]; // ??? [outb addString:user]; { NSString *dontcare; NSStringEncoding enc; int which1, which2; NSData *junk; OscarBuffer *buf2 = [OscarBuffer new]; OscarBuffer *buf3 = [OscarBuffer new]; [OscarEncoding encodeString:msg encodingName:&dontcare encodingID:&enc intFlag:&which1 and:&which2]; junk = [msg dataUsingEncoding:enc]; [buf2 addTLV:0x0501 withBuffer:NULL andLength:0]; [buf3 addInt16:which1]; [buf3 addInt16:which2]; [buf3 addBuffer:[junk bytes] withLength:[junk length]]; [buf2 addTLV:0x0101 with:buf3]; [outb addTLV:0x02 with:buf2]; [buf2 release]; [buf3 release]; } if( (f&GrouchMessageAway) ) [outb addTLV:0x04 withBuffer:NULL andLength:0]; [self write:outb]; [outb release]; } - (void)addProfileString:(NSString*)str toBuffer:(OscarBuffer*)buf withTLV:(int)tlv { static NSString *fmt = @"text/x-aolrtf; charset=\"%@\""; NSString *encoding; NSStringEncoding enc; int dontcare; NSData *junk; [OscarEncoding encodeString:str encodingName:&encoding encodingID:&enc intFlag:&dontcare and:&dontcare]; [buf addTLV:tlv withString:[NSString stringWithFormat:fmt,encoding]]; junk = [str dataUsingEncoding:enc]; [buf addTLV:tlv+1 withBuffer:[junk bytes] andLength:[junk length]]; } - (void)updateInfo { NSData *caps = [[[self client] capHandler] capabilityBuffer]; OscarBuffer *outb = [OscarBuffer snacWithFamily:0x02 andType:0x04 andFlags:0 andTag:nil]; [self addProfileString:profile?profile:@"" toBuffer:outb withTLV:0x01]; [self addProfileString:away?away:@"" toBuffer:outb withTLV:0x03]; [outb addTLV:0x05 withBuffer:[caps bytes] andLength:[caps length]]; [self write:outb]; [outb release]; } - (void)profile:(NSString*)str { if( profile != str ) { if( profile ) [profile release]; profile = str ? [str retain] : str; } [self updateInfo]; } - (void)away:(NSString*)msg { if( away != msg ) { if( away ) [away release]; if( (away=msg) ) [away retain]; } [self updateInfo]; } - (void)idle:(time_t)i { OscarBuffer *outb = [OscarBuffer snacWithFamily:0x01 andType:0x11 andFlags:0 andTag:nil]; [outb addInt32:i]; [self write:outb]; [outb release]; } enum { REQ_PROFILE = 0x1, REQ_AWAY_MSG = 0x2 }; - (void)getInfo:(id)prof forUser:(NSString*)str withFlags:(int)flags { OscarBuffer *outb = [OscarBuffer snacWithFamily:0x02 andType:0x15 andFlags:0 andTag:prof]; [outb addInt32:flags]; [outb addString:str]; [self write:outb]; [outb release]; } - (void)getInfo:(id)prof forUser:(NSString*)str { [self getInfo:prof forUser:str withFlags:(REQ_PROFILE | REQ_AWAY_MSG)]; } - (void)getAwayMessage:(id)prof forUser:(NSString*)str { [self getInfo:prof forUser:str withFlags:REQ_AWAY_MSG]; } - (void)askForChatNav { OscarBuffer *outb = [OscarBuffer snacWithFamily:0x01 andType:0x04 andFlags:0 andTag:nil]; [outb addInt16:0x0d]; [self write:outb]; [outb release]; } - (void)askForChannel:(NSString*)channel withCookie:(NSString*)cookie andExchange:(int)ex andInstance:(int)inst { OscarBuffer *outb = [OscarBuffer snacWithFamily:0x01 andType:0x04 andFlags:0 andTag:channel]; OscarBuffer *tlv = [OscarBuffer new]; [outb addInt16:0x0e]; [tlv addInt16:ex]; [tlv addString:cookie]; [tlv addInt16:inst]; [outb addTLV:0x01 with:tlv]; [tlv release]; [self write:outb]; [outb release]; } - (void)ssiBegin { OscarBuffer *outb; outb = [OscarBuffer snacWithFamily:0x13 andType:0x11 andFlags:0 andTag:nil]; [self write:outb]; [outb release]; } - (void)ssiEnd { OscarBuffer *outb; outb = [OscarBuffer snacWithFamily:0x13 andType:0x12 andFlags:0 andTag:nil]; [self write:outb]; [outb release]; } - (void)addSsiRecords:(NSArray*)records { OscarBuffer *outb; int i; [self ssiBegin]; outb = [OscarBuffer snacWithFamily:0x13 andType:0x08 andFlags:0 andTag:records]; for( i=0; i<[records count]; ++i ) { OscarSsiRecord *rec = [records objectAtIndex:i]; [rec output:outb]; } [self write:outb]; [outb release]; [self ssiEnd]; } - (void)updateSsiRecords:(NSArray*)records { OscarBuffer *outb; int i; [self ssiBegin]; outb = [OscarBuffer snacWithFamily:0x13 andType:0x09 andFlags:0 andTag:nil]; for( i=0; i<[records count]; ++i ) { OscarSsiRecord *rec = [records objectAtIndex:i]; [rec output:outb children:NO]; } [self write:outb]; [outb release]; [self ssiEnd]; } - (void)removeSsiRecords:(NSArray*)records { OscarBuffer *outb; int i; [self ssiBegin]; outb = [OscarBuffer snacWithFamily:0x13 andType:0x0a andFlags:0 andTag:nil]; for( i=0; i<[records count]; ++i ) { OscarSsiRecord *rec = [records objectAtIndex:i]; [rec output:outb]; } [self write:outb]; [outb release]; [self ssiEnd]; } - (void)welcome { [[self client] welcome]; [self askForChatNav]; } - (void)connectionClosed:sock { OscarClient *cli = [self client]; if( cli ) [cli lostBos]; } @end