/* * Grouch.app Copyright (C) 2006 Andy Sveikauskas * ------------------------------------------------------------------------ * This program is free software under the GNU General Public License * -- * This is the ugliest class in the whole program. I suggest you look away. * Or look at the header, as it's easier to intuit from that. */ #import #import #import #import #define node oscarbuffer_node #import #import #import #include /* For malloc() etc. */ #include /* For memcpy(), strlen() */ #ifdef GNUSTEP #define net_swap16 GSSwapBigI16ToHost #define net_swap32 GSSwapBigI32ToHost #else #ifndef NO_STDINT #include #endif #include #include typedef uint16_t gsu16; typedef uint32_t gsu32; #define net_swap16 htons #define net_swap32 htonl #endif int oscar_int16_get( const void *p ) { gsu16 i; memcpy( &i, p, sizeof(i) ); return net_swap16(i); } long oscar_int32_get( const void *p ) { gsu32 i; memcpy( &i, p, sizeof(i) ); return net_swap32(i); } @implementation OscarBuffer + (OscarBuffer*) snacWithFamily:(int)fam andType:(int)type andFlags:(int)fgs andTag:r { OscarBuffer *buf = [OscarBuffer new]; [[[[buf addInt16:fam] addInt16:type] addInt16:fgs] addInt32:[OscarCache newTag:r]]; return buf; } + (OscarBuffer*) snacWithFamily:(int)fam andType:(int)type andTag:r { return [self snacWithFamily:fam andType:type andFlags:0 andTag:r]; } - init { [super init]; head = tail = NULL; return self; } - (void)dealloc { struct node *n = head; while( n ) { switch( n->type ) { case BUFFER: if( n->data.mem.buf ) free( n->data.mem.buf ); break; default: [n->data.obj release]; } n = n->next; } [super dealloc]; } - createHeapBuffer:(void**)ptr withLength:(size_t*)l { OscarBuffer *buf = [OscarBuffer new]; struct node *n = head; while( n ) { switch( n->type ) { case BUFFER: [buf addBuffer:n->data.mem.buf withLength:n->data.mem.len]; break; case OBJ: { void *buf2; size_t len; [n->data.obj createHeapBuffer:&buf2 withLength:&len]; if( buf2 ) { [buf addBuffer:buf2 withLength:len]; free( buf2 ); } } } n = n->next; } if( buf->head ) { *ptr = buf->head->data.mem.buf; *l = buf->head->data.mem.len; buf->head->data.mem.buf = NULL; } else { *ptr = NULL; *l = 0; } [buf release]; return self; } - add:obj { struct node *n; if( [(NSObject*)obj isKindOfClass:[NSString class]] ) return [self addString:obj]; n = malloc(sizeof(struct node)); if( n ) { n->type = OBJ; n->data.obj = [obj retain]; n->next = NULL; if( tail ) tail = (tail->next = n); else head = tail = n; } else [GrouchException raiseMemoryException]; return self; } - addBuffer:(const void *)data withLength:(size_t) len { size_t alloc; if( !tail || tail->type != BUFFER ) { struct node *n = malloc(sizeof(struct node)); if( !n ) [GrouchException raiseMemoryException]; n->type = BUFFER; n->data.mem.buf = NULL; n->data.mem.len = n->data.mem.alloc = 0; n->next = NULL; if( tail ) tail = (tail->next = n); else head = tail = n; } alloc = tail->data.mem.alloc ? tail->data.mem.alloc : 1; while( alloc-tail->data.mem.len < len ) alloc *= 2; if( alloc != tail->data.mem.alloc ) { void *buf = realloc(tail->data.mem.buf, alloc); if( buf ) { tail->data.mem.buf = buf; tail->data.mem.alloc = alloc; } else [GrouchException raiseMemoryException]; } memcpy( ((char*)tail->data.mem.buf) + tail->data.mem.len, data, len ); tail->data.mem.len += len; return self; } - addByte:(char)c { return [self addBuffer:&c withLength:1]; } - addInt16:(int)i { gsu16 buf = net_swap16(i); return [self addBuffer:&buf withLength:sizeof(buf)]; } - addInt32:(int)i { gsu32 buf = net_swap32(i); return [self addBuffer:&buf withLength:sizeof(buf)]; } - addString:(NSString*)so { const char *s = [so cString]; size_t l; if( !s ) s = ""; l = strlen(s); if( l > 0xff ) l = 0xff; return [[self addByte:l] addBuffer:s withLength:l]; } - addTLV:(int)type { return [[self addInt16:type] addInt16:0]; } - addTLV:(int)type with:obj { void *buf; size_t len; [obj createHeapBuffer:&buf withLength:&len]; [[[self addInt16:type] addInt16:len] addBuffer:buf withLength:len]; if( buf ) free( buf ); return self; } - addTLV:(int)type withBuffer:(const void*)buf andLength:(size_t)l { return [[[self addInt16:type] addInt16:l] addBuffer:buf withLength:l]; } - addTLV:(int)type withInt16:(int)i { return [[[self addInt16:type] addInt16:2] addInt16:i]; } - addTLV:(int)type withInt32:(int)i { return [[[self addInt16:type] addInt16:4] addInt32:i]; } - addTLV:(int)type withString:(NSString*)s { const char *p = [s cString]; size_t l = strlen(p?p:""); return [[[self addInt16:type] addInt16:l] addBuffer:p withLength:l]; } // Crummy functions to edit lists - (OscarIncomingSnac*)bufferAsSnac { void *buf = NULL; size_t len = 0; [self createHeapBuffer:&buf withLength:&len]; // Create an NSData object so that it gets free()d on autorelease [NSData dataWithBytesNoCopy:buf length:len freeWhenDone:YES]; return [[OscarIncomingSnac snacAtHeaderlessBuffer:buf withLength:len] autorelease]; } - (OscarTlvListIn*)bufferAsTlv { return [[self bufferAsSnac] readTLVs]; } - (OscarBuffer*)bufferByRemovingTlv:(int)offendingType { OscarIncomingSnac *obj = [self bufferAsSnac]; OscarBuffer *r = [[OscarBuffer new] autorelease]; const void *buf = [obj buffer]; size_t len = 0; while( [obj bytesRemaining] ) { int type = [obj readInt16]; int tlvLen = [obj readInt16]; [obj skipBytes:tlvLen]; if( type == offendingType ) { if( buf && len ) [r addBuffer:buf withLength:len]; buf = [obj buffer]; len = 0; } else len += tlvLen + 4; } return r; } @end