From e15a978647af20acc4ccf0cb665f2ac05438290c Mon Sep 17 00:00:00 2001 From: achmizs <1874748+achmizs@users.noreply.github.com> Date: Sat, 4 Dec 2021 22:21:14 -0500 Subject: [PATCH] Large refactoring --- IRCClient/IRCClientSession.h | 178 ++-- IRCClient/IRCClientSession.m | 1740 +++++++++++++++++----------------- 2 files changed, 953 insertions(+), 965 deletions(-) diff --git a/IRCClient/IRCClientSession.h b/IRCClient/IRCClientSession.h index c719fdc..0cb6f73 100644 --- a/IRCClient/IRCClientSession.h +++ b/IRCClient/IRCClientSession.h @@ -25,15 +25,9 @@ * session.delegate = controller; * controller.session = session; * - * NSData *server = [NSData dataWithBytes:@"chat.freenode.net".UTF8String length:strlen(@"chat.freenode.net".UTF8String.length) + 1]; - * NSUinteger port = 6665; - * NSData *nickname = [NSData dataWithBytes:@"test".UTF8String length:strlen(@"test".UTF8String) + 1]; - * NSData *username = [NSData dataWithBytes:@"test".UTF8String length:strlen(@"test".UTF8String) + 1]; - * NSData *realname = [NSData dataWithBytes:@"test".UTF8String length:strlen(@"test".UTF8String) + 1]; - * - * [session setServer:server]; - * [session setPort:port]; - * [session setNickname:nickname username:username realname:realname]; + * session.server = @"chat.freenode.net".dataAsUTF8]; + * session.port = 6665; + * [session setNickname:@"test".dataAsUTF8 username:@"test".dataAsUTF8 realname:@"test".dataAsUTF8]; * [session connect]; * * [session run]; //starts the thread @@ -97,28 +91,28 @@ There is usually no reason to set this, as IRCClient correctly sets its own version string automatically, but this can be any string you like. */ -@property (copy) NSData *version; +@property (copy, nonatomic) NSData *version; /** IRC server to connect to */ -@property (copy) NSData *server; +@property (copy, nonatomic) NSData *server; /** IRC port to connect to */ @property (assign) NSUInteger port; /** Server password to provide on connect (may be left empty or nil) */ -@property (copy) NSData *password; +@property (copy, nonatomic) NSData *password; /** Nickname of the connected client. */ -@property (readonly) NSData *nickname; +@property (nonatomic, readonly) NSData *nickname; /** Username of the connected client. Also known as the ident. */ -@property (readonly) NSData *username; +@property (nonatomic, readonly) NSData *username; /** Realname of the connected client. */ -@property (readonly) NSData *realname; +@property (nonatomic, readonly) NSData *realname; /** The suggested text encoding for messages on this server. @@ -134,33 +128,59 @@ Keys are channel names (NSData objects containing C strings), values are IRCClientChannel objects. */ -@property (readonly) NSDictionary *channels; +@property (nonatomic, readonly) NSDictionary *channels; /** Returns YES if the server is currently connected successfully, and NO if it is not. */ @property (readonly, getter=isConnected) bool connected; /** Stores arbitrary user info. */ -@property (strong) NSDictionary *userInfo; +@property (nonatomic, readonly) NSMutableDictionary *userInfo; + +/** Returns a dictionary of IRC numeric codes. + + The dictionary contains entries for all known IRC numeric codes (as keys). + (The list is taken from https://www.alien.net.au/irc/irc2numerics.html .) + + The value for each key is an NSArray with the known numeric reply names for + which the numeric code is used. + + Note that there is no guarantee whatsoever that any given numeric reply + name will, in fact, describe the contents of the message; most IRC numeric + messages have implementation-specific uses. See the various RFCs, and other + info sources, for details. + */ +@property (class, nonatomic, readonly) NSDictionary *ircNumericCodes; + +/********************************************/ +#pragma mark - Initializers & factory methods +/********************************************/ + ++(instancetype) session; /***************************/ #pragma mark - Class methods /***************************/ -/** Returns a dictionary of IRC numeric codes. - - The dictionary contains entries for all known IRC numeric codes (as keys). - (The list is taken from https://www.alien.net.au/irc/irc2numerics.html .) - - The value for each key is an NSArray with the known numeric reply names for - which the numeric code is used. - - Note that there is no gaurantee whatsoever that any given numeric reply - name will, in fact, describe the contents of the message; most IRC numeric - messages have implementation-specific uses. See the various RFCs, and other - info sources, for details. +/** Returns the nick part of a nick!user@host string. */ -+ (NSDictionary *)ircNumericCodes; ++(NSData *) nickFromNickUserHost:(NSData *)nickUserHost; + +/** Returns the user part of a nick!user@host string. + May be blank if the user component can’t be found + (i.e. if the passed string is not, in fact, in nick!user@host format). + */ ++(NSData *) userFromNickUserHost:(NSData *)nickUserHost; + +/** Returns the host part of a nick!user@host string. + May be blank if the host component can’t be found + (i.e. if the passed string is not, in fact, in nick!user@host format). + */ ++(NSData *) hostFromNickUserHost:(NSData *)nickUserHost; + +/******************************/ +#pragma mark - Instance methods +/******************************/ /** Set the nickname, username, and realname for the session. @@ -169,7 +189,9 @@ already connected; use the nick: method to attempt a nick change while connected.) */ -- (int)setNickname:(NSData *)nickname username:(NSData *)username realname:(NSData *)realname; +-(int) setNickname:(NSData *)nickname + username:(NSData *)username + realname:(NSData *)realname; /** Connect to the IRC server. @@ -178,38 +200,39 @@ Look at the libircclient documentation for the different return codes. */ -- (int)connect; +-(int) connect; /** Disconnect from the IRC server. This always works, as it simply shuts down the socket. If you want to disconnect in a friendly way, you should use the quit: message. */ -- (void)disconnect; +-(void) disconnect; /** Starts a new thread and starts the libircclient runloop, processing events and firing messages back to the main runloop as required. Calling this again will do nothing other than raise a warning in your logs. */ -- (void)run; +-(void) run; /**************************/ #pragma mark - IRC commands /**************************/ /** Sends a raw message to the IRC server. Please consult rfc1459 for the format - of IRC commands. */ -- (int)sendRaw:(NSData *)message; + of IRC commands. + */ +-(int) sendRaw:(NSData *)message; -/** quits the IRC server with the given reason. +/** Quits the IRC server with the given reason. On success, a userQuit:withReason: event will be sent to the IRCClientSessionDelegate with the nickname of the IRC client and the reason provided by the user (or nil if no reason was provided). */ -- (int)quit:(NSData *)reason; +-(int) quit:(NSData *)reason; -/** Joins a channel with a given name and key +/** Joins a channel with a given name and key. On success, a userJoined:channel: event will be sent to the IRCClientSessionDelegate with the nickname of the IRC client and the name of @@ -218,22 +241,30 @@ @param channel the channel to join @param key they key for the channel (may be nil) */ -- (int)join:(NSData *)channel key:(NSData *)key; +-(int) join:(NSData *)channel + key:(NSData *)key; -/** lists channels on the IRC server. +/** Lists users in an IRC channel (or channels). + + @param channel a channel name or string to pass to the NAMES command. + Implementation specific. + */ +-(int) names:(NSData *)channel; + +/** Lists channels on the IRC server. @param channel a channel name or string to pass to the LIST command. Implementation specific. */ -- (int)list:(NSData *)channel; +-(int) list:(NSData *)channel; -/** sets the user mode for the IRC client +/** Sets the user mode for the IRC client. @param mode string to set */ -- (int)userMode:(NSData *)mode; +-(int) userMode:(NSData *)mode; -/** sets the IRC client nickname. +/** Sets the IRC client nickname. On success, a nickChangedFrom:to: event will be sent to the IRCClientSessionDelegate with our old nick and the new nick that we now @@ -241,67 +272,58 @@ @param newnick new nickname to set. */ -- (int)nick:(NSData *)newnick; +-(int) nick:(NSData *)newnick; -/** sends a WHOIS request to the IRC server +/** Sends a WHO query to the IRC server. + + @param nickmask nickname mask of the irc client to who. + */ +-(int) who:(NSData *)nickmask; + +/** Sends a WHOIS query to the IRC server. @param nick nickname of the irc client to whois. */ -- (int)whois:(NSData *)nick; +-(int) whois:(NSData *)nick; -/** send a PRIVMSG to another IRC client +/** Send a PRIVMSG to another IRC client. @param message message to send @param target the other IRC client to send the message to. */ -- (int)message:(NSData *)message to:(NSData *)target; +-(int) message:(NSData *)message + to:(NSData *)target; -/** send a CTCP ACTION to another IRC client +/** Sends a CTCP ACTION to another IRC client. @param action the action message to send @param target the nickname of the irc client to send the message to. */ -- (int)action:(NSData *)action to:(NSData *)target; +-(int) action:(NSData *)action + to:(NSData *)target; -/** send a NOTICE to another IRC client +/** Send a NOTICE to another IRC client. @param notice the message text to send @param target the nickname of the irc client to send the notice to. */ -- (int)notice:(NSData *)notice to:(NSData *)target; +-(int) notice:(NSData *)notice + to:(NSData *)target; -/** send a CTCP request to another IRC client +/** Send a CTCP request to another IRC client. @param request the CTCP request string to send @param target the nickname of the IRC client to send the request to. */ -- (int)ctcpRequest:(NSData *)request target:(NSData *)target; +-(int) ctcpRequest:(NSData *)request + target:(NSData *)target; -/** send a CTCP reply to another IRC client +/** Send a CTCP reply to another IRC client. @param reply the CTCP reply string to send @param target the nickname of the IRC client to send the reply to. */ -- (int)ctcpReply:(NSData *)reply target:(NSData *)target; +-(int) ctcpReply:(NSData *)reply + target:(NSData *)target; @end - -/*************************************/ -#pragma mark - Useful helper functions -/*************************************/ - -/** Returns the nick part of a nick!user@host string. - */ -NSData* getNickFromNickUserHost(NSData *nickUserHost); - -/** Returns the user part of a nick!user@host string. - May be blank if the user component can't be found - (i.e. if the passed string is not, in fact, in nick!user@host format). - */ -NSData* getUserFromNickUserHost(NSData *nickUserHost); - -/** Returns the host part of a nick!user@host string. - May be blank if the host component can't be found - (i.e. if the passed string is not, in fact, in nick!user@host format). - */ -NSData* getHostFromNickUserHost(NSData *nickUserHost); diff --git a/IRCClient/IRCClientSession.m b/IRCClient/IRCClientSession.m index 03c4790..253450d 100644 --- a/IRCClient/IRCClientSession.m +++ b/IRCClient/IRCClientSession.m @@ -21,219 +21,229 @@ #pragma mark Defines and includes /********************************/ -#define IRCCLIENTVERSION "2.0a3" +#define IRCCLIENTVERSION "2.0a5" #import "IRCClientSession.h" #import "IRCClientChannel.h" #import "IRCClientChannel_Private.h" + +#import "NSArray+SA_NSArrayExtensions.h" #import "NSData+SA_NSDataExtensions.h" +#import "NSString+SA_NSStringExtensions.h" +#import "NSRange-Conventional.h" /********************************************/ #pragma mark - Callback function declarations /********************************************/ -static void onConnect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onNick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onQuit(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onJoinChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onPartChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onUserMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onTopic(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onKick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onChannelPrvmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onPrivmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onServerMsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onChannelNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onServerNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onInvite(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onCtcpRequest(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onCtcpReply(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onCtcpAction(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onUnknownEvent(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); -static void onNumericEvent(irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count); +static void onEvent (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); +static void onNumericEvent (irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count); +static void onDCCChatRequest(irc_session_t *session, const char *nick, const char *addr, irc_dcc_t dccid); +static void onDCCSendRequest(irc_session_t *session, const char *nick, const char *addr, const char *filename, size_t size, irc_dcc_t dccid); static NSDictionary* ircNumericCodeList; -/**********************************************/ -#pragma mark - IRCClientSession class extension -/**********************************************/ - -@interface IRCClientSession() -{ - irc_callbacks_t _callbacks; - irc_session_t *_irc_session; - NSThread *_thread; - - NSMutableDictionary *_channels; -} - -@property (readwrite) NSMutableDictionary *channels; - -+(void)loadNumericCodes; - -@end - /***************************************************/ #pragma mark - IRCClientSession class implementation /***************************************************/ -@implementation IRCClientSession +@implementation IRCClientSession { + irc_callbacks_t _callbacks; + irc_session_t *_irc_session; + + NSMutableDictionary *_channels; +} /******************************/ #pragma mark - Custom accessors /******************************/ -- (NSDictionary*)channels -{ - NSDictionary* channelsCopy = [_channels copy]; - return channelsCopy; +-(NSDictionary *) channels { + return [_channels copy]; } -- (void)setChannels:(NSDictionary *)channels -{ - _channels = [channels mutableCopy]; -} - -- (bool)isConnected -{ +-(bool) isConnected { return irc_is_connected(_irc_session); } ++(NSDictionary *) ircNumericCodes { + if (ircNumericCodeList == nil) + [IRCClientSession loadNumericCodes]; + + return ircNumericCodeList; +} + +/********************************************/ +#pragma mark - Initializers & factory methods +/********************************************/ + ++(instancetype) session { + return [self new]; +} + +/*************************************/ +#pragma mark - Useful helper functions +/*************************************/ + ++(NSData *) nickFromNickUserHost:(NSData *)nickUserHost { + if (nickUserHost == nil) return nil; + + NSRange rangeOfNickUserSeparator = [nickUserHost rangeOfData:[NSData dataFromCString:"!"] + options:(NSDataSearchOptions) 0 + range:NSRangeMake(0, nickUserHost.length)]; + + return ((rangeOfNickUserSeparator.location == NSNotFound) ? + nickUserHost : + [nickUserHost subdataWithRange:NSRangeMake(0, + rangeOfNickUserSeparator.location)]); +} + ++(NSData *) userFromNickUserHost:(NSData *)nickUserHost { + if (nickUserHost == nil) return nil; + + NSRange rangeOfNickUserSeparator = [nickUserHost rangeOfData:[NSData dataFromCString:"!"] + options:(NSDataSearchOptions) 0 + range:NSRangeMake(0, nickUserHost.length)]; + + NSRange rangeOfUserHostSeparator = [nickUserHost rangeOfData:[NSData dataFromCString:"@"] + options:(NSDataSearchOptions) 0 + range:NSRangeMake(0, nickUserHost.length)]; + + return ((rangeOfNickUserSeparator.location == NSNotFound || rangeOfUserHostSeparator.location == NSNotFound) ? + [NSData data] : + [nickUserHost subdataWithRange:NSRangeMake(rangeOfNickUserSeparator.location + 1, + rangeOfUserHostSeparator.location - (rangeOfNickUserSeparator.location + 1))]); +} + ++(NSData *) hostFromNickUserHost:(NSData *)nickUserHost { + if (nickUserHost == nil) return nil; + + NSRange rangeOfUserHostSeparator = [nickUserHost rangeOfData:[NSData dataFromCString:"@"] + options:(NSDataSearchOptions) 0 + range:NSRangeMake(0, nickUserHost.length)]; + + return ((rangeOfUserHostSeparator.location == NSNotFound) ? + [NSData data] : + [nickUserHost subdataWithRange:NSRangeMake(rangeOfUserHostSeparator.location + 1, + nickUserHost.length - (rangeOfUserHostSeparator.location + 1))]); +} + /***************************/ #pragma mark - Class methods /***************************/ -- (instancetype)init -{ - if ((self = [super init])) - { - _callbacks.event_connect = onConnect; - _callbacks.event_nick = onNick; - _callbacks.event_quit = onQuit; - _callbacks.event_join = onJoinChannel; - _callbacks.event_part = onPartChannel; - _callbacks.event_mode = onMode; - _callbacks.event_umode = onUserMode; - _callbacks.event_topic = onTopic; - _callbacks.event_kick = onKick; - _callbacks.event_channel = onChannelPrvmsg; - _callbacks.event_privmsg = onPrivmsg; - _callbacks.event_server_msg = onServerMsg; - _callbacks.event_notice = onNotice; - _callbacks.event_channel_notice = onChannelNotice; - _callbacks.event_server_notice = onServerNotice; - _callbacks.event_invite = onInvite; - _callbacks.event_ctcp_req = onCtcpRequest; - _callbacks.event_ctcp_rep = onCtcpReply; - _callbacks.event_ctcp_action = onCtcpAction; - _callbacks.event_unknown = onUnknownEvent; - _callbacks.event_numeric = onNumericEvent; - _callbacks.event_dcc_chat_req = NULL; - _callbacks.event_dcc_send_req = NULL; - - _irc_session = irc_create_session(&_callbacks); - - if (!_irc_session) { - NSLog(@"Could not create irc_session."); - return nil; - } - - // Strip server info from nicks. -// irc_option_set(_irc_session, LIBIRC_OPTION_STRIPNICKS); - - // Set debug mode. -// irc_option_set(_irc_session, LIBIRC_OPTION_DEBUG); - - irc_set_ctx(_irc_session, (__bridge void *)(self)); - - unsigned int high, low; - irc_get_version (&high, &low); - - NSString* versionString = [NSString stringWithFormat:@"IRCClient Framework v%s (Said Achmiz) - libirc v%d.%d (Georgy Yunaev)", IRCCLIENTVERSION, high, low]; - _version = [NSData dataWithBytes:versionString.UTF8String length:versionString.length]; - - _channels = [[NSMutableDictionary alloc] init]; - _encoding = NSUTF8StringEncoding; - } - return self; +-(instancetype) init { + if (!(self = [super init])) + return nil; + + _callbacks.event_connect = onEvent; + _callbacks.event_ping = onEvent; + _callbacks.event_nick = onEvent; + _callbacks.event_quit = onEvent; + _callbacks.event_join = onEvent; + _callbacks.event_part = onEvent; + _callbacks.event_mode = onEvent; + _callbacks.event_umode = onEvent; + _callbacks.event_topic = onEvent; + _callbacks.event_kick = onEvent; + _callbacks.event_error = onEvent; + _callbacks.event_channel = onEvent; + _callbacks.event_privmsg = onEvent; + _callbacks.event_server_msg = onEvent; + _callbacks.event_notice = onEvent; + _callbacks.event_channel_notice = onEvent; + _callbacks.event_server_notice = onEvent; + _callbacks.event_invite = onEvent; + _callbacks.event_ctcp_req = onEvent; + _callbacks.event_ctcp_rep = onEvent; + _callbacks.event_ctcp_action = onEvent; + _callbacks.event_unknown = onEvent; + _callbacks.event_numeric = onNumericEvent; + _callbacks.event_dcc_chat_req = onDCCChatRequest; + _callbacks.event_dcc_send_req = onDCCSendRequest; + + _irc_session = irc_create_session(&_callbacks); + + if (!_irc_session) { + NSLog(@"Could not create irc_session."); + return nil; + } + + // Strip server info from nicks. +// irc_option_set(_irc_session, LIBIRC_OPTION_STRIPNICKS); + + // Set debug mode. +// irc_option_set(_irc_session, LIBIRC_OPTION_DEBUG); + + irc_set_ctx(_irc_session, (__bridge void *)(self)); + + unsigned int high, low; + irc_get_version (&high, &low); + + _version = [[NSString stringWithFormat:@"IRCClient Framework v%s (Said Achmiz) - libirc v%d.%d (Georgy Yunaev)", + IRCCLIENTVERSION, + high, + low] dataAsUTF8]; + + _channels = [NSMutableDictionary dictionary]; + _encoding = NSUTF8StringEncoding; + + _userInfo = [NSMutableDictionary dictionary]; + + return self; } -- (void)dealloc -{ - if (irc_is_connected(_irc_session)) - { +-(void) dealloc { + if (irc_is_connected(_irc_session)) { NSLog(@"Warning: IRC Session is not disconnected on dealloc"); } irc_destroy_session(_irc_session); } -+ (NSDictionary *)ircNumericCodes -{ - if(ircNumericCodeList == nil) - { - [IRCClientSession loadNumericCodes]; - } - - return ircNumericCodeList; -} - -+ (void)loadNumericCodes -{ - NSString* numericCodeListPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"IRC_Numerics" ofType:@"plist"]; ++(void) loadNumericCodes { + NSString* numericCodeListPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"IRC_Numerics" + ofType:@"plist"]; ircNumericCodeList = [NSDictionary dictionaryWithContentsOfFile:numericCodeListPath]; - if(ircNumericCodeList) - { + if (ircNumericCodeList) { NSLog(@"IRC numeric codes list loaded successfully.\n"); -// NSLog(@"%@", ircNumericCodeList); - } - else - { + } else { NSLog(@"Could not load IRC numeric codes list!\n"); } } -- (int)connect; -{ - return irc_connect(_irc_session, _server.SA_terminatedCString, (unsigned short) _port, (_password.length > 0 ? _password.SA_terminatedCString : NULL), _nickname.SA_terminatedCString, _username.SA_terminatedCString, _realname.SA_terminatedCString); +-(int) connect { + return irc_connect(_irc_session, + _server.SA_terminatedCString, + (unsigned short) _port, + (_password.length > 0 ? _password.SA_terminatedCString : NULL), + _nickname.SA_terminatedCString, + _username.SA_terminatedCString, + _realname.SA_terminatedCString); } -- (void)disconnect -{ +-(void) disconnect { irc_disconnect(_irc_session); } -- (void)startThread -{ - @autoreleasepool { - irc_run(_irc_session); - } +-(void) run { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + @autoreleasepool { + irc_run(_irc_session); + } + [_delegate disconnected:self]; + }); } -- (void)run -{ - if (_thread) { - NSLog(@"Thread already running!"); - return; - } - - _thread = [[NSThread alloc] initWithTarget:self selector:@selector(startThread) object:nil]; - [_thread start]; -} - -- (int)setNickname:(NSData *)nickname username:(NSData *)username realname:(NSData *)realname -{ - if(self.isConnected) - { +-(int) setNickname:(NSData *)nickname + username:(NSData *)username + realname:(NSData *)realname { + if (self.isConnected) { return 0; - } - else - { - _nickname = nickname.SA_dataWithTerminatedCString; - _username = username.SA_dataWithTerminatedCString; - _realname = realname.SA_dataWithTerminatedCString; + } else { + _nickname = nickname; + _username = username; + _realname = realname; return 1; } @@ -243,240 +253,635 @@ static NSDictionary* ircNumericCodeList; #pragma mark - IRC commands /**************************/ -- (int)sendRaw:(NSData *)message -{ - return irc_send_raw(_irc_session, message.SA_terminatedCString); +-(int) sendRaw:(NSData *)message { + return irc_send_raw(_irc_session, + message.SA_terminatedCString); } -- (int)quit:(NSData *)reason -{ - return irc_cmd_quit(_irc_session, reason.SA_terminatedCString); +- (int) quit:(NSData *)reason { + return irc_send_raw(_irc_session, + "QUIT :%s", + reason ? reason.SA_terminatedCString : "quit"); } -- (int)join:(NSData *)channel key:(NSData *)key -{ - if (!key || !(key.length > 0)) - { - return irc_cmd_join(_irc_session, channel.SA_terminatedCString, NULL); - } +-(int) join:(NSData *)channel + key:(NSData *)key { + if (!channel || channel.length == 0) + return LIBIRC_ERR_STATE; - return irc_cmd_join(_irc_session, channel.SA_terminatedCString, key.SA_terminatedCString); -} - -- (int)list:(NSData *)channel -{ - return irc_cmd_list(_irc_session, channel.SA_terminatedCString); -} - -- (int)userMode:(NSData *)mode -{ - return irc_cmd_user_mode(_irc_session, mode.SA_terminatedCString); -} - -- (int)nick:(NSData *)newnick -{ - return irc_cmd_nick(_irc_session, newnick.SA_terminatedCString); -} - -- (int)whois:(NSData *)nick -{ - return irc_cmd_whois(_irc_session, nick.SA_terminatedCString); -} - -- (int)message:(NSData *)message to:(NSData *)target -{ - return irc_cmd_msg(_irc_session, target.SA_terminatedCString, message.SA_terminatedCString); -} - -- (int)action:(NSData *)action to:(NSData *)target -{ - return irc_cmd_me(_irc_session, target.SA_terminatedCString, action.SA_terminatedCString); -} - -- (int)notice:(NSData *)notice to:(NSData *)target -{ - return irc_cmd_notice(_irc_session, target.SA_terminatedCString, notice.SA_terminatedCString); -} - -- (int)ctcpRequest:(NSData *)request target:(NSData *)target -{ - return irc_cmd_ctcp_request(_irc_session, target.SA_terminatedCString, request.SA_terminatedCString); -} - -- (int)ctcpReply:(NSData *)reply target:(NSData *)target -{ - return irc_cmd_ctcp_reply(_irc_session, target.SA_terminatedCString, reply.SA_terminatedCString); -} - -/****************************/ -#pragma mark - Event handlers -/****************************/ - -- (void)connectionSucceeded -{ - [_delegate connectionSucceeded:self]; -} - -- (void)nickChangedFrom:(NSData *)oldNick to:(NSData *)newNick -{ - NSData* oldNickOnly = getNickFromNickUserHost(oldNick); - - if ([_nickname isEqualToData:oldNickOnly]) - { - _nickname = newNick; - [_delegate nickChangedFrom:oldNickOnly to:newNick own:YES session:self]; - } + if (key && key.length > 0) + return irc_send_raw(_irc_session, + "JOIN %s :%s", + channel.SA_terminatedCString, + key.SA_terminatedCString); else - { - [_delegate nickChangedFrom:oldNickOnly to:newNick own:NO session:self]; + return irc_send_raw(_irc_session, + "JOIN %s", + channel.SA_terminatedCString); +} + +-(int) names:(NSData *)channel { + if (channel) + return irc_send_raw(_irc_session, + "NAMES %s", + channel.SA_terminatedCString); + else + return irc_send_raw(_irc_session, + "NAMES"); +} + +-(int) list:(NSData *)channel { + if (channel) + return irc_send_raw(_irc_session, + "LIST %s", + channel.SA_terminatedCString); + else + return irc_send_raw(_irc_session, + "LIST"); +} + +-(int) userMode:(NSData *)mode { + if (mode) + return irc_send_raw(_irc_session, + "MODE %s %s", + _nickname.SA_terminatedCString, + mode.SA_terminatedCString); + else + return irc_send_raw(_irc_session, + "MODE %s", + _nickname.SA_terminatedCString); +} + +-(int) nick:(NSData *)newnick { + if (!newnick || newnick.length == 0) + return LIBIRC_ERR_INVAL; + + return irc_send_raw(_irc_session, + "NICK %s", + newnick.SA_terminatedCString); +} + +-(int) who:(NSData *)nickmask { + if (!nickmask || nickmask.length == 0) + return LIBIRC_ERR_INVAL; + + return irc_send_raw(_irc_session, + "WHO %s", + nickmask.SA_terminatedCString); +} + +-(int) whois:(NSData *)nick { + if (!nick || nick.length == 0) + return LIBIRC_ERR_INVAL; + + return irc_send_raw(_irc_session, + "WHOIS %s", + nick.SA_terminatedCString); +} + +-(int) message:(NSData *)message + to:(NSData *)target { + if ( !target || target.length == 0 + || !message || message.length == 0) + return LIBIRC_ERR_STATE; + + return irc_send_raw(_irc_session, + "PRIVMSG %s :%s", + target.SA_terminatedCString, + irc_color_convert_to_mirc(message.SA_terminatedCString)); +} + +-(int) action:(NSData *)action + to:(NSData *)target { + if ( !target || target.length == 0 + || !action || action.length == 0) + return LIBIRC_ERR_STATE; + + return irc_send_raw(_irc_session, + "PRIVMSG %s :\x01" "ACTION %s\x01", + target.SA_terminatedCString, + irc_color_convert_to_mirc(action.SA_terminatedCString)); +} + +-(int) notice:(NSData *)notice + to:(NSData *)target { + if ( !target || target.length == 0 + || !notice || notice.length == 0) + return LIBIRC_ERR_STATE; + + return irc_send_raw(_irc_session, + "NOTICE %s :%s", + target.SA_terminatedCString, + notice.SA_terminatedCString); +} + +-(int) ctcpRequest:(NSData *)request + target:(NSData *)target { + if ( !target || target.length == 0 + || !request || request.length == 0) + return LIBIRC_ERR_STATE; + + return irc_send_raw(_irc_session, + "PRIVMSG %s :\x01%s\x01", + target.SA_terminatedCString, + request.SA_terminatedCString); +} + +-(int) ctcpReply:(NSData *)reply + target:(NSData *)target { + if ( !target || target.length == 0 + || !reply || reply.length == 0) + return LIBIRC_ERR_STATE; + + return irc_send_raw(_irc_session, + "NOTICE %s :\x01%s\x01", + target.SA_terminatedCString, + reply.SA_terminatedCString); +} + +/********************************/ +#pragma mark - IRC event handlers +/********************************/ + +-(void) ircEventReceived:(const char *)event + from:(const char *)origin + withParams:(const char **)params + count:(unsigned int)count { + typedef enum : NSUInteger { + SA_IRC_ParseColorCodes, + SA_IRC_StripColorCodes, + SA_IRC_IgnoreColorCodes, + } SA_IRC_ColorCodeHandling; + + SA_IRC_ColorCodeHandling whatAboutColors = SA_IRC_ParseColorCodes; + + NSMutableArray *params_array; + if ((whatAboutColors != SA_IRC_IgnoreColorCodes) + && ( !strcmp(event, "PRIVMSG") + || !strcmp(event, "CHANMSG") + || !strcmp(event, "SERVMSG") + || !strcmp(event, "PRIVNOTICE") + || !strcmp(event, "CHANNOTICE") + || !strcmp(event, "SERVNOTICE") + || !strcmp(event, "CTCP_ACTION") + )) { + char* (*process_color_codes) (const char *) = (whatAboutColors == SA_IRC_ParseColorCodes ? + irc_color_convert_from_mirc : + irc_color_strip_from_mirc); + + params_array = [NSMutableArray arrayWithCapacity:count]; + for (NSUInteger i = 0; i < count; i++) { + [params_array addObject:[NSData dataFromCString:(*process_color_codes)(params[i])]]; + } + } else { + params_array = (NSMutableArray *) [NSArray arrayOfCStringData:params + count:count]; + } + + NSData *origin_data = origin ? [NSData dataFromCString:origin] : nil; + NSData *param_0_data = ((count > 0) ? params_array[0] : nil); + NSData *param_1_data = ((count > 1) ? params_array[1] : nil); + NSData *param_2_data = ((count > 2) ? params_array[2] : nil); + + if (!strcmp(event, "CONNECT")) { + /*! + * The ‘on_connect’ event is triggered when the client successfully + * connects to the server, and could send commands to the server. + * No extra params supplied; \a params is 0. + */ + [_delegate connectionSucceeded:self]; + } else if (!strcmp(event, "PING")) { + /*! + * The ‘ping’ event is triggered when the client receives a PING message. + * It is only generated if the LIBIRC_OPTION_PING_PASSTHROUGH option is set; + * otherwise, the library responds to PING messages automatically. + * + * \param origin the person, who generated the ping. + * \param params[0] mandatory, contains who knows what. + */ + if ([_delegate respondsToSelector:@selector(ping:session:)]) { + [_delegate ping:param_0_data + session:self]; + } + } else if (!strcmp(event, "NICK")) { + /*! + * The ‘nick’ event is triggered when the client receives a NICK message, + * meaning that someone (including you) on a channel with the client has + * changed their nickname. + * + * \param origin The person who changed their nick. Note that it can be you! + * \param params[0] Mandatory; contains the new nick. + */ + [self nickChangedFrom:origin_data + to:param_0_data]; + } else if (!strcmp(event, "QUIT")) { + /*! + * The ‘quit’ event is triggered upon receipt of a QUIT message, which + * means that someone on a channel with the client has disconnected. + * + * \param origin The person who is disconnected. + * \param params[0] Optional; contains the reason message (user-specified). + */ + [_delegate userQuit:origin_data + withReason:param_0_data + session:self]; + } else if (!strcmp(event, "JOIN")) { + /*! + * The ‘join’ event is triggered upon receipt of a JOIN message, which + * means that someone has entered a channel that the client is on. + * + * \param origin The person who joined the channel. By comparing it with + * your own nickname, you can check whether your JOIN + * command succeed. + * \param params[0] Mandatory; contains the channel name. + */ + [self userJoined:origin_data + channel:param_0_data]; + } else if (!strcmp(event, "PART")) { + /*! + * The ‘part’ event is triggered upon receipt of a PART message, which + * means that someone has left a channel that the client is on. + * + * \param Origin The person who left the channel. By comparing it with + * your own nickname, you can check whether your PART + * command succeed. + * \param params[0] Mandatory; contains the channel name. + * \param params[1] Optional; contains the reason message (user-defined). + */ + [self userParted:origin_data + channel:param_0_data + withReason:param_1_data]; + } else if (!strcmp(event, "MODE")) { + /*! + * The ‘mode’ event is triggered upon receipt of a channel MODE message, + * which means that someone on a channel with the client has changed the + * channel’s parameters. + * + * \param origin The person who changed the channel mode. + * \param params[0] Mandatory; contains the channel name. + * \param params[1] Mandatory; contains the changed channel mode, like + * ‘+t’, ‘-i’, and so on. + * \param params[2] Optional; contains the mode argument (for example, a + * key for +k mode, or user who got channel operator status for + * +o mode) + */ + IRCClientChannel *channel = _channels[param_0_data]; + [channel modeSet:param_1_data + withParams:param_2_data + by:origin_data]; + } else if (!strcmp(event, "UMODE")) { + /*! + * The ‘umode’ event is triggered upon receipt of a user MODE message, + * which means that your user mode has been changed. + * + * \param origin The person who changed the user mode. + * \param params[0] Mandatory; contains the user changed mode, like + * ‘+t’, ‘-i’ and so on. + */ + // TODO: keep track of the user's mode on the connection? + [_delegate modeSet:param_0_data + by:origin_data + session:self]; + } else if (!strcmp(event, "TOPIC")) { + /*! + * The ‘topic’ event is triggered upon receipt of a TOPIC message, which + * means that someone on a channel with the client has changed the + * channel’s topic. + * + * \param origin The person who changes the channel topic. + * \param params[0] Mandatory; contains the channel name. + * \param params[1] Optional; contains the new topic. + */ + IRCClientChannel *channel = _channels[param_0_data]; + [channel topicSet:param_1_data + by:origin_data]; + } else if (!strcmp(event, "KICK")) { + /*! + * The ‘kick’ event is triggered upon receipt of a KICK message, which + * means that someone on a channel with the client (or possibly the + * client itself!) has been forcibly ejected. + * + * \param origin The person who kicked the poor victim. + * \param params[0] Mandatory; contains the channel name. + * \param params[1] Optional; contains the nick of kicked person. + * \param params[2] Optional; contains the kick text. + */ + [self userKicked:param_1_data + fromChannel:param_0_data + by:origin_data + withReason:param_2_data]; + } else if (!strcmp(event, "ERROR")) { + /*! + * The ‘error’ event is triggered upon receipt of an ERROR message, which + * (when sent to clients) usually means the client has been disconnected. + * + * \param origin the person, who generates the message. + * \param params optional, contains who knows what. + */ + [_delegate errorReceived:param_0_data + session:self]; + } else if (!strcmp(event, "INVITE")) { + /*! + * The ‘invite’ event is triggered upon receipt of an INVITE message, + * which means that someone is permitting the client’s entry into a +i + * channel. + * + * \param origin The person who INVITEd you. + * \param params[0] Mandatory; contains your nick. + * \param params[1] Mandatory; contains the channel name you’re invited into. + * + * \sa irc_cmd_invite irc_cmd_chanmode_invite + */ + [_delegate invitedToChannel:param_1_data + by:origin_data + session:self]; + } else if (!strcmp(event, "PRIVMSG")) { + /*! + * The ‘privmsg’ event is triggered upon receipt of a PRIVMSG message + * which is addressed to one or more clients, which means that someone + * is sending the client a private message. + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; contains your nick. + * \param params[1] Optional; contains the message text. + */ + [_delegate privateMessageReceived:param_1_data + fromUser:origin_data + session:self]; + } else if (!strcmp(event, "CHANMSG")) { + /*! + * The ‘chanmsg’ event is triggered upon receipt of a PRIVMSG message + * to an entire channel, which means that someone on a channel with + * the client has said something aloud. Your own messages don’t trigger + * PRIVMSG event. + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; contains the channel name. + * \param params[1] Optional; contains the message text. + */ + IRCClientChannel *channel = _channels[param_0_data]; + [channel messageSent:param_1_data + byUser:origin_data]; + } else if (!strcmp(event, "SERVMSG")) { + /*! + * The ‘servmsg’ event is triggered upon receipt of a PRIVMSG message + * which is addressed to no one in particular, but it sent to the client + * anyway. + * + * \param origin The person who generated the message. + * \param params Optional; contains who knows what. + */ + [_delegate serverMessageReceivedFrom:origin_data + params:params_array + session:self]; + } else if (!strcmp(event, "PRIVNOTICE")) { + /*! + * The ‘notice’ event is triggered upon receipt of a NOTICE message + * which means that someone has sent the client a public or private + * notice. According to RFC 1459, the only difference between NOTICE + * and PRIVMSG is that you should NEVER automatically reply to NOTICE + * messages. Unfortunately, this rule is frequently violated by IRC + * servers itself - for example, NICKSERV messages require reply, and + * are NOTICEs. + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; contains your nick. + * \param params[1] Optional; contains the message text. + */ + [_delegate privateNoticeReceived:param_1_data + fromUser:origin_data + session:self]; + } else if (!strcmp(event, "CHANNOTICE")) { + /*! + * The ‘notice’ event is triggered upon receipt of a NOTICE message + * which means that someone has sent the client a public or private + * notice. According to RFC 1459, the only difference between NOTICE + * and PRIVMSG is that you should NEVER automatically reply to NOTICE + * messages. Unfortunately, this rule is frequently violated by IRC + * servers itself - for example, NICKSERV messages require reply, and + * are NOTICEs. + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; contains the target channel name. + * \param params[1] Optional; contains the message text. + */ + IRCClientChannel *channel = _channels[param_0_data]; + [channel noticeSent:param_1_data + byUser:origin_data]; + } else if (!strcmp(event, "SERVNOTICE")) { + /*! + * The ‘server_notice’ event is triggered upon receipt of a NOTICE + * message which means that the server has sent the client a notice. + * This notice is not necessarily addressed to the client’s nick + * (for example, AUTH notices, sent before the client’s nick is known). + * According to RFC 1459, the only difference between NOTICE + * and PRIVMSG is that you should NEVER automatically reply to NOTICE + * messages. Unfortunately, this rule is frequently violated by IRC + * servers itself - for example, NICKSERV messages require reply, and + * are NOTICEs. + * + * \param origin The person who generated the message. + * \param params Optional; contains who knows what. + */ + [_delegate serverNoticeReceivedFrom:origin_data + params:params_array + session:self]; + } else if (!strcmp(event, "CTCP_REQ")) { + /*! + * The ‘ctcp’ event is triggered when the client receives the CTCP + * request. By default, the built-in CTCP request handler is used. The + * build-in handler automatically replies on most CTCP messages, so you + * will rarely need to override it. + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; contains the complete CTCP message, including + * its arguments. + * + * Mirc generates PING, FINGER, VERSION, TIME and ACTION messages, + * check the source code of \c libirc_event_ctcp_internal function to + * see how to write your own CTCP request handler. Also you may find + * useful this question in FAQ: \ref faq4 + */ + [self CTCPRequestReceived:param_0_data + fromUser:origin_data]; + } else if (!strcmp(event, "CTCP_REPL")) { + /*! + * The ‘ctcp’ event is triggered when the client receives the CTCP reply. + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; the CTCP message itself with its arguments. + */ + if ([_delegate respondsToSelector:@selector(CTCPReplyReceived:fromUser:session:)]) { + [_delegate CTCPReplyReceived:param_0_data + fromUser:origin_data + session:self]; + } + } else if (!strcmp(event, "CTCP_ACTION")) { + /*! + * The ‘action’ event is triggered when the client receives the CTCP + * ACTION message. These messages usually looks like:\n + * \code + * [23:32:55] * Tim gonna sleep. + * \endcode + * + * \param origin The person who generated the message. + * \param params[0] Mandatory; the target of the message. + * \param params[1] Mandatory; the ACTION message. + */ + IRCClientChannel* channel = _channels[param_0_data]; + if (channel != nil) { + // An action on a channel we’re on. + [channel actionPerformed:param_1_data + byUser:origin_data]; + } else { + // An action in a private message. + [_delegate privateCTCPActionReceived:param_1_data + fromUser:origin_data + session:self]; + } + } else { + /*! + * The ‘unknown’ event is triggered upon receipt of any number of + * unclassifiable miscellaneous messages, which aren’t handled by the + * library. + */ + if ([_delegate respondsToSelector:@selector(unknownEventReceived:from:params:session:)]) { + [_delegate unknownEventReceived:[NSData dataFromCString:event] + from:origin_data + params:params_array + session:self]; + } } } -- (void)userQuit:(NSData *)nick withReason:(NSData *)reason -{ - [_delegate userQuit:nick withReason:reason session:self]; +-(void) numericEventReceived:(NSUInteger)event + from:(NSData *)origin + params:(NSArray *)params { + if ([_delegate respondsToSelector:@selector(numericEventReceived:from:params:session:)]) { + [_delegate numericEventReceived:event + from:origin + params:params + session:self]; + } } -- (void)userJoined:(NSData *)nick channel:(NSData *)channelName -{ - NSData* nickOnly = getNickFromNickUserHost(nick); +/******************************************/ +#pragma mark - Event handler helper methods +/******************************************/ + +-(void) nickChangedFrom:(NSData *)oldNick + to:(NSData *)newNick { + NSData* oldNickOnly = [IRCClientSession nickFromNickUserHost:oldNick]; - if ([_nickname isEqualToData:nickOnly]) - { + if ([_nickname isEqualToData:oldNickOnly]) { + _nickname = newNick; + [_delegate nickChangedFrom:oldNickOnly + to:newNick + own:YES + session:self]; + } else { + [_delegate nickChangedFrom:oldNickOnly + to:newNick + own:NO + session:self]; + } +} + +-(void) userJoined:(NSData *)nick + channel:(NSData *)channelName { + NSData* nickOnly = [IRCClientSession nickFromNickUserHost:nick]; + + if ([_nickname isEqualToData:nickOnly]) { // We just joined a channel; allocate an IRCClientChannel object and // add it to our channels list. - IRCClientChannel* newChannel = [[IRCClientChannel alloc] initWithName:channelName andIRCSession:_irc_session]; + IRCClientChannel* newChannel = [[IRCClientChannel alloc] initWithName:channelName + andIRCSession:_irc_session]; _channels[channelName] = newChannel; - [_delegate joinedNewChannel:newChannel session:self]; - } - else - { - // Someone joined a channel we're on. + [_delegate joinedNewChannel:newChannel + session:self]; + } else { + // Someone joined a channel we’re on. IRCClientChannel* channel = _channels[channelName]; [channel userJoined:nick]; } } -- (void)userParted:(NSData *)nick channel:(NSData *)channelName withReason:(NSData *)reason -{ +-(void) userParted:(NSData *)nick + channel:(NSData *)channelName + withReason:(NSData *)reason { IRCClientChannel* channel = _channels[channelName]; - NSData* nickOnly = getNickFromNickUserHost(nick); + NSData* nickOnly = [IRCClientSession nickFromNickUserHost:nick]; - if ([_nickname isEqualToData:nickOnly]) - { + if ([_nickname isEqualToData:nickOnly]) { // We just left a channel; remove it from the channels dict. + [_channels removeObjectForKey:channelName]; - [channel userParted:nick withReason:reason us:YES]; - } - else - { - [channel userParted:nick withReason:reason us:NO]; + [channel userParted:nick + withReason:reason + us:YES]; + } else { + [channel userParted:nick + withReason:reason + us:NO]; } } -- (void)modeSet:(NSData* )mode withParams:(NSData *)params forChannel:(NSData *)channelName by:(NSData *)nick -{ - IRCClientChannel *channel = _channels[channelName]; - - [channel modeSet:mode withParams:params by:nick]; -} - -- (void)modeSet:(NSData *)mode by:(NSData *)nick -{ - [_delegate modeSet:mode by:nick session:self]; -} - -- (void)topicSet:(NSData *)newTopic forChannel:(NSData *)channelName by:(NSData *)nick -{ - IRCClientChannel *channel = _channels[channelName]; - - [channel topicSet:newTopic by:nick]; -} - -- (void)userKicked:(NSData *)nick fromChannel:(NSData *)channelName by:(NSData *)byNick withReason:(NSData *)reason -{ +-(void) userKicked:(NSData *)nick + fromChannel:(NSData *)channelName + by:(NSData *)byNick + withReason:(NSData *)reason { IRCClientChannel* channel = _channels[channelName]; - if (nick == nil) - { - // we got kicked from a channel we're on + if (nick == nil) { + // we got kicked from a channel we’re on :( [_channels removeObjectForKey:channelName]; - [channel userKicked:_nickname withReason:reason by:byNick us:YES]; - } - else - { - // someone else got booted from a channel we're on - [channel userKicked:nick withReason:reason by:byNick us:NO]; + [channel userKicked:_nickname + withReason:reason + by:byNick + us:YES]; + } else { + // Someone else got booted from a channel we’re on. + [channel userKicked:nick + withReason:reason + by:byNick + us:NO]; } } -- (void)messageSent:(NSData *)message toChannel:(NSData *)channelName byUser:(NSData *)nick -{ - IRCClientChannel *channel = _channels[channelName]; - - [channel messageSent:message byUser:nick]; -} +/*****************************************/ +#pragma mark - CTCP request handler helper +/*****************************************/ -- (void)privateMessageReceived:(NSData *)message fromUser:(NSData *)nick -{ - [_delegate privateMessageReceived:message fromUser:nick session:self]; -} +-(void) CTCPRequestReceived:(NSData *)request + fromUser:(NSData *)nick { + NSData *nickOnly = [IRCClientSession nickFromNickUserHost:nick]; -- (void)serverMessageReceivedFrom:(NSData *)origin params:(NSArray *)params -{ - [_delegate serverMessageReceivedFrom:origin params:params session:self]; -} + if (!strncmp(request.SA_terminatedCString, "PING", 4)) { + [self ctcpReply:request + target:nickOnly]; + } else if (!strcmp(request.SA_terminatedCString, "VERSION")) { + const char *versionFormat = "VERSION %s"; + NSMutableData* versionReply = [NSMutableData dataWithLength:(strlen(versionFormat) + (_version.length - 2))]; + sprintf(versionReply.mutableBytes, + versionFormat, + _version.SA_terminatedCString); -- (void)privateNoticeReceived:(NSData *)notice fromUser:(NSData *)nick -{ - [_delegate privateNoticeReceived:notice fromUser:nick session:self]; -} + [self ctcpReply:versionReply + target:nickOnly]; + } else if (!strcmp(request.SA_terminatedCString, "FINGER")) { + const char *fingerFormat = "FINGER %s (%s) Idle 0 seconds)"; + NSMutableData* fingerReply = [NSMutableData dataWithLength:(strlen(fingerFormat) + (_username.length - 2) + (_realname.length - 2))]; + sprintf(fingerReply.mutableBytes, + fingerFormat, + _username.SA_terminatedCString, + _realname.SA_terminatedCString); -- (void)noticeSent:(NSData *)notice toChannel:(NSData *)channelName byUser:(NSData *)nick -{ - IRCClientChannel *channel = _channels[channelName]; - - [channel noticeSent:notice byUser:nick]; -} - -- (void)serverNoticeReceivedFrom:(NSData *)origin params:(NSArray *)params -{ - [_delegate serverNoticeReceivedFrom:origin params:params session:self]; -} - -- (void)invitedToChannel:(NSData *)channelName by:(NSData *)nick -{ - [_delegate invitedToChannel:channelName by:nick session:self]; -} - -- (void)CTCPRequestReceived:(NSData *)request fromUser:(NSData *)nick -{ - const char* the_nick = getNickFromNickUserHost(nick).bytes; - const char* the_request = request.bytes; - - if (strstr(the_request, "PING") == the_request) - { - irc_cmd_ctcp_reply(_irc_session, the_nick, the_request); - } - else if (!strcmp (the_request, "VERSION")) - { - NSMutableData* versionReply = [NSMutableData dataWithLength:8 + _version.length]; - sprintf(versionReply.mutableBytes, "VERSION %s", _version.bytes); - - irc_cmd_ctcp_reply (_irc_session, the_nick, versionReply.bytes); - } - else if (!strcmp (the_request, "FINGER")) - { - NSMutableData* fingerReply = [NSMutableData dataWithLength:25 + _username.length + _realname.length]; - sprintf(fingerReply.mutableBytes, "FINGER %s (%s) Idle 0 seconds)", _username.bytes, _realname.bytes); - - irc_cmd_ctcp_reply (_irc_session, the_nick, fingerReply.bytes); - } - else if (!strcmp (the_request, "TIME")) - { + [self ctcpReply:fingerReply + target:nickOnly]; + } else if (!strcmp(request.SA_terminatedCString, "TIME")) { time_t current_time; char timestamp[40]; struct tm *time_info; @@ -486,557 +891,118 @@ static NSDictionary* ircNumericCodeList; strftime(timestamp, 40, "TIME %a %b %e %H:%M:%S %Z %Y", time_info); - irc_cmd_ctcp_reply(_irc_session, the_nick, timestamp); - } - else - { - if ([_delegate respondsToSelector:@selector(CTCPRequestReceived:ofType:fromUser:session:)]) - { - char* request_string = malloc(request.length); - [request getBytes:request_string length:request.length]; + [self ctcpReply:[NSData dataFromCString:timestamp] + target:nickOnly]; + } else { + if ([_delegate respondsToSelector:@selector(CTCPRequestReceived:ofType:fromUser:session:)]) { + NSRange rangeOfFirstSpace = [request rangeOfData:[NSData dataFromCString:" "] + options:(NSDataSearchOptions) 0 + range:NSRangeMake(0, request.length)]; + + NSRange rangeOfSecondSpace = (rangeOfFirstSpace.location != NSNotFound ? + [request rangeOfData:[NSData dataFromCString:" "] + options:(NSDataSearchOptions) 0 + range:NSRangeMake(rangeOfFirstSpace.location + 1, + request.length - (rangeOfFirstSpace.location + 1))] : + NSRangeMake(NSNotFound, 0)); + + NSData *requestTypeData = (rangeOfFirstSpace.location != NSNotFound ? + [request subdataWithRange:NSRangeMake(0, rangeOfFirstSpace.location)] : + request); + NSData *requestBodyData = ((rangeOfSecondSpace.location != NSNotFound) ? + [request subdataWithRange:NSRangeMake(rangeOfFirstSpace.location + 1, + rangeOfSecondSpace.location - (rangeOfFirstSpace.location + 1))] : + nil); - char* request_type = strtok(request_string, " "); - char* request_body = strtok(NULL, " " ); - - NSData* requestTypeData = [NSData dataWithBytes:request_body length:strlen(request_body) + 1]; - NSData* requestBodyData = [NSData dataWithBytes:request_type length:strlen(request_type) + 1]; - - [_delegate CTCPRequestReceived:requestBodyData ofType:requestTypeData fromUser:nick session:self]; - - free(request_string); + [_delegate CTCPRequestReceived:requestBodyData + ofType:requestTypeData + fromUser:nick + session:self]; } } } -- (void)CTCPReplyReceived:(NSData *)reply fromUser:(NSData *)nick -{ - if([_delegate respondsToSelector:@selector(CTCPReplyReceived:fromUser:session:)]) - { - [_delegate CTCPReplyReceived:reply fromUser:nick session:self]; - } -} - -- (void)CTCPActionPerformed:(NSData *)action byUser:(NSData *)nick atTarget:(NSData *)target -{ - IRCClientChannel* channel = _channels[target]; - - if(channel != nil) - { - // An action on a channel we're on - [channel actionPerformed:action byUser:nick]; - } - else - { - // An action in a private message - [_delegate privateCTCPActionReceived:action fromUser:nick session:self]; - } -} - -- (void)unknownEventReceived:(NSData *)event from:(NSData *)origin params:(NSArray *)params -{ - if([_delegate respondsToSelector:@selector(unknownEventReceived:from:params:session:)]) - { - [_delegate unknownEventReceived:event from:origin params:params session:self]; - } -} - --(void)numericEventReceived:(NSUInteger)event from:(NSData *)origin params:(NSArray *)params -{ - if([_delegate respondsToSelector:@selector(numericEventReceived:from:params:session:)]) - { - [_delegate numericEventReceived:event from:origin params:params session:self]; - } -} - @end -/*************************************/ -#pragma mark - Useful helper functions -/*************************************/ - -NSData* getNickFromNickUserHost(NSData *nickUserHost) -{ - char *nick_user_host_buf = malloc(nickUserHost.length); - [nickUserHost getBytes:nick_user_host_buf length:nickUserHost.length]; - - char *nick_buf; - nick_buf = strtok(nick_user_host_buf, "!"); - - NSData *nick = (nick_buf != NULL) ? [NSData dataWithBytes:nick_buf length:strlen(nick_buf) + 1] : [NSData dataWithBlankCString]; - - free(nick_user_host_buf); - - return nick; -} - -NSData* getUserFromNickUserHost(NSData *nickUserHost) -{ - char *nick_user_host_buf = malloc(nickUserHost.length); - [nickUserHost getBytes:nick_user_host_buf length:nickUserHost.length]; - - char *nick_buf, *user_buf; - nick_buf = strtok(nick_user_host_buf, "!"); - user_buf = strtok(NULL, "@"); - - NSData *user = (user_buf != NULL) ? [NSData dataWithBytes:user_buf length:strlen(user_buf) + 1] : [NSData dataWithBlankCString]; - - free(nick_user_host_buf); - - return user; -} - -NSData* getHostFromNickUserHost(NSData *nickUserHost) -{ - char *nick_user_host_buf = malloc(nickUserHost.length); - [nickUserHost getBytes:nick_user_host_buf length:nickUserHost.length]; - - char *nick_buf, *user_buf, *host_buf; - nick_buf = strtok(nick_user_host_buf, "!"); - user_buf = strtok(NULL, "@"); - host_buf = strtok(NULL, ""); - - NSData *host = (host_buf != NULL) ? [NSData dataWithBytes:host_buf length:strlen(host_buf) + 1] : [NSData dataWithBlankCString]; - - free(nick_user_host_buf); - - return host; -} - /***********************************************/ #pragma mark - Callback function implementations /***********************************************/ -/*! - * The "on_connect" event is triggered when the client successfully - * connects to the server, and could send commands to the server. - * No extra params supplied; \a params is 0. - */ -static void onConnect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - [clientSession connectionSucceeded]; -} - -/*! - * The "nick" event is triggered when the client receives a NICK message, - * meaning that someone (including you) on a channel with the client has - * changed their nickname. - * - * \param origin the person, who changes the nick. Note that it can be you! - * \param params[0] mandatory, contains the new nick. - */ -static void onNick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *oldNick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *newNick = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - - [clientSession nickChangedFrom:oldNick to:newNick]; -} - -/*! - * The "quit" event is triggered upon receipt of a QUIT message, which - * means that someone on a channel with the client has disconnected. - * - * \param origin the person, who is disconnected - * \param params[0] optional, contains the reason message (user-specified). - */ -static void onQuit(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *reason = (count > 0) ? [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1] : [NSData dataWithBlankCString]; - - [clientSession userQuit:nick withReason:reason]; -} - -/*! - * The "join" event is triggered upon receipt of a JOIN message, which - * means that someone has entered a channel that the client is on. - * - * \param origin the person, who joins the channel. By comparing it with - * your own nickname, you can check whether your JOIN - * command succeed. - * \param params[0] mandatory, contains the channel name. - */ -static void onJoinChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - - [clientSession userJoined:nick channel:channelName]; -} - -/*! - * The "part" event is triggered upon receipt of a PART message, which - * means that someone has left a channel that the client is on. - * - * \param origin the person, who leaves the channel. By comparing it with - * your own nickname, you can check whether your PART - * command succeed. - * \param params[0] mandatory, contains the channel name. - * \param params[1] optional, contains the reason message (user-defined). - */ -static void onPartChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *reason = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBlankCString]; - - [clientSession userParted:nick channel:channelName withReason:reason]; -} - -/*! - * The "mode" event is triggered upon receipt of a channel MODE message, - * which means that someone on a channel with the client has changed the - * channel's parameters. - * - * \param origin the person, who changed the channel mode. - * \param params[0] mandatory, contains the channel name. - * \param params[1] mandatory, contains the changed channel mode, like - * '+t', '-i' and so on. - * \param params[2] optional, contains the mode argument (for example, a - * key for +k mode, or user who got the channel operator status for - * +o mode) - */ -static void onMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *mode = [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1]; - NSData *modeParams = (count > 2) ? [NSData dataWithBytes:params[2] length:strlen(params[2]) + 1] : [NSData dataWithBlankCString]; - - [clientSession modeSet:mode withParams:modeParams forChannel:channelName by:nick]; -} - -/*! - * The "umode" event is triggered upon receipt of a user MODE message, - * which means that your user mode has been changed. - * - * \param origin the person, who changed the channel mode. - * \param params[0] mandatory, contains the user changed mode, like - * '+t', '-i' and so on. - */ -static void onUserMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *mode = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - - [clientSession modeSet:mode by:nick]; -} - -/*! - * The "topic" event is triggered upon receipt of a TOPIC message, which - * means that someone on a channel with the client has changed the - * channel's topic. - * - * \param origin the person, who changes the channel topic. - * \param params[0] mandatory, contains the channel name. - * \param params[1] optional, contains the new topic. - */ -static void onTopic(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *topic = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBlankCString]; - - [clientSession topicSet:topic forChannel:channelName by:nick]; -} - -/*! - * The "kick" event is triggered upon receipt of a KICK message, which - * means that someone on a channel with the client (or possibly the - * client itself!) has been forcibly ejected. - * - * \param origin the person, who kicked the poor. - * \param params[0] mandatory, contains the channel name. - * \param params[1] optional, contains the nick of kicked person. - * \param params[2] optional, contains the kick text - */ -static void onKick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *byNick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *nick = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : nil; - NSData *reason = (count > 2) ? [NSData dataWithBytes:params[2] length:strlen(params[2]) + 1] : [NSData dataWithBlankCString]; - - [clientSession userKicked:nick fromChannel:channelName by:byNick withReason:reason]; -} - -/*! - * The "channel" event is triggered upon receipt of a PRIVMSG message - * to an entire channel, which means that someone on a channel with - * the client has said something aloud. Your own messages don't trigger - * PRIVMSG event. - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, contains the channel name. - * \param params[1] optional, contains the message text - */ -static void onChannelPrvmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *message = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBlankCString]; - - [clientSession messageSent:message toChannel:channelName byUser:nick]; -} - -/*! - * The "privmsg" event is triggered upon receipt of a PRIVMSG message - * which is addressed to one or more clients, which means that someone - * is sending the client a private message. - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, contains your nick. - * \param params[1] optional, contains the message text - */ -static void onPrivmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *message = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBlankCString]; - - [clientSession privateMessageReceived:message fromUser:nick]; -} - -/*! - * The "privmsg" event is triggered upon receipt of a PRIVMSG message - * which is addressed to no one in particular, but it sent to the client - * anyway. - * - * \param origin the person, who generates the message. - * \param params optional, contains who knows what. - */ -static void onServerMsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *sender = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count]; - for (unsigned int i = 0; i < count; i++) - { - [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]]; +static void onEvent(irc_session_t *session, + const char *event, + const char *origin, + const char **params, + unsigned int count) { + @autoreleasepool { + [(__bridge IRCClientSession *) irc_get_ctx(session) ircEventReceived:event + from:origin + withParams:params + count:count]; } - - [clientSession serverMessageReceivedFrom:sender params:paramsArray]; } /*! - * The "notice" event is triggered upon receipt of a NOTICE message - * which means that someone has sent the client a public or private - * notice. According to RFC 1459, the only difference between NOTICE - * and PRIVMSG is that you should NEVER automatically reply to NOTICE - * messages. Unfortunately, this rule is frequently violated by IRC - * servers itself - for example, NICKSERV messages require reply, and - * are NOTICEs. - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, contains your nick. - * \param params[1] optional, contains the message text - */ -static void onNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *notice = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBlankCString]; - - [clientSession privateNoticeReceived:notice fromUser:nick]; -} - -/*! - * The "notice" event is triggered upon receipt of a NOTICE message - * which means that someone has sent the client a public or private - * notice. According to RFC 1459, the only difference between NOTICE - * and PRIVMSG is that you should NEVER automatically reply to NOTICE - * messages. Unfortunately, this rule is frequently violated by IRC - * servers itself - for example, NICKSERV messages require reply, and - * are NOTICEs. - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, contains the target channel name. - * \param params[1] optional, contains the message text. - */ -static void onChannelNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *notice = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBlankCString]; - - [clientSession noticeSent:notice toChannel:channelName byUser:nick]; -} - -/*! - * The "server_notice" event is triggered upon receipt of a NOTICE - * message which means that the server has sent the client a notice. - * This notice is not necessarily addressed to the client's nick - * (for example, AUTH notices, sent before the client's nick is known). - * According to RFC 1459, the only difference between NOTICE - * and PRIVMSG is that you should NEVER automatically reply to NOTICE - * messages. Unfortunately, this rule is frequently violated by IRC - * servers itself - for example, NICKSERV messages require reply, and - * are NOTICEs. - * - * \param origin the person, who generates the message. - * \param params optional, contains who knows what. - */ -static void onServerNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *sender = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count]; - for (unsigned int i = 0; i < count; i++) - { - [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]]; - } - - [clientSession serverNoticeReceivedFrom:sender params:paramsArray]; -} - -/*! - * The "invite" event is triggered upon receipt of an INVITE message, - * which means that someone is permitting the client's entry into a +i - * channel. - * - * \param origin the person, who INVITEs you. - * \param params[0] mandatory, contains your nick. - * \param params[1] mandatory, contains the channel name you're invited into. - * - * \sa irc_cmd_invite irc_cmd_chanmode_invite - */ -static void onInvite(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *channelName = [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1]; - - [clientSession invitedToChannel:channelName by:nick]; -} - -/*! - * The "ctcp" event is triggered when the client receives the CTCP - * request. By default, the built-in CTCP request handler is used. The - * build-in handler automatically replies on most CTCP messages, so you - * will rarely need to override it. - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, the complete CTCP message, including its - * arguments. - * - * Mirc generates PING, FINGER, VERSION, TIME and ACTION messages, - * check the source code of \c libirc_event_ctcp_internal function to - * see how to write your own CTCP request handler. Also you may find - * useful this question in FAQ: \ref faq4 - */ -static void onCtcpRequest(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *request = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - - [clientSession CTCPRequestReceived:request fromUser:nick]; -} - -/*! - * The "ctcp" event is triggered when the client receives the CTCP reply. - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, the CTCP message itself with its arguments. - */ -static void onCtcpReply(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *reply = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - - [clientSession CTCPReplyReceived:reply fromUser:nick]; -} - -/*! - * The "action" event is triggered when the client receives the CTCP - * ACTION message. These messages usually looks like:\n - * \code - * [23:32:55] * Tim gonna sleep. - * \endcode - * - * \param origin the person, who generates the message. - * \param params[0] mandatory, the target of the message. - * \param params[1] mandatory, the ACTION message. - */ -static void onCtcpAction(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSData *target = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1]; - NSData *action = [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1]; - - [clientSession CTCPActionPerformed:action byUser:nick atTarget:target]; -} - -/*! - * The "unknown" event is triggered upon receipt of any number of - * unclassifiable miscellaneous messages, which aren't handled by the - * library. - */ -static void onUnknownEvent(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSData *eventType = [NSData dataWithBytes:event length:strlen(event) + 1]; - NSData *sender = (origin != NULL) ? [NSData dataWithBytes:origin length:strlen(origin) + 1] : [NSData dataWithBlankCString]; - NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count]; - for (unsigned int i = 0; i < count; i++) - { - [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]]; - } - - [clientSession unknownEventReceived:eventType from:sender params:paramsArray]; -} - -/*! - * The "numeric" event is triggered upon receipt of any numeric response + * The ‘numeric’ event is triggered upon receipt of any numeric response * from the server. There is a lot of such responses, see the full list * here: \ref rfcnumbers. * - * See the params in ::irc_eventcode_callback_t specification. + * \param session the session, which generates an event + * \param event the numeric code of the event. Useful in case you use a + * single event handler for several events simultaneously. + * \param origin the originator of the event. See the note below. + * \param params a list of event params. Depending on the event nature, it + * could have zero or more params. The actual number of params + * is specified in count. None of the params can be NULL, but + * ‘params’ pointer itself could be NULL for some events. + * \param count the total number of params supplied. */ -static void onNumericEvent(irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count) -{ - IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session); - - NSUInteger eventNumber = event; - NSData *sender = [NSData dataWithBytes:origin length:strlen(origin) + 1]; - NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count]; - for (unsigned int i = 0; i < count; i++) - { - [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]]; +static void onNumericEvent(irc_session_t *session, + unsigned int event, + const char *origin, + const char **params, + unsigned int count) { + @autoreleasepool { + [(__bridge IRCClientSession *) irc_get_ctx(session) numericEventReceived:event + from:[NSData dataFromCString:origin] + params:[NSArray arrayOfCStringData:params + count:count]]; } - - [clientSession numericEventReceived:eventNumber from:sender params:paramsArray]; +} + +/*! + * The ‘dcc chat’ event is triggered when someone requests a DCC CHAT from + * you. + * + * \param session the session, which generates an event + * \param nick the person who requested DCC CHAT with you. + * \param addr the person's IP address in decimal-dot notation. + * \param dccid an id associated with this request. Use it in calls to + * irc_dcc_accept() or irc_dcc_decline(). + */ +static void onDCCChatRequest(irc_session_t *session, + const char *nick, + const char *addr, + irc_dcc_t dccid) { + // TODO: figure out what to do here??? +} + +/*! + * The ‘dcc send’ event is triggered when someone wants to send a file + * to you via DCC SEND request. + * + * \param session the session, which generates an event + * \param nick the person who requested DCC SEND to you. + * \param addr the person's IP address in decimal-dot notation. + * \param filename the sent filename. + * \param size the filename size. + * \param dccid an id associated with this request. Use it in calls to + * irc_dcc_accept() or irc_dcc_decline(). + */ +static void onDCCSendRequest(irc_session_t *session, + const char *nick, + const char *addr, + const char *filename, + size_t size, + irc_dcc_t dccid) { + // TODO: figure out what to do here??? }