diff --git a/IRCClient.xcodeproj/project.pbxproj b/IRCClient.xcodeproj/project.pbxproj index c62ed1e..0af69fa 100644 --- a/IRCClient.xcodeproj/project.pbxproj +++ b/IRCClient.xcodeproj/project.pbxproj @@ -8,12 +8,26 @@ /* Begin PBXBuildFile section */ 86F2EFEA1C21F73600B033A4 /* IRCClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F2EFE91C21F73600B033A4 /* IRCClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 86F2EFF81C21F81900B033A4 /* IRCClientChannel_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F2EFF11C21F81900B033A4 /* IRCClientChannel_Private.h */; }; + 86F2EFF91C21F81900B033A4 /* IRCClientChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F2EFF21C21F81900B033A4 /* IRCClientChannel.h */; }; + 86F2EFFA1C21F81900B033A4 /* IRCClientChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 86F2EFF31C21F81900B033A4 /* IRCClientChannel.m */; }; + 86F2EFFB1C21F81900B033A4 /* IRCClientChannelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F2EFF41C21F81900B033A4 /* IRCClientChannelDelegate.h */; }; + 86F2EFFC1C21F81900B033A4 /* IRCClientSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F2EFF51C21F81900B033A4 /* IRCClientSession.h */; }; + 86F2EFFD1C21F81900B033A4 /* IRCClientSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 86F2EFF61C21F81900B033A4 /* IRCClientSession.m */; }; + 86F2EFFE1C21F81900B033A4 /* IRCClientSessionDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F2EFF71C21F81900B033A4 /* IRCClientSessionDelegate.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 86F2EFE61C21F73600B033A4 /* IRCClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IRCClient.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 86F2EFE91C21F73600B033A4 /* IRCClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IRCClient.h; sourceTree = ""; }; 86F2EFEB1C21F73600B033A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 86F2EFF11C21F81900B033A4 /* IRCClientChannel_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRCClientChannel_Private.h; sourceTree = ""; }; + 86F2EFF21C21F81900B033A4 /* IRCClientChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRCClientChannel.h; sourceTree = ""; }; + 86F2EFF31C21F81900B033A4 /* IRCClientChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IRCClientChannel.m; sourceTree = ""; }; + 86F2EFF41C21F81900B033A4 /* IRCClientChannelDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRCClientChannelDelegate.h; sourceTree = ""; }; + 86F2EFF51C21F81900B033A4 /* IRCClientSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRCClientSession.h; sourceTree = ""; }; + 86F2EFF61C21F81900B033A4 /* IRCClientSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IRCClientSession.m; sourceTree = ""; }; + 86F2EFF71C21F81900B033A4 /* IRCClientSessionDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRCClientSessionDelegate.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -46,6 +60,13 @@ 86F2EFE81C21F73600B033A4 /* IRCClient */ = { isa = PBXGroup; children = ( + 86F2EFF11C21F81900B033A4 /* IRCClientChannel_Private.h */, + 86F2EFF21C21F81900B033A4 /* IRCClientChannel.h */, + 86F2EFF31C21F81900B033A4 /* IRCClientChannel.m */, + 86F2EFF41C21F81900B033A4 /* IRCClientChannelDelegate.h */, + 86F2EFF51C21F81900B033A4 /* IRCClientSession.h */, + 86F2EFF61C21F81900B033A4 /* IRCClientSession.m */, + 86F2EFF71C21F81900B033A4 /* IRCClientSessionDelegate.h */, 86F2EFE91C21F73600B033A4 /* IRCClient.h */, 86F2EFEB1C21F73600B033A4 /* Info.plist */, ); @@ -59,6 +80,11 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 86F2EFFE1C21F81900B033A4 /* IRCClientSessionDelegate.h in Headers */, + 86F2EFF91C21F81900B033A4 /* IRCClientChannel.h in Headers */, + 86F2EFFB1C21F81900B033A4 /* IRCClientChannelDelegate.h in Headers */, + 86F2EFFC1C21F81900B033A4 /* IRCClientSession.h in Headers */, + 86F2EFF81C21F81900B033A4 /* IRCClientChannel_Private.h in Headers */, 86F2EFEA1C21F73600B033A4 /* IRCClient.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -130,6 +156,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 86F2EFFD1C21F81900B033A4 /* IRCClientSession.m in Sources */, + 86F2EFFA1C21F81900B033A4 /* IRCClientChannel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -233,6 +261,7 @@ INFOPLIST_FILE = IRCClient/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.6; PRODUCT_BUNDLE_IDENTIFIER = saidachmiz.IRCClient; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -251,6 +280,7 @@ INFOPLIST_FILE = IRCClient/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.6; PRODUCT_BUNDLE_IDENTIFIER = saidachmiz.IRCClient; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/IRCClient/IRCClientChannel.h b/IRCClient/IRCClientChannel.h new file mode 100644 index 0000000..7c35c58 --- /dev/null +++ b/IRCClient/IRCClientChannel.h @@ -0,0 +1,138 @@ +/* + * Modified IRCClient Copyright 2015 Said Achmiz (www.saidachmiz.net) + * + * Original IRCClient Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net + * + * This library 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 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +/** + * @file IRCClientChannel.h + * @author Nathan Ollerenshaw + * @version 1.0 + * @date 01.2009 + * @brief Represents a connected IRC Channel. + */ + +#import +#import "IRCClientChannelDelegate.h" +#import "libircclient.h" + +/** \class IRCClientChannel + * @brief Represents a connected IRC Channel. + * + * IRCClientChannel objects are created by the IRCClientSession object + * for a given session when the client joins an IRC channel. + */ + +@class IRCClientSession; + +#pragma mark IRCClientChannel class declaration + +@interface IRCClientChannel : NSObject + +/************************/ +#pragma mark - Properties +/************************/ + +/** Delegate to send events to */ +@property (assign) id delegate; + +/** Name of the channel */ +@property (nonatomic, retain) NSData *name; + +/** Encoding used by, and in, this channel */ +@property (assign) NSStringEncoding encoding; + +/** Topic of the channel + * + * You can (attempt to) set the topic by using setChannelTopic:, not by + * changing this property (which is readonly). If the connected user has the + * privileges to set the channel topic, the channel's delegate will receive a + * topicSet:by: message (and the topic property of the channel object will be + * updated automatically). + */ +@property (nonatomic, readonly) NSData *topic; + +/** Modes of the channel */ +@property (nonatomic, retain) NSString *modes; + +/** An array of nicknames stored as NSStrings that list the connected users + for the channel */ +@property (nonatomic, readonly) NSArray *nicks; + +/**************************/ +#pragma mark - IRC commands +/**************************/ + +/** Parts the channel. */ +- (int)part; + +/** Invites another IRC client to the channel. + * + * @param nick the nickname of the client to invite. + */ +- (int)invite:(NSString *)nick; + +/** Sets the topic of the channel. + * + * Note that not all users on a channel have permission to change the topic; if you fail + * to set the topic, then you will not see an onTopic event on the IRCClientChannelDelegate. + * + * @param aTopic the topic the client wishes to set for the channel. + */ +- (void)setChannelTopic:(NSString *)newTopic; + +/** Sets the mode of the channel. + * + * Note that not all users on a channel have permission to change the mode; if you fail + * to set the mode, then you will not see a modeSet event on the IRCClientChannelDelegate. + * + * @param mode the mode to set the channel to + */ +- (int)setMode:(NSString *)mode params:(NSString *)params; + +/** Sends a public PRIVMSG to the channel. If you try to send more than can fit on an IRC + buffer, it will be truncated. + + @param message the message to send to the channel. + */ +- (int)message:(NSString *)message; + +/** Sends a public CTCP ACTION to the channel. + * + * @param action action to send to the channel. + */ +- (int)action:(NSString *)action; + +/** Sends a public NOTICE to the channel. + * + * @param notice message to send to the channel. + */ +- (int)notice:(NSString *)notice; + +/** Kicks someone from a channel. + * + * @param nick the IRC client to kick from the channel. + * @param reason the message to give to the channel and the IRC client for the kick. + */ +- (int)kick:(NSString *)nick reason:(NSString *)reason; + +/** Sends a CTCP request to the channel. + * + * It is perfectly legal to send a CTCP request to an IRC channel, however many clients + * decline to respond to them, and often they are percieved as annoying. + * + * @param request the string of the request, in CTCP format. + */ +- (int)ctcpRequest:(NSData *)request; + +@end diff --git a/IRCClient/IRCClientChannel.m b/IRCClient/IRCClientChannel.m new file mode 100644 index 0000000..10dbce5 --- /dev/null +++ b/IRCClient/IRCClientChannel.m @@ -0,0 +1,189 @@ +/* + * Modified IRCClient Copyright 2015 Said Achmiz (www.saidachmiz.net) + * + * Original IRCClient Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net + * + * This library 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 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#import "IRCClientChannel.h" +#import "IRCClientSession.h" +#import "IRCClientChannel_Private.h" + +#pragma mark IRCClientChannel private category + +@interface IRCClientChannel() +{ + NSData *name; + irc_session_t *irc_session; + NSStringEncoding encoding; + NSData *topic; + NSString *modes; + NSMutableArray *nicks; +} + +@property (nonatomic, retain) NSMutableArray *nicks; + +@end + +#pragma mark - IRCClientChannel class implementation + +@implementation IRCClientChannel + +#pragma mark - Property synthesis + +@synthesize delegate; +@synthesize name; +@synthesize encoding; +@synthesize topic; +@synthesize modes; + +#pragma mark - Custom accessors + +-(NSArray *)nicks +{ + NSArray* nicksCopy = [nicks copy]; + return nicksCopy; +} + +-(void)setNicks:(NSArray *)newNicks +{ + nicks = [newNicks mutableCopy]; +} + +/**************************/ +#pragma mark - Initializers +/**************************/ + +-(instancetype)initWithName:(NSData *)aName andIRCSession:(irc_session_t *)session +{ + if ((self = [super init])) { + name = aName; + irc_session = session; + topic = [NSData dataWithBytes:@"".UTF8String length:1]; + encoding = NSUTF8StringEncoding; + } + + return self; +} + +/**************************/ +#pragma mark - IRC commands +/**************************/ + +- (int)part +{ + return irc_cmd_part(irc_session, name.bytes); +} + +- (int)invite:(NSString *)nick +{ + return irc_cmd_invite(irc_session, nick.UTF8String, name.bytes); +} + +- (int)refreshNames +{ + return irc_cmd_names(irc_session, name.bytes); +} + +- (void)setChannelTopic:(NSString *)newTopic +{ + irc_cmd_topic(irc_session, name.bytes, [newTopic cStringUsingEncoding:encoding]); +} + +- (int)setMode:(NSString *)mode params:(NSString *)params +{ + NSMutableString* modeString = [mode mutableCopy]; + + if(params != nil && params.length > 0) + [modeString appendFormat:@" %@", params]; + + return irc_cmd_channel_mode(irc_session, name.bytes, modeString.UTF8String); +} + +- (int)message:(NSString *)message +{ + return irc_cmd_msg(irc_session, name.bytes, [message cStringUsingEncoding:encoding]); +} + +- (int)action:(NSString *)action +{ + return irc_cmd_me(irc_session, name.bytes, [action cStringUsingEncoding:encoding]); +} + +- (int)notice:(NSString *)notice +{ + return irc_cmd_notice(irc_session, name.bytes, [notice cStringUsingEncoding:encoding]); +} + +- (int)kick:(NSString *)nick reason:(NSString *)reason +{ + return irc_cmd_kick(irc_session, nick.UTF8String, name.bytes, [reason cStringUsingEncoding:encoding]); +} + +- (int)ctcpRequest:(NSData *)request +{ + return irc_cmd_ctcp_request(irc_session, name.bytes, request.bytes); +} + +/****************************/ +#pragma mark - Event handlers +/****************************/ + +- (void)userJoined:(NSString *)nick +{ + [delegate userJoined:nick]; +} + +- (void)userParted:(NSString *)nick withReason:(NSData *)reason us:(BOOL)wasItUs +{ + NSString* reasonString = [[NSString alloc] initWithData:reason encoding:encoding]; + [delegate userParted:nick withReason:reasonString us:wasItUs]; +} + +- (void)modeSet:(NSString *)mode withParams:(NSString *)params by:(NSString *)nick +{ + [delegate modeSet:mode withParams:params by:nick]; +} + +- (void)topicSet:(NSData *)newTopic by:(NSString *)nick +{ + topic = newTopic; + + NSString* topicString = [[NSString alloc] initWithData:topic encoding:encoding]; + [delegate topicSet:topicString by:nick]; +} + +- (void)userKicked:(NSString *)nick withReason:(NSData *)reason by:(NSString *)byNick us:(BOOL)wasItUs +{ + NSString* reasonString = [[NSString alloc] initWithData:reason encoding:encoding]; + [delegate userKicked:nick withReason:reasonString by:byNick us:wasItUs]; +} + +- (void)messageSent:(NSData *)message byUser:(NSString *)nick +{ + NSString* messageString = [[NSString alloc] initWithData:message encoding:encoding]; + [delegate messageSent:messageString byUser:nick]; +} + +- (void)noticeSent:(NSData *)notice byUser:(NSString *)nick +{ + NSString* noticeString = [[NSString alloc] initWithData:notice encoding:encoding]; + [delegate noticeSent:noticeString byUser:nick]; +} + +- (void)actionPerformed:(NSData *)action byUser:(NSString *)nick +{ + NSString* actionString = [[NSString alloc] initWithData:action encoding:encoding]; + [delegate actionPerformed:actionString byUser:nick]; +} + +@end diff --git a/IRCClient/IRCClientChannelDelegate.h b/IRCClient/IRCClientChannelDelegate.h new file mode 100644 index 0000000..34053e9 --- /dev/null +++ b/IRCClient/IRCClientChannelDelegate.h @@ -0,0 +1,110 @@ +/* + * Modified IRCClient Copyright 2015 Said Achmiz (www.saidachmiz.net) + * + * Original IRCClient Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net + * + * This library 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 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +/** + * @file IRCClientChannelDelegate.h + * @author Nathan Ollerenshaw + * @version 1.0 + * @date 01.2009 + * @brief Receives delegate messages from an IRCClientChannel. + * @protocol IRCClientChannelDelegate + */ + +#import + +/** @brief Receives delegate messages from an IRCClientChannel. + * + * Each IRCClientChannel object needs a delegate. Delegate methods are called + * for each event that occurs on an IRC channel that the client is current on. + * + * Note that for any given parameter, it may be optional, in which case a nil + * object may be supplied instead of the given parameter. + */ + +@protocol IRCClientChannelDelegate + +/** When a client joins this channel, the userJoined event is fired. Note that + * the nickname is most likely in nick!user\@host format, but may simply be a + * nickname, depending on the server implementation. + * + * You should also expect to see this event when the client first joins a channel, + * with a parameter of the client's nickname. + * + * @param nick The nickname of the user that joined the channel. + */ +- (void)userJoined:(NSString *)nick; + +/** When an IRC client parts a channel you are connect to, you will see + * an onPart event. You will also see this event when you part a channel. + * + * @param nick (required) The nickname of the user that left the channel. + * @param reason (optional) The reason, if any, that the user gave for leaving. + * @param wasItUs (required) Was it us who parted, or another user? + */ +- (void)userParted:(NSString *)nick withReason:(NSString *)reason us:(BOOL)wasItUs; + +/** Received when an IRC client changes the channel mode. What modes are available + * for a given channel is an implementation detail for each server. + * + * @param mode the new channel mode. + * @param params any parameters with the mode (such as channel key). + * @param nick the nickname of the IRC client that changed the mode. + */ +- (void)modeSet:(NSString *)mode withParams:(NSString *)params by:(NSString *)nick; + +/** Received when the topic is changed for the channel. + * + * @param aTopic The new topic of the channel. + * @param nick Nickname of the IRC client that changed the topic. + */ +- (void)topicSet:(NSString *)newTopic by:(NSString *)nick; + +/** Received when an IRC client is kicked from a channel. + * + * @param nick nickname of the client that was kicked + * @param reason reason message given for the kick + * @param byNick nickname of the client that performed the kick command + * @param wasItUs Was it us who got kicked, or another user? + */ +- (void)userKicked:(NSString *)nick withReason:(NSString *)reason by:(NSString *)byNick us:(BOOL)wasItUs; + +/** Received when an IRC client sends a public PRIVMSG to the channel. Note that the + * user may not necessarily be required to be on the channel to send a message + * to it. + * + * @param message the message sent to the channel. + * @param nick the nickname of the IRC client that sent the message. + */ +- (void)messageSent:(NSString *)message byUser:(NSString *)nick; + +/** Received when an IRC client sends a public NOTICE to the channel. Note that + * the user may not necessarily be required to be on the channel to send a notice to + * it. Furthermore, the RFC states that the only difference between PRIVMSG and + * NOTICE is that a NOTICE may never be responded to automatically. + * + * @param notice the notice sent to the channel. + * @param nick the nickname of the IRC client that sent the notice. + */ +- (void)noticeSent:(NSString *)notice byUser:(NSString *)nick; + +/** Received when an IRC client sends a CTCP ACTION message to the channel. + * + * @param action the action message sent to the channel. + * @param nick the nickname of the IRC client that sent the message. + */ +- (void)actionPerformed:(NSString *)action byUser:(NSString *)nick; + +@end diff --git a/IRCClient/IRCClientChannel_Private.h b/IRCClient/IRCClientChannel_Private.h new file mode 100644 index 0000000..c0b0fd7 --- /dev/null +++ b/IRCClient/IRCClientChannel_Private.h @@ -0,0 +1,48 @@ +// +// IRCClientChannel_Private.h +// Meil +// +// Copyright 2015 Said Achmiz (www.saidachmiz.net) +// + +#import "IRCClientChannel.h" + +@interface IRCClientChannel () + +/** initWithName:andIRCSession: + * + * Returns an initialised IRCClientChannel with a given channel name, associated + * with the given irc_session_t object. You are not expected to initialise your + * own IRCClientChannel objects; if you wish to join a channel you should send a + * [IRCClientSession join:key:] message to your IRCClientSession object. + * + * @param aName Name of the channel. + */ +-(instancetype)initWithName:(NSData *)aName andIRCSession:(irc_session_t *)session; + +/****************************/ +#pragma mark - Event handlers +/****************************/ + +/* NOTE: These methods are not to be called by classes that use IRCClient; + * they are for the framework's internal use only. Do not import this header + * in files that make use of the IRCClientChannel class. + */ + +- (void)userJoined:(NSString *)nick; + +- (void)userParted:(NSString *)nick withReason:(NSData *)reason us:(BOOL)wasItUs; + +- (void)modeSet:(NSString *)mode withParams:(NSString *)params by:(NSString *)nick; + +- (void)topicSet:(NSData *)newTopic by:(NSString *)nick; + +- (void)userKicked:(NSString *)nick withReason:(NSData *)reason by:(NSString *)byNick us:(BOOL)wasItUs; + +- (void)messageSent:(NSData *)message byUser:(NSString *)nick; + +- (void)noticeSent:(NSData *)notice byUser:(NSString *)nick; + +- (void)actionPerformed:(NSData *)action byUser:(NSString *)nick; + +@end diff --git a/IRCClient/IRCClientSession.h b/IRCClient/IRCClientSession.h new file mode 100644 index 0000000..11dc056 --- /dev/null +++ b/IRCClient/IRCClientSession.h @@ -0,0 +1,254 @@ +/*! \mainpage IRCClient - a Cocoa IRC Framework to create IRC clients + * + * \section intro_sec Introduction + * + * IRCClient is a Cocoa Framework that uses the excellent libircclient library + * written by Georgy Yunaev. + * + * \section usage Basic Usage + * + * To use this framework, you will need to write an IRCClientSessionDelegate to + * handle all of the events generated by the server, and an IRCClientChannelDelegate + * to handle all of the events generated by channels on that server. + * + * You then create an IRCClientSession object in your code, assign the required + * properties, and call connect: to connect to the server and run: to create + * the new thread and start receiving events. For example: + * + * \code + * IRCClientSession *session = [[IRCClientSession alloc] init]; + * MyIRCClientSessionDelegate *controller = [[MyIRCClientSessionDelegate alloc] init]; + * + * [session setDelegate:controller]; + * [controller setSession:session]; + * + * [session setServer:@"irc.dal.net"]; + * [session setPort:@"6667"]; + * [session setNickname:@"test"]; + * [session setUsername:@"test"]; + * [session setRealname:@"test"]; + * [session connect]; + * + * [session run]; //starts the thread + * \endcode + * + * \section author Author, copyright, support. + * + * If you have any questions, bug reports, suggestions regarding libircclient + * or the IRCClient framework, please visit http://libircclient.sourceforge.net + * + *
+ * libircclient Copyright (C) 2004-2009 Georgy Yunaev gyunaev@ulduzsoft.com
+ * Original IRCClient Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net
+ * Modified IRCClient Copyright 2015 Said Achmiz (www.saidachmiz.net)
+ *
+ * This library 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 of the License, or (at your 
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
+ * License for more details.
+ * 
+ */ + +/** + * @file IRCClientSession.h + * @author Nathan Ollerenshaw + * @version 1.0 + * @date 01.2009 + * @brief Represents a connected IRC Session. + */ + +#import +#import "IRCClientSessionDelegate.h" +#include "libircclient.h" + +/** @class IRCClientSession + * @brief Represents a connected IRC Session. + * + * IRCClientSession represents a single connection to an IRC server. On initialising + * the object, and setting the delegate, server, port, password, nickname, username and realname + * properties, you call the connect: and run: methods to connect to the IRC server + * and start a new thread. + * + * This thread then sends messages back to the main runloop to the IRC server delegate, + * or to the IRCClientChannel delegate as required. + */ + +@class IRCClientChannel; + +#pragma mark IRCClientSession class declaration + +@interface IRCClientSession : NSObject + +/************************/ +#pragma mark - Properties +/************************/ + +/** delegate to send events to. */ +@property (assign) id delegate; + +/** User-defined session ID (for one delegate to keep track of multiple sessions, + if desired). + */ +@property NSUInteger sessionID; + +/** The version string for the client to send back on CTCP VERSION requests */ +@property (copy) NSString *version; + +/** IRC server to connect to */ +@property (copy) NSString *server; + +/** IRC port to connect to */ +@property NSUInteger port; + +/** Server password to provide on connect (may be left empty or nil) */ +@property (copy) NSData *password; + +/** Nickname of the connected client. Note that setting this after connection will + not result in the client renaming on IRC. You need to send a nick: message instead. + */ +@property (copy) NSString *nickname; + +/** Username of the connected client. Also known as the ident. + + Setting this after connection does nothing. + */ +@property (copy) NSString *username; + +/** Realname of the connected client. + + Setting this after connection does nothing. + */ +@property (copy) NSString *realname; + +/** An NSDictionary of channels that the client is currently connected to. */ +@property (retain, readonly) NSDictionary *channels; + +/** The default text encoding for messages on this server. + + This concerns messages received via PRIVMSG and NOTICE, and TOPIC in a channel. + It also affects what encoding reasons given for QUIT messages are assumed to be in. + You may change this at any time. + */ +@property (assign) NSStringEncoding encoding; + +/** returns YES if the server is currently connected successfully, and NO if + it is not. */ +@property (nonatomic, readonly) bool connected; + +/***************************/ +#pragma mark - Class methods +/***************************/ + +/** Connect to the IRC server. + + Note that this performs the initial DNS lookup and the TCP connection, so if + there are any problems you will be notified via the return code of the message. + + Look at the libircclient documentation for the different return codes. + */ +- (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; + +/** 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; + +/**************************/ +#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; + +/** quits the IRC server with the given reason. On success, an onQuit event will be + sent to the IRCClientSessionDelegate with the nickname of the IRC client. + */ +- (int)quit:(NSData *)reason; + +/** Joins a channel with a given name and key + + @param channel the channel to join + @param key they key for the channel (may be nil) + */ +- (int)join:(NSData *)channel key:(NSData *)key; + +/** 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; + +/** sets the user mode for the IRC client + + @param mode string to set + */ +- (int)userMode:(NSString *)mode; + +/** sets the IRC client nickname. On success, an onNick event will be sent to the delegate + + @param newnick new nickname to set. + */ +- (int)nick:(NSString *)newnick; + +/** sends a WHOIS request to the IRC server + + @param nick nickname of the irc client to whois. +*/ +- (int)whois:(NSString *)nick; + +/** 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:(NSString *)target; + +/** send 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:(NSString *)target; + +/** 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:(NSString *)target; + +/** 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:(NSString *)target; + +/** 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:(NSString *)target; + +@end + +NSString* getNickFromNickUserHost(NSString *nuh); + +NSString* getUserFromNickUserHost(NSString *nuh); + +NSString* getHostFromNickUserHost(NSString *nuh); diff --git a/IRCClient/IRCClientSession.m b/IRCClient/IRCClientSession.m new file mode 100644 index 0000000..31e5c84 --- /dev/null +++ b/IRCClient/IRCClientSession.m @@ -0,0 +1,948 @@ +/* + * Modified IRCClient Copyright 2015 Said Achmiz (www.saidachmiz.net) + * + * Original IRCClient Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net + * + * This library 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 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#pragma mark Defines and includes + +#define IRCCLIENTVERSION "1.0" + +#import "IRCClientSession.h" +#import "IRCClientChannel.h" +#import "IRCClientChannel_Private.h" +#include "string.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 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 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); + +#pragma mark - IRCClientSession private category declaration + +@interface IRCClientSession() +{ + irc_callbacks_t callbacks; + irc_session_t *irc_session; + NSThread *thread; + + NSString *version; + NSString *server; + NSUInteger port; + NSData *password; + + NSString *nickname; + NSString *username; + NSString *realname; + + NSMutableDictionary *channels; + NSStringEncoding encoding; +} + +@property (nonatomic, retain) NSMutableDictionary *channels; + +@end + +#pragma mark - IRCClientSession class implementation + +@implementation IRCClientSession + +#pragma mark - Property synthesis + +@synthesize delegate; +@synthesize sessionID; +@synthesize version; +@synthesize server; +@synthesize port; +@synthesize password; +@synthesize nickname; +@synthesize username; +@synthesize realname; +@synthesize encoding; + +#pragma mark - Custom accessors + +-(NSDictionary*)channels +{ + NSDictionary* channelsCopy = [channels copy]; + return channelsCopy; +} + +-(void)setChannels:(NSMutableDictionary *)newChannels +{ + channels = newChannels; +} + +- (bool)connected +{ + return irc_is_connected(irc_session); +} + +/************************************/ +#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_notice = onNotice; + callbacks.event_channel_notice = onChannelNotice; + 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); + + irc_set_ctx(irc_session, (__bridge void *)(self)); + + unsigned int high, low; + irc_get_version (&high, &low); + + version = [NSString stringWithFormat:@"IRCClient Framework v%s (Nathan Ollerenshaw) - libirc v%d.%d (Georgy Yunaev)", IRCCLIENTVERSION, high, low]; + + channels = [[NSMutableDictionary alloc] init]; + } + return self; +} + +-(void)dealloc +{ + if (irc_is_connected(irc_session)) + NSLog(@"Warning: IRC Session is not disconnected on dealloc"); + + irc_destroy_session(irc_session); +} + +- (int)connect; +{ + unsigned short sPort = port; + + return irc_connect(irc_session, server.UTF8String, sPort, (password.length > 0 ? password.bytes : NULL), nickname.UTF8String, username.UTF8String, realname.UTF8String); +} + +- (void)disconnect +{ + irc_disconnect(irc_session); +} + +- (void)startThread +{ + @autoreleasepool { + irc_run(irc_session); + } +} + +- (void)run +{ + if (thread) { + NSLog(@"Thread already running!"); + return; + } + + thread = [[NSThread alloc] initWithTarget:self selector:@selector(startThread) object:nil]; + [thread start]; +} + +/**************************/ +#pragma mark - IRC commands +/**************************/ + +- (int)sendRaw:(NSData *)message +{ + return irc_send_raw(irc_session, message.bytes); +} + +- (int)quit:(NSData *)reason +{ + return irc_cmd_quit(irc_session, reason.bytes); +} + +- (int)join:(NSData *)channel key:(NSData *)key +{ + NSLog(@"Joining %@", channel); + + if (!key || !key.length > 0) + return irc_cmd_join(irc_session, channel.bytes, NULL); + + return irc_cmd_join(irc_session, channel.bytes, key.bytes); +} + +- (int)list:(NSData *)channel +{ + return irc_cmd_list(irc_session, channel.bytes); +} + +- (int)userMode:(NSString *)mode +{ + return irc_cmd_user_mode(irc_session, mode.UTF8String); +} + +- (int)nick:(NSString *)newnick +{ + return irc_cmd_nick(irc_session, newnick.UTF8String); +} + +- (int)whois:(NSString *)nick +{ + return irc_cmd_whois(irc_session, nick.UTF8String); +} + +- (int)message:(NSData *)message to:(NSString *)target +{ + return irc_cmd_msg(irc_session, target.UTF8String, message.bytes); +} + +- (int)action:(NSData *)action to:(NSString *)target +{ + return irc_cmd_me(irc_session, target.UTF8String, action.bytes); +} + +- (int)notice:(NSData *)notice to:(NSString *)target +{ + return irc_cmd_notice(irc_session, target.UTF8String, notice.bytes); +} + +- (int)ctcpRequest:(NSData *)request target:(NSString *)target +{ + return irc_cmd_ctcp_request(irc_session, target.UTF8String, request.bytes); +} + +- (int)ctcpReply:(NSData *)reply target:(NSString *)target +{ + return irc_cmd_ctcp_reply(irc_session, target.UTF8String, reply.bytes); +} + +/****************************/ +#pragma mark - Event handlers +/****************************/ + +- (void)connectionSucceeded +{ + [delegate connectionSucceeded]; +} + +- (void)nickChangedFrom:(NSString *)oldNick to:(NSString *)newNick +{ + if ([nickname isEqualToString:oldNick]) + { + nickname = newNick; + [delegate nickChangedFrom:oldNick to:newNick own:YES]; + } + else + { + [delegate nickChangedFrom:oldNick to:newNick own:NO]; + } +} + +- (void)userQuit:(NSString *)nick withReason:(NSData *)reason +{ + NSString* reasonString; + + if(reason) + { + reasonString = [[NSString alloc] initWithData:reason encoding:encoding]; + } + + [delegate userQuit:nick withReason:reasonString]; +} + +- (void)userJoined:(NSString *)nick channel:(NSData *)channelName +{ + NSString* nickOnly = getNickFromNickUserHost(nick); + + if ([nickname isEqualToString:nickOnly]) + { + // We just joined a channel; allocate an IRCClientChannel object and send it + // to the main thread. + + IRCClientChannel* newChannel = [[IRCClientChannel alloc] initWithName:channelName andIRCSession:irc_session]; + channels[channelName] = newChannel; + [delegate joinedNewChannel:newChannel]; + } + else + { + // Someone joined a channel we're on. + + IRCClientChannel* channel = channels[channelName]; + [channel userJoined:nick]; + } +} + +- (void)userParted:(NSString *)nick channel:(NSData *)channelName withReason:(NSData *)reason +{ + IRCClientChannel* channel = channels[channelName]; + + NSString* nickOnly = getNickFromNickUserHost(nick); + + if ([nickname isEqualToString: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]; + } +} + +- (void)modeSet:(NSString* )mode withParams:(NSString *)params forChannel:(NSData *)channelName by:(NSString *)nick +{ + IRCClientChannel *channel = channels[channelName]; + + [channel modeSet:mode withParams:params by:nick]; +} + +- (void)modeSet:(NSString *)mode by:(NSString *)nick +{ + [delegate modeSet:mode by:nick]; +} + +- (void)topicSet:(NSData *)newTopic forChannel:(NSData *)channelName by:(NSString *)nick +{ + IRCClientChannel *channel = channels[channelName]; + + [channel topicSet:newTopic by:nick]; +} + +- (void)userKicked:(NSString *)nick fromChannel:(NSData *)channelName by:(NSString *)byNick withReason:(NSData *)reason +{ + IRCClientChannel* channel = channels[channelName]; + + 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]; + } +} + +- (void)messageSent:(NSData *)message toChannel:(NSData *)channelName byUser:(NSString *)nick +{ + IRCClientChannel *channel = channels[channelName]; + + [channel messageSent:message byUser:nick]; +} + +- (void)privateMessageReceived:(NSData *)message fromUser:(NSString *)nick +{ + NSString* messageString = [[NSString alloc] initWithData:message encoding:encoding]; + + [delegate privateMessageReceived:messageString fromUser:nick]; +} + +- (void)noticeSent:(NSData *)notice toChannel:(NSData *)channelName byUser:(NSString *)nick +{ + IRCClientChannel *channel = channels[channelName]; + + [channel noticeSent:notice byUser:nick]; +} + +- (void)privateNoticeReceived:(NSData *)notice fromUser:(NSString *)nick +{ + NSString* noticeString = [[NSString alloc] initWithData:notice encoding:encoding]; + + [delegate privateNoticeReceived:noticeString fromUser:nick]; +} + +- (void)invitedToChannel:(NSData *)channelName by:(NSString *)nick +{ + [delegate invitedToChannel:channelName by:nick]; +} + +- (void)CTCPRequestReceived:(NSData *)request fromUser:(NSString *)nick +{ + const char* the_nick = getNickFromNickUserHost(nick).UTF8String; + 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")) + { + irc_cmd_ctcp_reply (irc_session, the_nick, [NSString stringWithFormat:@"VERSION %@", version].UTF8String); + } + else if (!strcmp (the_request, "FINGER")) + { + irc_cmd_ctcp_reply (irc_session, the_nick, [NSString stringWithFormat:@"FINGER %@ (%@) Idle 0 seconds", username, realname].UTF8String); + } + else if (!strcmp (the_request, "TIME")) + { + irc_cmd_ctcp_reply(irc_session, the_nick, [[NSDate dateWithTimeIntervalSinceNow:0] descriptionWithCalendarFormat:@"TIME %a %b %e %H:%M:%S %Z %Y" timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]].UTF8String); + } + else + { + if ([delegate respondsToSelector:@selector(CTCPRequestReceived:ofType:fromUser:)]) + { + char* request_string = malloc(request.length); + [request getBytes:request_string length:request.length]; + + char* request_type = strtok(request_string, " "); + char* request_body = strtok(NULL, " " ); + + [delegate CTCPRequestReceived:[NSData dataWithBytes:request_body length:strlen(request_body)+1] ofType:[NSData dataWithBytes:request_type length:strlen(request_type)+1] fromUser:nick]; + } + } +} + +- (void)CTCPReplyReceived:(NSData *)reply fromUser:(NSString *)nick +{ + [delegate CTCPReplyReceived:reply fromUser:nick]; +} + +- (void)CTCPActionPerformed:(NSData *)action byUser:(NSString *)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 + NSString* actionString = [[NSString alloc] initWithData:action encoding:encoding]; + [delegate privateCTCPActionReceived:actionString fromUser:nick]; + } +} + +- (void)unknownEventReceived:(NSData *)event from:(NSString *)origin params:(NSArray *)params +{ + [delegate unknownEventReceived:event from:origin params:params]; +} + +-(void)numericEventReceived:(NSUInteger)event from:(NSString *)origin params:(NSArray *)params +{ + [delegate numericEventReceived:event from:origin params:params]; +} + +@end + +#pragma mark - Useful helper functions + +NSString* getNickFromNickUserHost(NSString *nuh) +{ + NSArray *nuhArray = [nuh componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; + + if (nuhArray.count == 3) + { + return [NSString stringWithString:nuhArray[0]]; + } + else + { + return [NSString stringWithString:nuh]; + } +} + +NSString* getUserFromNickUserHost(NSString *nuh) +{ + NSArray *nuhArray = [nuh componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; + + if (nuhArray.count == 3) + { + return [NSString stringWithString:nuhArray[1]]; + } + else + { + return nil; + } +} + +NSString* getHostFromNickUserHost(NSString *nuh) +{ + NSArray *nuhArray = [nuh componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; + + if (nuhArray.count == 3) + { + return [NSString stringWithString:nuhArray[2]]; + } + else + { + return nil; + } +} + +/***********************************************/ +#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); + NSString *oldNick = @(origin); + NSString *newNick = @(params[0]); + + [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); + + NSString *nick = @(origin); + NSData *reason = nil; + if(count > 0) + { + reason = [[NSData alloc] initWithBytes:params[0] length:strlen(params[0])]; + } + + [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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + + [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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSData *reason = nil; + + if (count > 1) + { + reason = [NSData dataWithBytes:params[1] length:strlen(params[1])+1]; + } + + [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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSString *mode = @(params[1]); + NSString *modeParams = nil; + + if (count > 2) + { + modeParams = @(params[2]); + } + + [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); + NSString* nick = @(origin); + NSString *mode = @(params[0]); + + [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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSData *topic = nil; + + if (count > 1) + { + topic = [NSData dataWithBytes:params[1] length:strlen(params[1])]; + } + + [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); + NSString *byNick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSString *nick = nil; + NSData *reason = nil; + + if (count > 1) + { + nick = @(params[1]); + } + + if (count > 2) + { + reason = [NSData dataWithBytes:params[2] length:strlen(params[2])]; + } + + [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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSData *message = nil; + + if (count > 1) + { + message = [NSData dataWithBytes:params[1] length:strlen(params[1])]; + } + + NSLog(@"onChannelPrvmsg"); + [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); + NSString *nick = @(origin); + NSData *message = nil; + + if (count > 1) + { + message = [NSData dataWithBytes:params[1] length:strlen(params[1])]; + } + + [clientSession privateMessageReceived:message 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 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); + NSString *nick = @(origin); + NSData *notice = nil; + + if (count > 1) + { + notice = [NSData dataWithBytes:params[1] length:strlen(params[1])]; + } + + [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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSData *notice = nil; + + if (count > 1) + { + notice = [NSData dataWithBytes:params[1] length:strlen(params[1])]; + } + + [clientSession noticeSent:notice toChannel:channelName byUser:nick]; +} + +/*! + * 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); + NSString *nick = @(origin); + NSData *channelName = [NSData dataWithBytes:params[1] length:strlen(params[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); + NSString *nick = @(origin); + NSData* request = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + + [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); + + NSString *nick = @(origin); + NSData *reply = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + + [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); + + NSString *nick = @(origin); + NSData *target = [NSData dataWithBytes:params[0] length:strlen(params[0])]; + NSData *action = [NSData dataWithBytes:params[1] length:strlen(params[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 *eventString = [NSData dataWithBytes:event length:strlen(event)]; + NSString *sender = nil; + + if (origin != NULL) + sender = @(origin); + + NSMutableArray *paramsArray = [[NSMutableArray alloc] init]; + + for (unsigned int i = 0; i < count; i++) + [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i])]]; + + [clientSession unknownEventReceived:eventString from:sender params:[paramsArray copy]]; +} + +/*! + * 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. + */ +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; + NSString *sender = @(origin); + + NSMutableArray *paramsArray = [[NSMutableArray alloc] init]; + + for (unsigned int i = 0; i < count; i++) + [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i])]]; + + [clientSession numericEventReceived:eventNumber from:sender params:[paramsArray copy]]; +} diff --git a/IRCClient/IRCClientSessionDelegate.h b/IRCClient/IRCClientSessionDelegate.h new file mode 100644 index 0000000..d60881d --- /dev/null +++ b/IRCClient/IRCClientSessionDelegate.h @@ -0,0 +1,152 @@ +/* + * Modified IRCClient Copyright 2015 Said Achmiz (www.saidachmiz.net) + * + * Original IRCClient Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net + * + * This library 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 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#import +#include "libircclient.h" + +/** + * @file IRCClientSessionDelegate.h + * @author Nathan Ollerenshaw + * @version 1.0 + * @date 01.2009 + * @brief Receives delegate messages from an IRCClientSession. + * @protocol IRCClientSessionDelegate + */ + +@class IRCClientChannel; + +/** @brief Receives delegate messages from an IRCClientSession. + * + * Each IRCClientSession object needs a single delegate. Methods are called + * for each event that occurs on an IRC server that the client is connected to. + * + * Note that for any given parameter, it may be optional, in which case a nil + * object may be supplied instead of the given parameter. + */ + +@protocol IRCClientSessionDelegate + +/** The client has successfully connected to the IRC server. */ +- (void)connectionSucceeded; + +/** An IRC client on a channel that this client is connected to has changed nickname, + * or this IRC client has changed nicknames. + * + * @param nick the new nickname + * @param oldNick the old nickname + * @param wasItUs did our nick change, or someone else's? + */ +- (void)nickChangedFrom:(NSString *)oldNick to:(NSString *)newNick own:(BOOL)wasItUs; + +/** An IRC client on a channel that this client is connected to has quit IRC. + * + * @param nick the nickname of the client that quit. + * @param reason (optional) the quit message, if any. + */ +- (void)userQuit:(NSString *)nick withReason:(NSString *)reason; + +/** The IRC client has joined (connected) successfully to a new channel. This + * event creates an IRCClientChannel object, which you are expected to assign a + * delegate to, to handle events from the channel. + * + * For example, on receipt of this message, a graphical IRC client would most + * likely open a new window, create an IRCClientChannelDelegate for the window, + * set the new IRCClientChannel's delegate to the new delegate, and then hook + * it up so that new events sent to the IRCClientChannelDelegate are sent to + * the window. + * + * @param channel the IRCClientChannel object for the newly joined channel. + */ +- (void)joinedNewChannel:(IRCClientChannel *)channel; + +/** The client has changed it's user mode. + * + * @param mode the new mode. + */ +- (void)modeSet:(NSString *)mode by:(NSString *)nick; + +/** The client has received a private PRIVMSG from another IRC client. + * + * @param message the text of the message + * @param nick the other IRC Client that sent the message. + */ +- (void)privateMessageReceived:(NSString *)message fromUser:(NSString *)nick; + +/** The client has received a private NOTICE from another client. + * + * @param notice the text of the message + * @param nick the nickname of the other IRC client that sent the message. + */ +- (void)privateNoticeReceived:(NSString *)notice fromUser:(NSString *)nick; + +/** The IRC client has been invited to a channel. + * + * Note that the name is provided as an NSData object, because we have no idea + * what encoding the channel name is in (it needn't be the same as used by e.g. + * server messages, etc.), and it's important to attempt to join *exactly* the + * channel you're being invited to, byte for byte, otherwise you might end up + * joining the wrong channel. You should convert the name to an NSString for + * display (using the encoding set for the server is probably a sane default) + * but be aware that you might be displaying entirely wrong characters and are + * not gauranteed that the channel name will look right - only that it will be + * actually be the right name, internally. + * + * @param channel the channel for the invitation. + * @param nick the nickname of the user that sent the invitation. + */ +- (void)invitedToChannel:(NSData *)channelName by:(NSString *)nick; + +/** A private CTCP request was sent to the IRC client. + * + * @param request the CTCP request string (after the type) + * @param type the CTCP request type + * @param nick the nickname of the user that sent the request. + */ +- (void)CTCPRequestReceived:(NSData *)request ofType:(NSData *)type fromUser:(NSString *)nick; + +/** A private CTCP reply was sent to the IRC client. + * + * @param reply an NSData containing the raw C string of the reply. + * @param nick the nickname of the user that sent the reply. + */ +- (void)CTCPReplyReceived:(NSData *)reply fromUser:(NSString *)nick; + +/** A private CTCP ACTION was sent to the IRC client. + * + * CTCP ACTION is not limited to channels; it may also be sent directly to other users. + * + * @param action the action message text. + * @param nick the nickname of the client that sent the action. + */ +- (void)privateCTCPActionReceived:(NSString *)action fromUser:(NSString *)nick; + +/** An unhandled event was received from the IRC server. + * + * @param event the unknown event name + * @param origin the sender of the event + * @param params an NSArray of NSData objects that are the raw C strings of the event. + */ +- (void)unknownEventReceived:(NSData *)event from:(NSString *)origin params:(NSArray *)params; + +/** An unhandled numeric was received from the IRC server + * + * @param event the unknown event number + * @param origin the sender of the event + * @param params an NSArray of NSData objects that are the raw C strings of the event. + */ +- (void)numericEventReceived:(NSUInteger)event from:(NSString *)origin params:(NSArray *)params; + +@end diff --git a/IRCClient/Info.plist b/IRCClient/Info.plist index e68f2f1..da3b6d3 100644 --- a/IRCClient/Info.plist +++ b/IRCClient/Info.plist @@ -21,7 +21,7 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright - Copyright © 2015 Said Achmiz. All rights reserved. + Copyright © 2015 Said Achmiz. NSPrincipalClass