/*************************************************************************** IRCObject.m ------------------- begin : Thu May 30 22:06:25 UTC 2002 copyright : (C) 2005 by Andrew Ruder email : aeruder@ksu.edu ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; either version 2.1 of the * * License or (at your option) any later version. * * * ***************************************************************************/ /** * IRCObject reference * * * * * Revision 1 * November 8, 2003 * Andrew Ruder *

* Much of the information presented in this document is based off * of information presented in RFC 1459 (Oikarinen and Reed 1999). * This document is NOT aimed at reproducing the information in the RFC, * and the RFC should still always be consulted for various server-related * replies to messages and proper format of the arguments. In short, if you * are doing a serious project dealing with IRC, even with the use of * netclasses, RFC 1459 is indispensable. *

*/ #import "NetBase.h" #import "NetTCP.h" #import "IRCObject.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #include #include #include #include NSString *IRCException = @"IRCException"; static NSMapTable *command_to_function = 0; static NSMapTable *ctcp_to_function = 0; static NSData *IRC_new_line = nil; @implementation NSString (IRCAddition) - (NSString *)uppercaseIRCString { NSMutableString *aString = [NSString stringWithString: [self uppercaseString]]; NSRange aRange = {0, [aString length]}; [aString replaceOccurrencesOfString: @"{" withString: @"[" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"}" withString: @"]" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"|" withString: @"\\" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"^" withString: @"~" options: 0 range: aRange]; return [aString uppercaseString]; } - (NSString *)uppercaseStrictRFC1459IRCString { NSMutableString *aString = [NSString stringWithString: [self uppercaseString]]; NSRange aRange = {0, [aString length]}; [aString replaceOccurrencesOfString: @"{" withString: @"[" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"}" withString: @"]" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"|" withString: @"\\" options: 0 range: aRange]; return [aString uppercaseString]; } - (NSString *)lowercaseIRCString { NSMutableString *aString = [NSMutableString stringWithString: [self lowercaseString]]; NSRange aRange = {0, [aString length]}; [aString replaceOccurrencesOfString: @"[" withString: @"{" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"]" withString: @"}" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"\\" withString: @"|" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"~" withString: @"^" options: 0 range: aRange]; return [aString lowercaseString]; } - (NSString *)lowercaseStrictRFC1459IRCString { NSMutableString *aString = [NSMutableString stringWithString: [self lowercaseString]]; NSRange aRange = {0, [aString length]}; [aString replaceOccurrencesOfString: @"[" withString: @"{" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"]" withString: @"}" options: 0 range: aRange]; [aString replaceOccurrencesOfString: @"\\" withString: @"|" options: 0 range: aRange]; return [aString lowercaseString]; } @end @interface IRCObject (InternalIRCObject) - setErrorString: (NSString *)anError; @end #define NEXT_SPACE(__y, __z, __string)\ {\ __z = [(__string) rangeOfCharacterFromSet:\ [NSCharacterSet whitespaceCharacterSet] options: 0\ range: NSMakeRange((__y), [(__string) length] - (__y))].location;\ if (__z == NSNotFound) __z = [(__string) length];\ } #define NEXT_NON_SPACE(__y, __z, __string)\ {\ int __len = [(__string) length];\ id set = [NSCharacterSet whitespaceCharacterSet];\ __z = (__y);\ while (__z < __len && \ [set characterIsMember: [(__string) characterAtIndex: __z]]) __z++;\ } static inline NSString *get_IRC_prefix(NSString *line, NSString **prefix) { int beg; int end; int len = [line length]; if (len == 0) { *prefix = nil; return @""; } NEXT_NON_SPACE(0, beg, line); if (beg == len) { *prefix = nil; return @""; } NEXT_SPACE(beg, end, line); if ([line characterAtIndex: beg] != ':') { *prefix = nil; return line; } else { beg++; if (beg == end) { *prefix = @""; if (beg == len) { return @""; } else { return [line substringFromIndex: beg]; } } } *prefix = [line substringWithRange: NSMakeRange(beg, end - beg)]; if (end != len) { return [line substringFromIndex: end]; } return @""; } static inline NSString *get_next_IRC_word(NSString *line, NSString **prefix) { int beg; int end; int len = [line length]; if (len == 0) { *prefix = nil; return @""; } NEXT_NON_SPACE(0, beg, line); if (beg == len) { *prefix = nil; return @""; } if ([line characterAtIndex: beg] == ':') { beg++; if (beg == len) { *prefix = @""; } else { *prefix = [line substringFromIndex: beg]; } return @""; } NEXT_SPACE(beg, end, line); *prefix = [line substringWithRange: NSMakeRange(beg, end - beg)]; if (end != len) { return [line substringFromIndex: end]; } return @""; } #undef NEXT_NON_SPACE #undef NEXT_SPACE static inline BOOL is_numeric_command(NSString *aString) { static NSCharacterSet *set = nil; unichar test[3]; if (!set) { set = RETAIN([NSCharacterSet characterSetWithCharactersInString: @"0123456789"]); } if ([aString length] != 3) { return NO; } [aString getCharacters: test]; if ([set characterIsMember: test[0]] && [set characterIsMember: test[1]] && [set characterIsMember: test[2]]) { return YES; } return NO; } static inline BOOL contains_a_space(NSString *aString) { return ([aString rangeOfCharacterFromSet: [NSCharacterSet whitespaceCharacterSet]].location == NSNotFound) ? NO : YES; } static inline NSString *string_to_string(NSString *aString, NSString *delim) { NSRange a = [aString rangeOfString: delim]; if (a.location == NSNotFound) return [NSString stringWithString: aString]; return [aString substringToIndex: a.location]; } static inline NSString *string_from_string(NSString *aString, NSString *delim) { NSRange a = [aString rangeOfString: delim]; if (a.location == NSNotFound) return nil; a.location += a.length; if (a.location == [aString length]) { return @""; } return [aString substringFromIndex: a.location]; } inline NSString *ExtractIRCNick(NSString *prefix) { if (!prefix) return @""; return string_to_string(prefix, @"!"); } inline NSString *ExtractIRCHost(NSString *prefix) { if (!prefix) return @""; return string_from_string(prefix, @"!"); } inline NSArray *SeparateIRCNickAndHost(NSString *prefix) { if (!prefix) return [NSArray arrayWithObject: @""]; return [NSArray arrayWithObjects: string_to_string(prefix, @"!"), string_from_string(prefix, @"!"), nil]; } static void rec_isupport(IRCObject *client, NSArray *paramList) { NSEnumerator *iter; id object; iter = [paramList objectEnumerator]; while ((object = [iter nextObject])) { object = [object lowercaseString]; if ([object hasPrefix: @"casemapping="]) { object = [object substringFromIndex: 12]; if ([object isEqualToString: @"rfc1459"]) { [client setLowercasingSelector: @selector(lowercaseIRCString)]; } else if ([object isEqualToString: @"strict-rfc1459"]) { [client setLowercasingSelector: @selector(lowercaseStrictRFC1459IRCString)]; } else if ([object isEqualToString: @"ascii"]) { [client setLowercasingSelector: @selector(lowercaseString)]; } else { NSLog(@"Did not understand casemapping=%@", object); } break; } } } static void rec_numeric(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if ([command isEqualToString: RPL_ISUPPORT]) { rec_isupport(client, paramList); } [client numericCommandReceived: command withParams: paramList from: prefix]; } static void rec_caction(IRCObject *client, NSString *prefix, NSString *command, NSString *rest, NSString *to) { if ([rest length] == 0) { return; } [client actionReceived: rest to: to from: prefix]; } static void rec_ccustom(IRCObject *client, NSString *prefix, NSString *command, NSString *rest, NSString *to, NSString *ctcp) { if ([command isEqualToString: @"NOTICE"]) { [client CTCPReplyReceived: ctcp withArgument: rest to: to from: prefix]; } else { [client CTCPRequestReceived: ctcp withArgument: rest to: to from: prefix]; } } static void rec_nick(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if (!prefix) { return; } if ([paramList count] < 1) { return; } if ([client caseInsensitiveCompare: [client nick] to: ExtractIRCNick(prefix)] == NSOrderedSame) { [client setNick: [paramList objectAtIndex: 0]]; } [client nickChangedTo: [paramList objectAtIndex: 0] from: prefix]; } static void rec_join(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if (!prefix) { return; } if ([paramList count] == 0) { return; } [client channelJoined: [paramList objectAtIndex: 0] from: prefix]; } static void rec_part(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { int x; if (!prefix) { return; } x = [paramList count]; if (x == 0) { return; } [client channelParted: [paramList objectAtIndex: 0] withMessage: (x == 2) ? [paramList objectAtIndex: 1] : 0 from: prefix]; } static void rec_quit(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if (!prefix) { return; } if ([paramList count] == 0) { return; } [client quitIRCWithMessage: [paramList objectAtIndex: 0] from: prefix]; } static void rec_topic(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if (!prefix) { return; } if ([paramList count] < 2) { return; } [client topicChangedTo: [paramList objectAtIndex: 1] in: [paramList objectAtIndex: 0] from: prefix]; } static void rec_privmsg(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { NSString *message; if ([paramList count] < 2) { return; } message = [paramList objectAtIndex: 1]; if ([message hasPrefix: @"\001"]) { void (*func)(IRCObject *, NSString *, NSString *, NSString *, NSString *); id ctcp = string_to_string(message, @" "); id rest; if ([ctcp isEqualToString: message]) { if ([ctcp hasSuffix: @"\001"]) { ctcp = [ctcp substringToIndex: [ctcp length] - 1]; } rest = nil; } else { NSRange aRange; aRange.location = [ctcp length] + 1; aRange.length = [message length] - aRange.location; if ([message hasSuffix: @"\001"]) { aRange.length--; } if (aRange.length > 0) { rest = [message substringWithRange: aRange]; } else { rest = nil; } } func = NSMapGet(ctcp_to_function, ctcp); if (func) { func(client, prefix, command, rest, [paramList objectAtIndex: 0]); } else { ctcp = [ctcp substringFromIndex: 1]; rec_ccustom(client, prefix, command, rest, [paramList objectAtIndex: 0], ctcp); } return; } if ([command isEqualToString: @"PRIVMSG"]) { [client messageReceived: message to: [paramList objectAtIndex: 0] from: prefix]; } else { [client noticeReceived: message to: [paramList objectAtIndex: 0] from: prefix]; } } static void rec_mode(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { NSArray *newParams; int x; if (!prefix) { return; } x = [paramList count]; if (x < 2) { return; } if (x == 2) { newParams = AUTORELEASE([NSArray new]); } else { NSRange aRange; aRange.location = 2; aRange.length = x - 2; newParams = [paramList subarrayWithRange: aRange]; } [client modeChanged: [paramList objectAtIndex: 1] on: [paramList objectAtIndex: 0] withParams: newParams from: prefix]; } static void rec_invite(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if (!prefix) { return; } if ([paramList count] < 2) { return; } [client invitedTo: [paramList objectAtIndex: 1] from: prefix]; } static void rec_kick(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { id object; if (!prefix) { return; } if ([paramList count] < 2) { return; } object = ([paramList count] > 2) ? [paramList objectAtIndex: 2] : nil; [client userKicked: [paramList objectAtIndex: 1] outOf: [paramList objectAtIndex: 0] for: object from: prefix]; } static void rec_ping(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { NSString *arg; arg = [paramList componentsJoinedByString: @" "]; [client pingReceivedWithArgument: arg from: prefix]; } static void rec_pong(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { NSString *arg; arg = [paramList componentsJoinedByString: @" "]; [client pongReceivedWithArgument: arg from: prefix]; } static void rec_wallops(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if (!prefix) { return; } if ([paramList count] < 1) { return; } [client wallopsReceived: [paramList objectAtIndex: 0] from: prefix]; } static void rec_error(IRCObject *client, NSString *command, NSString *prefix, NSArray *paramList) { if ([paramList count] < 1) { return; } [client errorReceived: [paramList objectAtIndex: 0]]; } @implementation IRCObject (InternalIRCObject) - setErrorString: (NSString *)anError { RELEASE(errorString); errorString = RETAIN(anError); return self; } @end @implementation IRCObject + (void)initialize { IRC_new_line = [[NSData alloc] initWithBytes: "\r\n" length: 2]; command_to_function = NSCreateMapTable(NSObjectMapKeyCallBacks, NSIntMapValueCallBacks, 13); NSMapInsert(command_to_function, @"NICK", rec_nick); NSMapInsert(command_to_function, @"JOIN", rec_join); NSMapInsert(command_to_function, @"PART", rec_part); NSMapInsert(command_to_function, @"QUIT", rec_quit); NSMapInsert(command_to_function, @"TOPIC", rec_topic); NSMapInsert(command_to_function, @"PRIVMSG", rec_privmsg); NSMapInsert(command_to_function, @"NOTICE", rec_privmsg); NSMapInsert(command_to_function, @"MODE", rec_mode); NSMapInsert(command_to_function, @"KICK", rec_kick); NSMapInsert(command_to_function, @"INVITE", rec_invite); NSMapInsert(command_to_function, @"PING", rec_ping); NSMapInsert(command_to_function, @"PONG", rec_pong); NSMapInsert(command_to_function, @"WALLOPS", rec_wallops); NSMapInsert(command_to_function, @"ERROR", rec_error); ctcp_to_function = NSCreateMapTable(NSObjectMapKeyCallBacks, NSIntMapValueCallBacks, 1); NSMapInsert(ctcp_to_function, @"\001ACTION", rec_caction); } - initWithNickname: (NSString *)aNickname withUserName: (NSString *)aUser withRealName: (NSString *)aRealName withPassword: (NSString *)aPassword { if (!(self = [super init])) return nil; lowercasingSelector = @selector(lowercaseIRCString); defaultEncoding = [NSString defaultCStringEncoding]; if (![self setNick: aNickname]) { [self release]; return nil; } if (![self setUserName: aUser]) { [self release]; return nil; } if (![self setRealName: aRealName]) { [self release]; return nil; } if (![self setPassword: aPassword]) { [self release]; return nil; } targetToEncoding = NSCreateMapTable(NSObjectMapKeyCallBacks, NSIntMapValueCallBacks, 10); if (!targetToEncoding) { [self release]; return nil; } targetToOriginalTarget = [NSMutableDictionary new]; if (!targetToOriginalTarget) { [self release]; return nil; } return self; } - (void)dealloc { NSFreeMapTable(targetToEncoding); DESTROY(targetToOriginalTarget); DESTROY(nick); DESTROY(userName); DESTROY(realName); DESTROY(password); DESTROY(errorString); [super dealloc]; } - (void)connectionLost { connected = NO; [super connectionLost]; } - setLowercasingSelector: (SEL)aSelector { NSEnumerator *iter; NSString *object; NSString *normal; NSStringEncoding aEncoding; NSMutableDictionary *new; if (aSelector == NULL) { aSelector = @selector(lowercaseIRCString); } new = [NSMutableDictionary new]; iter = [targetToOriginalTarget keyEnumerator]; while ((object = [iter nextObject])) { aEncoding = (NSStringEncoding)NSMapGet(targetToEncoding, object); NSMapRemove(targetToEncoding, object); normal = [targetToOriginalTarget objectForKey: object]; object = [normal performSelector: aSelector]; [new setObject: normal forKey: object]; NSMapInsert(targetToEncoding, object, (void *)aEncoding); } RELEASE(targetToOriginalTarget); targetToOriginalTarget = new; lowercasingSelector = aSelector; return self; } - (SEL)lowercasingSelector { return lowercasingSelector; } - (NSComparisonResult)caseInsensitiveCompare: (NSString *)aString1 to: (NSString *)aString2 { return ([(NSString *)[aString1 performSelector: lowercasingSelector] compare: [aString2 performSelector: lowercasingSelector]]); } - setNick: (NSString *)aNickname { if (aNickname == nick) return self; aNickname = string_to_string(aNickname, @" "); if ([aNickname length] == 0) { [self setErrorString: @"No usable nickname provided"]; return nil; } RELEASE(nick); nick = RETAIN(aNickname); return self; } - (NSString *)nick { return nick; } - setUserName: (NSString *)aUser { if ([aUser length] == 0) { aUser = NSUserName(); if ([aUser length] == 0) { aUser = @"netclasses"; } } if ([(aUser = string_to_string(aUser, @" ")) length] == 0) { aUser = @"netclasses"; } RELEASE(userName); userName = RETAIN(aUser); return self; } - (NSString *)userName { return userName; } - setRealName: (NSString *)aRealName { if ([aRealName length] == 0) { aRealName = @"John Doe"; } RELEASE(realName); realName = RETAIN(aRealName); return self; } - (NSString *)realName { return realName; } - setPassword: (NSString *)aPass { if ([aPass length]) { if ([(aPass = string_to_string(aPass, @" ")) length] == 0) { [self setErrorString: @"Unusable password"]; return nil; } } else { aPass = nil; } DESTROY(password); password = RETAIN(aPass); return self; } - (NSString *)password { return password; } - (NSString *)errorString { return errorString; } - connectionEstablished: (id )aTransport { [super connectionEstablished: aTransport]; [self setLowercasingSelector: @selector(lowercaseIRCString)]; if (password) { [self writeString: [NSString stringWithFormat: @"PASS %@", password]]; } [self changeNick: nick]; [self writeString: @"USER %@ %@ %@ :%@", userName, @"localhost", @"netclasses", realName]; return self; } - (BOOL)connected { return connected; } - setEncoding: (NSStringEncoding)aEncoding { defaultEncoding = aEncoding; return self; } - setEncoding: (NSStringEncoding)aEncoding forTarget: (NSString *)aTarget { NSString *lower = [aTarget performSelector: lowercasingSelector]; if (!lower) return self; NSMapInsert(targetToEncoding, lower, (void *)aEncoding); [targetToOriginalTarget setObject: aTarget forKey: lower]; return self; } - (NSStringEncoding)encoding { return defaultEncoding; } - (NSStringEncoding)encodingForTarget: (NSString *)aTarget { NSString *lower = [aTarget performSelector: lowercasingSelector]; if (!lower) return defaultEncoding; return (NSStringEncoding)NSMapGet(targetToEncoding, lower); } - (void)removeEncodingForTarget: (NSString *)aTarget { NSString *lower = [aTarget performSelector: lowercasingSelector]; if (!lower) return; NSMapRemove(targetToEncoding, lower); [targetToOriginalTarget removeObjectForKey: lower]; } - (NSArray *)targetsWithEncodings { return NSAllMapTableKeys(targetToEncoding); } - changeNick: (NSString *)aNick { if ([aNick length] > 0) { if ([(aNick = string_to_string(aNick, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject changeNick: '%@'] Unusable nickname given", aNick]; } if (!connected) { [self setNick: aNick]; } [self writeString: @"NICK %@", aNick]; } return self; } - quitWithMessage: (NSString *)aMessage { if ([aMessage length] > 0) { [self writeString: @"QUIT :%@", aMessage]; } else { [self writeString: @"QUIT"]; } return self; } - partChannel: (NSString *)aChannel withMessage: (NSString *)aMessage { if ([aChannel length] == 0) { return self; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject partChannel: '%@' ...] Unusable channel given", aChannel]; } if ([aMessage length] > 0) { [self writeString: @"PART %@ :%@", aChannel, aMessage]; } else { [self writeString: @"PART %@", aChannel]; } return self; } - joinChannel: (NSString *)aChannel withPassword: (NSString *)aPassword { if ([aChannel length] == 0) { return self; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject joinChannel: '%@' ...] Unusable channel", aChannel]; } if ([aPassword length] == 0) { [self writeString: @"JOIN %@", aChannel]; return self; } if ([(aPassword = string_to_string(aPassword, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject joinChannel: withPassword: '%@'] Unusable password", aPassword]; } [self writeString: @"JOIN %@ %@", aChannel, aPassword]; return self; } - sendCTCPReply: (NSString *)aCTCP withArgument: (NSString *)args to: (NSString *)aPerson { if ([aPerson length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject sendCTCPReply: '%@'withArgument: '%@' to: '%@'] Unusable receiver", aCTCP, args, aPerson]; } if (!aCTCP) { aCTCP = @""; } if ([args length]) { [self writeString: @"NOTICE %@ :\001%@ %@\001", aPerson, aCTCP, args]; } else { [self writeString: @"NOTICE %@ :\001%@\001", aPerson, aCTCP]; } return self; } - sendCTCPRequest: (NSString *)aCTCP withArgument: (NSString *)args to: (NSString *)aPerson { if ([aPerson length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject sendCTCPRequest: '%@'withArgument: '%@' to: '%@'] Unusable receiver", aCTCP, args, aPerson]; } if (!aCTCP) { aCTCP = @""; } if ([args length]) { [self writeString: @"PRIVMSG %@ :\001%@ %@\001", aPerson, aCTCP, args]; } else { [self writeString: @"PRIVMSG %@ :\001%@\001", aPerson, aCTCP]; } return self; } - sendMessage: (NSString *)aMessage to: (NSString *)aReceiver { if ([aMessage length] == 0) { return self; } if ([aReceiver length] == 0) { return self; } if ([(aReceiver = string_to_string(aReceiver, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject sendMessage: '%@' to: '%@'] Unusable receiver", aMessage, aReceiver]; } [self writeString: @"PRIVMSG %@ :%@", aReceiver, aMessage]; return self; } - sendNotice: (NSString *)aNotice to: (NSString *)aReceiver { if ([aNotice length] == 0) { return self; } if ([aReceiver length] == 0) { return self; } if ([(aReceiver = string_to_string(aReceiver, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject sendNotice: '%@' to: '%@'] Unusable receiver", aNotice, aReceiver]; } [self writeString: @"NOTICE %@ :%@", aReceiver, aNotice]; return self; } - sendAction: (NSString *)anAction to: (NSString *)aReceiver { if ([anAction length] == 0) { return self; } if ([aReceiver length] == 0) { return self; } if ([(aReceiver = string_to_string(aReceiver, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject sendAction: '%@' to: '%@'] Unusable receiver", anAction, aReceiver]; } [self writeString: @"PRIVMSG %@ :\001ACTION %@\001", aReceiver, anAction]; return self; } - becomeOperatorWithName: (NSString *)aName withPassword: (NSString *)aPassword { if (([aName length] == 0) || ([aPassword length] == 0)) { return self; } if ([(aPassword = string_to_string(aPassword, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject becomeOperatorWithName: %@ withPassword: %@] Unusable password", aName, aPassword]; } if ([(aName = string_to_string(aName, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject becomeOperatorWithName: %@ withPassword: %@] Unusable name", aName, aPassword]; } [self writeString: @"OPER %@ %@", aName, aPassword]; return self; } - requestNamesOnChannel: (NSString *)aChannel { if ([aChannel length] == 0) { [self writeString: @"NAMES"]; return self; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestNamesOnChannel: %@] Unusable channel", aChannel]; } [self writeString: @"NAMES %@", aChannel]; return self; } - requestMOTDOnServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"MOTD"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestMOTDOnServer:'%@'] Unusable server", aServer]; } [self writeString: @"MOTD %@", aServer]; return self; } - requestSizeInformationFromServer: (NSString *)aServer andForwardTo: (NSString *)anotherServer { if ([aServer length] == 0) { [self writeString: @"LUSERS"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestSizeInformationFromServer: '%@' andForwardTo: '%@'] Unusable first server", aServer, anotherServer]; } if ([anotherServer length] == 0) { [self writeString: @"LUSERS %@", aServer]; return self; } if ([(anotherServer = string_to_string(anotherServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestSizeInformationFromServer: '%@' andForwardTo: '%@'] Unusable second server", aServer, anotherServer]; } [self writeString: @"LUSERS %@ %@", aServer, anotherServer]; return self; } - requestVersionOfServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"VERSION"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestVersionOfServer: '%@'] Unusable server", aServer]; } [self writeString: @"VERSION %@", aServer]; return self; } - requestServerStats: (NSString *)aServer for: (NSString *)query { if ([query length] == 0) { [self writeString: @"STATS"]; return self; } if ([(query = string_to_string(query, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerStats: '%@' for: '%@'] Unusable query", aServer, query]; } if ([aServer length] == 0) { [self writeString: @"STATS %@", query]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerStats: '%@' for: '%@'] Unusable server", aServer, query]; } [self writeString: @"STATS %@ %@", query, aServer]; return self; } - requestServerLink: (NSString *)aLink from: (NSString *)aServer { if ([aLink length] == 0) { [self writeString: @"LINKS"]; return self; } if ([(aLink = string_to_string(aLink, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerLink: '%@' from: '%@'] Unusable link", aLink, aServer]; } if ([aServer length] == 0) { [self writeString: @"LINKS %@", aLink]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerLink: '%@' from: '%@'] Unusable server", aLink, aServer]; } [self writeString: @"LINKS %@ %@", aServer, aLink]; return self; } - requestTimeOnServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"TIME"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestTimeOnServer: '%@'] Unusable server", aServer]; } [self writeString: @"TIME %@", aServer]; return self; } - requestServerToConnect: (NSString *)aServer to: (NSString *)connectServer onPort: (NSString *)aPort { if ([connectServer length] == 0) { return self; } if ([(connectServer = string_to_string(connectServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerToConnect: '%@' to: '%@' onPort: '%@'] Unusable second server", aServer, connectServer, aPort]; } if ([aPort length] == 0) { return self; } if ([(aPort = string_to_string(aPort, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerToConnect: '%@' to: '%@' onPort: '%@'] Unusable port", aServer, connectServer, aPort]; } if ([aServer length] == 0) { [self writeString: @"CONNECT %@ %@", connectServer, aPort]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestServerToConnect: '%@' to: '%@' onPort: '%@'] Unusable first server", aServer, connectServer, aPort]; } [self writeString: @"CONNECT %@ %@ %@", connectServer, aPort, aServer]; return self; } - requestTraceOnServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"TRACE"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestTraceOnServer: '%@'] Unusable server", aServer]; } [self writeString: @"TRACE %@", aServer]; return self; } - requestAdministratorOnServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"ADMIN"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestAdministratorOnServer: '%@'] Unusable server", aServer]; } [self writeString: @"ADMIN %@", aServer]; return self; } - requestInfoOnServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"INFO"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestInfoOnServer: '%@'] Unusable server", aServer]; } [self writeString: @"INFO %@", aServer]; return self; } - requestServerRehash { [self writeString: @"REHASH"]; return self; } - requestServerShutdown { [self writeString: @"DIE"]; return self; } - requestServerRestart { [self writeString: @"RESTART"]; return self; } - requestUserInfoOnServer: (NSString *)aServer { if ([aServer length] == 0) { [self writeString: @"USERS"]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject requestUserInfoOnServer: '%@'] Unusable server", aServer]; } [self writeString: @"USERS %@", aServer]; return self; } - areUsersOn: (NSString *)userList { if ([userList length] == 0) { return self; } [self writeString: @"ISON %@", userList]; return self; } - sendWallops: (NSString *)aMessage { if ([aMessage length] == 0) { return self; } [self writeString: @"WALLOPS :%@", aMessage]; return self; } - listWho: (NSString *)aMask onlyOperators: (BOOL)operators { if ([aMask length] == 0) { [self writeString: @"WHO"]; return self; } if ([(aMask = string_to_string(aMask, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject listWho: '%@' onlyOperators: %d] Unusable mask", aMask, operators]; } if (operators) { [self writeString: @"WHO %@ o", aMask]; } else { [self writeString: @"WHO %@", aMask]; } return self; } - whois: (NSString *)aPerson onServer: (NSString *)aServer { if ([aPerson length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject whois: '%@' onServer: '%@'] Unusable person", aPerson, aServer]; } if ([aServer length] == 0) { [self writeString: @"WHOIS %@", aPerson]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject whois: '%@' onServer: '%@'] Unusable server", aPerson, aServer]; } [self writeString: @"WHOIS %@ %@", aServer, aPerson]; return self; } - whowas: (NSString *)aPerson onServer: (NSString *)aServer withNumberEntries: (NSString *)aNumber { if ([aPerson length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject whowas: '%@' onServer: '%@' withNumberEntries: '%@'] Unusable person", aPerson, aServer, aNumber]; } if ([aNumber length] == 0) { [self writeString: @"WHOWAS %@", aPerson]; return self; } if ([(aNumber = string_to_string(aNumber, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject whowas: '%@' onServer: '%@' withNumberEntries: '%@'] Unusable number of entries", aPerson, aServer, aNumber]; } if ([aServer length] == 0) { [self writeString: @"WHOWAS %@ %@", aPerson, aNumber]; return self; } if ([(aServer = string_to_string(aServer, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject whowas: '%@' onServer: '%@' withNumberEntries: '%@'] Unusable server", aPerson, aServer, aNumber]; } [self writeString: @"WHOWAS %@ %@ %@", aPerson, aNumber, aServer]; return self; } - kill: (NSString *)aPerson withComment: (NSString *)aComment { if ([aPerson length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject kill: '%@' withComment: '%@'] Unusable person", aPerson, aComment]; } if ([aComment length] == 0) { return self; } [self writeString: @"KILL %@ :%@", aPerson, aComment]; return self; } - setTopicForChannel: (NSString *)aChannel to: (NSString *)aTopic { if ([aChannel length] == 0) { return self; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject setTopicForChannel: %@ to: %@] Unusable channel", aChannel, aTopic]; } if ([aTopic length] == 0) { [self writeString: @"TOPIC %@", aChannel]; } else { [self writeString: @"TOPIC %@ :%@", aChannel, aTopic]; } return self; } - setMode: (NSString *)aMode on: (NSString *)anObject withParams: (NSArray *)aList { NSMutableString *aString; NSEnumerator *iter; id object; if ([anObject length] == 0) { return self; } if ([(anObject = string_to_string(anObject, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject setMode:'%@' on:'%@' withParams:'%@'] Unusable object", aMode, anObject, aList]; } if ([aMode length] == 0) { [self writeString: @"MODE %@", anObject]; return self; } if ([(aMode = string_to_string(aMode, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject setMode:'%@' on:'%@' withParams:'%@'] Unusable mode", aMode, anObject, aList]; } if (!aList) { [self writeString: @"MODE %@ %@", anObject, aMode]; return self; } aString = [NSMutableString stringWithFormat: @"MODE %@ %@", anObject, aMode]; iter = [aList objectEnumerator]; while ((object = [iter nextObject])) { [aString appendString: @" "]; [aString appendString: object]; } [self writeString: @"%@", aString]; return self; } - listChannel: (NSString *)aChannel onServer: (NSString *)aServer { if ([aChannel length] == 0) { [self writeString: @"LIST"]; return self; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject listChannel:'%@' onServer:'%@'] Unusable channel", aChannel, aServer]; } if ([aServer length] == 0) { [self writeString: @"LIST %@", aChannel]; return self; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject listChannel:'%@' onServer:'%@'] Unusable server", aChannel, aServer]; } [self writeString: @"LIST %@ %@", aChannel, aServer]; return self; } - invite: (NSString *)aPerson to: (NSString *)aChannel { if ([aPerson length] == 0) { return self; } if ([aChannel length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject invite:'%@' to:'%@'] Unusable person", aPerson, aChannel]; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject invite:'%@' to:'%@'] Unusable channel", aPerson, aChannel]; } [self writeString: @"INVITE %@ %@", aPerson, aChannel]; return self; } - kick: (NSString *)aPerson offOf: (NSString *)aChannel for: (NSString *)aReason { if ([aPerson length] == 0) { return self; } if ([aChannel length] == 0) { return self; } if ([(aPerson = string_to_string(aPerson, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject kick:'%@' offOf:'%@' for:'%@'] Unusable person", aPerson, aChannel, aReason]; } if ([(aChannel = string_to_string(aChannel, @" ")) length] == 0) { [NSException raise: IRCException format: @"[IRCObject kick:'%@' offOf:'%@' for:'%@'] Unusable channel", aPerson, aChannel, aReason]; } if ([aReason length] == 0) { [self writeString: @"KICK %@ %@", aChannel, aPerson]; return self; } [self writeString: @"KICK %@ %@ :%@", aChannel, aPerson, aReason]; return self; } - setAwayWithMessage: (NSString *)aMessage { if ([aMessage length] == 0) { [self writeString: @"AWAY"]; return self; } [self writeString: @"AWAY :%@", aMessage]; return self; } - sendPingWithArgument: (NSString *)aString { if (!aString) { aString = @""; } [self writeString: @"PING :%@", aString]; return self; } - sendPongWithArgument: (NSString *)aString { if (!aString) { aString = @""; } [self writeString: @"PONG :%@", aString]; return self; } @end @implementation IRCObject (Callbacks) - registeredWithServer { return self; } - couldNotRegister: (NSString *)aReason { return self; } - CTCPRequestReceived: (NSString *)aCTCP withArgument: (NSString *)anArgument to: (NSString *)aReceiver from: (NSString *)aPerson { return self; } - CTCPReplyReceived: (NSString *)aCTCP withArgument: (NSString *)anArgument to: (NSString *)aReceiver from: (NSString *)aPerson { return self; } - errorReceived: (NSString *)anError { return self; } - wallopsReceived: (NSString *)aMessage from: (NSString *)aSender { return self; } - userKicked: (NSString *)aPerson outOf: (NSString *)aChannel for: (NSString *)aReason from: (NSString *)aKicker { return self; } - invitedTo: (NSString *)aChannel from: (NSString *)anInviter { return self; } - modeChanged: (NSString *)aMode on: (NSString *)anObject withParams: (NSArray *)paramList from: (NSString *)aPerson { return self; } - numericCommandReceived: (NSString *)aCommand withParams: (NSArray *)paramList from: (NSString *)aSender { return self; } - nickChangedTo: (NSString *)newName from: (NSString *)aPerson { return self; } - channelJoined: (NSString *)aChannel from: (NSString *)aJoiner { return self; } - channelParted: (NSString *)aChannel withMessage: (NSString *)aMessage from: (NSString *)aParter { return self; } - quitIRCWithMessage: (NSString *)aMessage from: (NSString *)aQuitter { return self; } - topicChangedTo: (NSString *)aTopic in: (NSString *)aChannel from: (NSString *)aPerson { return self; } - messageReceived: (NSString *)aMessage to: (NSString *)aReceiver from: (NSString *)aSender { return self; } - noticeReceived: (NSString *)aNotice to: (NSString *)aReceiver from: (NSString *)aSender { return self; } - actionReceived: (NSString *)anAction to: (NSString *)aReceiver from: (NSString *)aSender { return self; } - pingReceivedWithArgument: (NSString *)anArgument from: (NSString *)aSender { return self; } - pongReceivedWithArgument: (NSString *)anArgument from: (NSString *)aSender { return self; } - newNickNeededWhileRegistering { [self changeNick: [NSString stringWithFormat: @"%@_", nick]]; return self; } @end @implementation IRCObject (LowLevel) - lineReceived: (NSData *)aLine { NSString *prefix = nil; NSString *command = nil; NSMutableArray *paramList = nil; id object; void (*function)(IRCObject *, NSString *, NSString *, NSArray *); NSString *line, *orig; orig = line = AUTORELEASE([[NSString alloc] initWithData: aLine encoding: defaultEncoding]); if ([line length] == 0) { return self; } paramList = AUTORELEASE([NSMutableArray new]); line = get_IRC_prefix(line, &prefix); if ([line length] == 0) { [NSException raise: IRCException format: @"[IRCObject lineReceived: '@'] Line ended prematurely.", orig]; } line = get_next_IRC_word(line, &command); if (command == nil) { [NSException raise: IRCException format: @"[IRCObject lineReceived: '@'] Line ended prematurely.", orig]; } while (1) { line = get_next_IRC_word(line, &object); if (!object) { break; } [paramList addObject: object]; } if (is_numeric_command(command)) { if ([paramList count] >= 2) { NSRange aRange; [self setNick: [paramList objectAtIndex: 0]]; aRange.location = 1; aRange.length = [paramList count] - 1; rec_numeric(self, command, prefix, [paramList subarrayWithRange: aRange]); } } else { function = NSMapGet(command_to_function, command); if (function != 0) { function(self, command, prefix, paramList); } else { NSLog(@"Could not handle :%@ %@ %@", prefix, command, paramList); } } if (!connected) { if ([command isEqualToString: ERR_NEEDMOREPARAMS] || [command isEqualToString: ERR_ALREADYREGISTRED] || [command isEqualToString: ERR_NONICKNAMEGIVEN]) { [[NetApplication sharedInstance] disconnectObject: self]; [self couldNotRegister: [NSString stringWithFormat: @"%@ %@ %@", prefix, command, paramList]]; return nil; } else if ([command isEqualToString: ERR_NICKNAMEINUSE] || [command isEqualToString: ERR_NICKCOLLISION] || [command isEqualToString: ERR_ERRONEUSNICKNAME]) { [self newNickNeededWhileRegistering]; } else if ([command isEqualToString: RPL_WELCOME]) { connected = YES; [self registeredWithServer]; } } return self; } - writeString: (NSString *)format, ... { NSString *temp; va_list ap; va_start(ap, format); temp = AUTORELEASE([[NSString alloc] initWithFormat: format arguments: ap]); [(id )transport writeData: [temp dataUsingEncoding: defaultEncoding]]; if (![temp hasSuffix: @"\r\n"]) { [(id )transport writeData: IRC_new_line]; } return self; } @end NSString *RPL_WELCOME = @"001"; NSString *RPL_YOURHOST = @"002"; NSString *RPL_CREATED = @"003"; NSString *RPL_MYINFO = @"004"; NSString *RPL_BOUNCE = @"005"; NSString *RPL_ISUPPORT = @"005"; NSString *RPL_USERHOST = @"302"; NSString *RPL_ISON = @"303"; NSString *RPL_AWAY = @"301"; NSString *RPL_UNAWAY = @"305"; NSString *RPL_NOWAWAY = @"306"; NSString *RPL_WHOISUSER = @"311"; NSString *RPL_WHOISSERVER = @"312"; NSString *RPL_WHOISOPERATOR = @"313"; NSString *RPL_WHOISIDLE = @"317"; NSString *RPL_ENDOFWHOIS = @"318"; NSString *RPL_WHOISCHANNELS = @"319"; NSString *RPL_WHOWASUSER = @"314"; NSString *RPL_ENDOFWHOWAS = @"369"; NSString *RPL_LISTSTART = @"321"; NSString *RPL_LIST = @"322"; NSString *RPL_LISTEND = @"323"; NSString *RPL_UNIQOPIS = @"325"; NSString *RPL_CHANNELMODEIS = @"324"; NSString *RPL_NOTOPIC = @"331"; NSString *RPL_TOPIC = @"332"; NSString *RPL_INVITING = @"341"; NSString *RPL_SUMMONING = @"342"; NSString *RPL_INVITELIST = @"346"; NSString *RPL_ENDOFINVITELIST = @"347"; NSString *RPL_EXCEPTLIST = @"348"; NSString *RPL_ENDOFEXCEPTLIST = @"349"; NSString *RPL_VERSION = @"351"; NSString *RPL_WHOREPLY = @"352"; NSString *RPL_ENDOFWHO = @"315"; NSString *RPL_NAMREPLY = @"353"; NSString *RPL_ENDOFNAMES = @"366"; NSString *RPL_LINKS = @"364"; NSString *RPL_ENDOFLINKS = @"365"; NSString *RPL_BANLIST = @"367"; NSString *RPL_ENDOFBANLIST = @"368"; NSString *RPL_INFO = @"371"; NSString *RPL_ENDOFINFO = @"374"; NSString *RPL_MOTDSTART = @"375"; NSString *RPL_MOTD = @"372"; NSString *RPL_ENDOFMOTD = @"376"; NSString *RPL_YOUREOPER = @"381"; NSString *RPL_REHASHING = @"382"; NSString *RPL_YOURESERVICE = @"383"; NSString *RPL_TIME = @"391"; NSString *RPL_USERSSTART = @"392"; NSString *RPL_USERS = @"393"; NSString *RPL_ENDOFUSERS = @"394"; NSString *RPL_NOUSERS = @"395"; NSString *RPL_TRACELINK = @"200"; NSString *RPL_TRACECONNECTING = @"201"; NSString *RPL_TRACEHANDSHAKE = @"202"; NSString *RPL_TRACEUNKNOWN = @"203"; NSString *RPL_TRACEOPERATOR = @"204"; NSString *RPL_TRACEUSER = @"205"; NSString *RPL_TRACESERVER = @"206"; NSString *RPL_TRACESERVICE = @"207"; NSString *RPL_TRACENEWTYPE = @"208"; NSString *RPL_TRACECLASS = @"209"; NSString *RPL_TRACERECONNECT = @"210"; NSString *RPL_TRACELOG = @"261"; NSString *RPL_TRACEEND = @"262"; NSString *RPL_STATSLINKINFO = @"211"; NSString *RPL_STATSCOMMANDS = @"212"; NSString *RPL_ENDOFSTATS = @"219"; NSString *RPL_STATSUPTIME = @"242"; NSString *RPL_STATSOLINE = @"243"; NSString *RPL_UMODEIS = @"221"; NSString *RPL_SERVLIST = @"234"; NSString *RPL_SERVLISTEND = @"235"; NSString *RPL_LUSERCLIENT = @"251"; NSString *RPL_LUSEROP = @"252"; NSString *RPL_LUSERUNKNOWN = @"253"; NSString *RPL_LUSERCHANNELS = @"254"; NSString *RPL_LUSERME = @"255"; NSString *RPL_ADMINME = @"256"; NSString *RPL_ADMINLOC1 = @"257"; NSString *RPL_ADMINLOC2 = @"258"; NSString *RPL_ADMINEMAIL = @"259"; NSString *RPL_TRYAGAIN = @"263"; NSString *ERR_NOSUCHNICK = @"401"; NSString *ERR_NOSUCHSERVER = @"402"; NSString *ERR_NOSUCHCHANNEL = @"403"; NSString *ERR_CANNOTSENDTOCHAN = @"404"; NSString *ERR_TOOMANYCHANNELS = @"405"; NSString *ERR_WASNOSUCHNICK = @"406"; NSString *ERR_TOOMANYTARGETS = @"407"; NSString *ERR_NOSUCHSERVICE = @"408"; NSString *ERR_NOORIGIN = @"409"; NSString *ERR_NORECIPIENT = @"411"; NSString *ERR_NOTEXTTOSEND = @"412"; NSString *ERR_NOTOPLEVEL = @"413"; NSString *ERR_WILDTOPLEVEL = @"414"; NSString *ERR_BADMASK = @"415"; NSString *ERR_UNKNOWNCOMMAND = @"421"; NSString *ERR_NOMOTD = @"422"; NSString *ERR_NOADMININFO = @"423"; NSString *ERR_FILEERROR = @"424"; NSString *ERR_NONICKNAMEGIVEN = @"431"; NSString *ERR_ERRONEUSNICKNAME = @"432"; NSString *ERR_NICKNAMEINUSE = @"433"; NSString *ERR_NICKCOLLISION = @"436"; NSString *ERR_UNAVAILRESOURCE = @"437"; NSString *ERR_USERNOTINCHANNEL = @"441"; NSString *ERR_NOTONCHANNEL = @"442"; NSString *ERR_USERONCHANNEL = @"443"; NSString *ERR_NOLOGIN = @"444"; NSString *ERR_SUMMONDISABLED = @"445"; NSString *ERR_USERSDISABLED = @"446"; NSString *ERR_NOTREGISTERED = @"451"; NSString *ERR_NEEDMOREPARAMS = @"461"; NSString *ERR_ALREADYREGISTRED = @"462"; NSString *ERR_NOPERMFORHOST = @"463"; NSString *ERR_PASSWDMISMATCH = @"464"; NSString *ERR_YOUREBANNEDCREEP = @"465"; NSString *ERR_YOUWILLBEBANNED = @"466"; NSString *ERR_KEYSET = @"467"; NSString *ERR_CHANNELISFULL = @"471"; NSString *ERR_UNKNOWNMODE = @"472"; NSString *ERR_INVITEONLYCHAN = @"473"; NSString *ERR_BANNEDFROMCHAN = @"474"; NSString *ERR_BADCHANNELKEY = @"475"; NSString *ERR_BADCHANMASK = @"476"; NSString *ERR_NOCHANMODES = @"477"; NSString *ERR_BANLISTFULL = @"478"; NSString *ERR_NOPRIVILEGES = @"481"; NSString *ERR_CHANOPRIVSNEEDED = @"482"; NSString *ERR_CANTKILLSERVER = @"483"; NSString *ERR_RESTRICTED = @"484"; NSString *ERR_UNIQOPPRIVSNEEDED = @"485"; NSString *ERR_NOOPERHOST = @"491"; NSString *ERR_UMODEUNKNOWNFLAG = @"501"; NSString *ERR_USERSDONTMATCH = @"502"; NSString *RPL_SERVICEINFO = @"231"; NSString *RPL_ENDOFSERVICES = @"232"; NSString *RPL_SERVICE = @"233"; NSString *RPL_NONE = @"300"; NSString *RPL_WHOISCHANOP = @"316"; NSString *RPL_KILLDONE = @"361"; NSString *RPL_CLOSING = @"262"; NSString *RPL_CLOSEEND = @"363"; NSString *RPL_INFOSTART = @"373"; NSString *RPL_MYPORTIS = @"384"; NSString *RPL_STATSCLINE = @"213"; NSString *RPL_STATSNLINE = @"214"; NSString *RPL_STATSILINE = @"215"; NSString *RPL_STATSKLINE = @"216"; NSString *RPL_STATSQLINE = @"217"; NSString *RPL_STATSYLINE = @"218"; NSString *RPL_STATSVLINE = @"240"; NSString *RPL_STATSLLINE = @"241"; NSString *RPL_STATSHLINE = @"244"; NSString *RPL_STATSSLINE = @"245"; NSString *RPL_STATSPING = @"246"; NSString *RPL_STATSBLINE = @"247"; NSString *RPL_STATSDLINE = @"250"; NSString *ERR_NOSERVICEHOST = @"492";