IRC client framework (wrapper around libircclient library).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IRCClientSession.m 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. //
  2. // IRCClientSession.m
  3. // IRCClient
  4. /*
  5. * Copyright 2015 Said Achmiz (www.saidachmiz.net)
  6. *
  7. * Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or (at your
  12. * option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. */
  19. #pragma mark Defines and includes
  20. #define IRCCLIENTVERSION "2.0a3"
  21. #import "IRCClientSession.h"
  22. #import "IRCClientChannel.h"
  23. #import "IRCClientChannel_Private.h"
  24. #import "NSData+SA_NSDataExtensions.h"
  25. #pragma mark - Callback function declarations
  26. static void onConnect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  27. static void onNick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  28. static void onQuit(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  29. static void onJoinChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  30. static void onPartChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  31. static void onMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  32. static void onUserMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  33. static void onTopic(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  34. static void onKick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  35. static void onChannelPrvmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  36. static void onPrivmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  37. static void onServerMsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  38. static void onNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  39. static void onChannelNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  40. static void onServerNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  41. static void onInvite(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  42. static void onCtcpRequest(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  43. static void onCtcpReply(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  44. static void onCtcpAction(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  45. static void onUnknownEvent(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count);
  46. static void onNumericEvent(irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count);
  47. #pragma mark - IRCClientSession private category declaration
  48. static NSDictionary* ircNumericCodeList;
  49. @interface IRCClientSession()
  50. {
  51. irc_callbacks_t _callbacks;
  52. irc_session_t *_irc_session;
  53. NSThread *_thread;
  54. NSMutableDictionary *_channels;
  55. }
  56. @property (readwrite) NSMutableDictionary *channels;
  57. +(void)loadNumericCodes;
  58. @end
  59. #pragma mark - IRCClientSession class implementation
  60. @implementation IRCClientSession
  61. #pragma mark - Property synthesis
  62. @synthesize delegate = _delegate;
  63. @synthesize sessionID = _sessionID;
  64. @synthesize version = _version;
  65. @synthesize server = _server;
  66. @synthesize port = _port;
  67. @synthesize password = _password;
  68. @synthesize nickname = _nickname;
  69. @synthesize username = _username;
  70. @synthesize realname = _realname;
  71. @synthesize encoding = _encoding;
  72. #pragma mark - Custom accessors
  73. -(NSDictionary*)channels
  74. {
  75. NSDictionary* channelsCopy = [_channels copy];
  76. return channelsCopy;
  77. }
  78. -(void)setChannels:(NSDictionary *)channels
  79. {
  80. _channels = [channels mutableCopy];
  81. }
  82. - (bool)isConnected
  83. {
  84. return irc_is_connected(_irc_session);
  85. }
  86. /************************************/
  87. #pragma mark - Class methods
  88. /************************************/
  89. -(instancetype)init
  90. {
  91. if ((self = [super init])) {
  92. _callbacks.event_connect = onConnect;
  93. _callbacks.event_nick = onNick;
  94. _callbacks.event_quit = onQuit;
  95. _callbacks.event_join = onJoinChannel;
  96. _callbacks.event_part = onPartChannel;
  97. _callbacks.event_mode = onMode;
  98. _callbacks.event_umode = onUserMode;
  99. _callbacks.event_topic = onTopic;
  100. _callbacks.event_kick = onKick;
  101. _callbacks.event_channel = onChannelPrvmsg;
  102. _callbacks.event_privmsg = onPrivmsg;
  103. _callbacks.event_server_msg = onServerMsg;
  104. _callbacks.event_notice = onNotice;
  105. _callbacks.event_channel_notice = onChannelNotice;
  106. _callbacks.event_server_notice = onServerNotice;
  107. _callbacks.event_invite = onInvite;
  108. _callbacks.event_ctcp_req = onCtcpRequest;
  109. _callbacks.event_ctcp_rep = onCtcpReply;
  110. _callbacks.event_ctcp_action = onCtcpAction;
  111. _callbacks.event_unknown = onUnknownEvent;
  112. _callbacks.event_numeric = onNumericEvent;
  113. _callbacks.event_dcc_chat_req = NULL;
  114. _callbacks.event_dcc_send_req = NULL;
  115. _irc_session = irc_create_session(&_callbacks);
  116. if (!_irc_session) {
  117. NSLog(@"Could not create irc_session.");
  118. return nil;
  119. }
  120. // Strip server info from nicks.
  121. // irc_option_set(_irc_session, LIBIRC_OPTION_STRIPNICKS);
  122. // Set debug mode.
  123. // irc_option_set(_irc_session, LIBIRC_OPTION_DEBUG);
  124. irc_set_ctx(_irc_session, (__bridge void *)(self));
  125. unsigned int high, low;
  126. irc_get_version (&high, &low);
  127. NSString* versionString = [NSString stringWithFormat:@"IRCClient Framework v%s (Said Achmiz) - libirc v%d.%d (George Yunaev)", IRCCLIENTVERSION, high, low];
  128. _version = [NSData dataWithBytes:versionString.UTF8String length:versionString.length];
  129. _channels = [[NSMutableDictionary alloc] init];
  130. _encoding = NSUTF8StringEncoding;
  131. }
  132. return self;
  133. }
  134. -(void)dealloc
  135. {
  136. if (irc_is_connected(_irc_session))
  137. NSLog(@"Warning: IRC Session is not disconnected on dealloc");
  138. irc_destroy_session(_irc_session);
  139. }
  140. +(NSDictionary *)ircNumericCodes
  141. {
  142. if(ircNumericCodeList == nil)
  143. {
  144. [IRCClientSession loadNumericCodes];
  145. }
  146. return ircNumericCodeList;
  147. }
  148. +(void)loadNumericCodes
  149. {
  150. NSString* numericCodeListPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"IRC_Numerics" ofType:@"plist"];
  151. ircNumericCodeList = [NSDictionary dictionaryWithContentsOfFile:numericCodeListPath];
  152. if(ircNumericCodeList)
  153. {
  154. NSLog(@"IRC numeric codes list loaded successfully.\n");
  155. // NSLog(@"%@", ircNumericCodeList);
  156. }
  157. else
  158. {
  159. NSLog(@"Could not load IRC numeric codes list!\n");
  160. }
  161. }
  162. - (int)connect;
  163. {
  164. 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);
  165. }
  166. - (void)disconnect
  167. {
  168. irc_disconnect(_irc_session);
  169. }
  170. - (void)startThread
  171. {
  172. @autoreleasepool {
  173. irc_run(_irc_session);
  174. }
  175. }
  176. - (void)run
  177. {
  178. if (_thread) {
  179. NSLog(@"Thread already running!");
  180. return;
  181. }
  182. _thread = [[NSThread alloc] initWithTarget:self selector:@selector(startThread) object:nil];
  183. [_thread start];
  184. }
  185. -(int)setNickname:(NSData *)nickname username:(NSData *)username realname:(NSData *)realname
  186. {
  187. if(self.isConnected)
  188. {
  189. return 0;
  190. }
  191. else
  192. {
  193. _nickname = nickname.SA_dataWithTerminatedCString;
  194. _username = username.SA_dataWithTerminatedCString;
  195. _realname = realname.SA_dataWithTerminatedCString;
  196. return 1;
  197. }
  198. }
  199. /**************************/
  200. #pragma mark - IRC commands
  201. /**************************/
  202. - (int)sendRaw:(NSData *)message
  203. {
  204. return irc_send_raw(_irc_session, message.SA_terminatedCString);
  205. }
  206. - (int)quit:(NSData *)reason
  207. {
  208. return irc_cmd_quit(_irc_session, reason.SA_terminatedCString);
  209. }
  210. - (int)join:(NSData *)channel key:(NSData *)key
  211. {
  212. if (!key || !key.length > 0)
  213. {
  214. return irc_cmd_join(_irc_session, channel.SA_terminatedCString, NULL);
  215. }
  216. return irc_cmd_join(_irc_session, channel.SA_terminatedCString, key.SA_terminatedCString);
  217. }
  218. - (int)list:(NSData *)channel
  219. {
  220. return irc_cmd_list(_irc_session, channel.SA_terminatedCString);
  221. }
  222. - (int)userMode:(NSData *)mode
  223. {
  224. return irc_cmd_user_mode(_irc_session, mode.SA_terminatedCString);
  225. }
  226. - (int)nick:(NSData *)newnick
  227. {
  228. return irc_cmd_nick(_irc_session, newnick.SA_terminatedCString);
  229. }
  230. - (int)whois:(NSData *)nick
  231. {
  232. return irc_cmd_whois(_irc_session, nick.SA_terminatedCString);
  233. }
  234. - (int)message:(NSData *)message to:(NSData *)target
  235. {
  236. return irc_cmd_msg(_irc_session, target.SA_terminatedCString, message.SA_terminatedCString);
  237. }
  238. - (int)action:(NSData *)action to:(NSData *)target
  239. {
  240. return irc_cmd_me(_irc_session, target.SA_terminatedCString, action.SA_terminatedCString);
  241. }
  242. - (int)notice:(NSData *)notice to:(NSData *)target
  243. {
  244. return irc_cmd_notice(_irc_session, target.SA_terminatedCString, notice.SA_terminatedCString);
  245. }
  246. - (int)ctcpRequest:(NSData *)request target:(NSData *)target
  247. {
  248. return irc_cmd_ctcp_request(_irc_session, target.SA_terminatedCString, request.SA_terminatedCString);
  249. }
  250. - (int)ctcpReply:(NSData *)reply target:(NSData *)target
  251. {
  252. return irc_cmd_ctcp_reply(_irc_session, target.SA_terminatedCString, reply.SA_terminatedCString);
  253. }
  254. /****************************/
  255. #pragma mark - Event handlers
  256. /****************************/
  257. - (void)connectionSucceeded
  258. {
  259. [_delegate connectionSucceeded];
  260. }
  261. - (void)nickChangedFrom:(NSData *)oldNick to:(NSData *)newNick
  262. {
  263. if ([_nickname isEqualToData:oldNick])
  264. {
  265. _nickname = newNick;
  266. [_delegate nickChangedFrom:oldNick to:newNick own:YES];
  267. }
  268. else
  269. {
  270. [_delegate nickChangedFrom:oldNick to:newNick own:NO];
  271. }
  272. }
  273. - (void)userQuit:(NSData *)nick withReason:(NSData *)reason
  274. {
  275. [_delegate userQuit:nick withReason:reason];
  276. }
  277. - (void)userJoined:(NSData *)nick channel:(NSData *)channelName
  278. {
  279. NSData* nickOnly = getNickFromNickUserHost(nick);
  280. if ([_nickname isEqualToData:nickOnly])
  281. {
  282. // We just joined a channel; allocate an IRCClientChannel object and send it
  283. // to the main thread.
  284. IRCClientChannel* newChannel = [[IRCClientChannel alloc] initWithName:channelName andIRCSession:_irc_session];
  285. _channels[channelName] = newChannel;
  286. [_delegate joinedNewChannel:newChannel];
  287. }
  288. else
  289. {
  290. // Someone joined a channel we're on.
  291. IRCClientChannel* channel = _channels[channelName];
  292. [channel userJoined:nick];
  293. }
  294. }
  295. - (void)userParted:(NSData *)nick channel:(NSData *)channelName withReason:(NSData *)reason
  296. {
  297. IRCClientChannel* channel = _channels[channelName];
  298. NSData* nickOnly = getNickFromNickUserHost(nick);
  299. if ([_nickname isEqualToData:nickOnly])
  300. {
  301. // We just left a channel; remove it from the channels dict.
  302. [_channels removeObjectForKey:channelName];
  303. [channel userParted:nick withReason:reason us:YES];
  304. }
  305. else
  306. {
  307. [channel userParted:nick withReason:reason us:NO];
  308. }
  309. }
  310. - (void)modeSet:(NSData* )mode withParams:(NSData *)params forChannel:(NSData *)channelName by:(NSData *)nick
  311. {
  312. IRCClientChannel *channel = _channels[channelName];
  313. [channel modeSet:mode withParams:params by:nick];
  314. }
  315. - (void)modeSet:(NSData *)mode by:(NSData *)nick
  316. {
  317. [_delegate modeSet:mode by:nick];
  318. }
  319. - (void)topicSet:(NSData *)newTopic forChannel:(NSData *)channelName by:(NSData *)nick
  320. {
  321. IRCClientChannel *channel = _channels[channelName];
  322. [channel topicSet:newTopic by:nick];
  323. }
  324. - (void)userKicked:(NSData *)nick fromChannel:(NSData *)channelName by:(NSData *)byNick withReason:(NSData *)reason
  325. {
  326. IRCClientChannel* channel = _channels[channelName];
  327. if (nick == nil)
  328. {
  329. // we got kicked from a channel we're on
  330. [_channels removeObjectForKey:channelName];
  331. [channel userKicked:_nickname withReason:reason by:byNick us:YES];
  332. }
  333. else
  334. {
  335. // someone else got booted from a channel we're on
  336. [channel userKicked:nick withReason:reason by:byNick us:NO];
  337. }
  338. }
  339. - (void)messageSent:(NSData *)message toChannel:(NSData *)channelName byUser:(NSData *)nick
  340. {
  341. IRCClientChannel *channel = _channels[channelName];
  342. [channel messageSent:message byUser:nick];
  343. }
  344. - (void)privateMessageReceived:(NSData *)message fromUser:(NSData *)nick
  345. {
  346. [_delegate privateMessageReceived:message fromUser:nick];
  347. }
  348. - (void)serverMessageReceivedFrom:(NSData *)origin params:(NSArray *)params
  349. {
  350. [_delegate serverMessageReceivedFrom:origin params:params];
  351. }
  352. - (void)privateNoticeReceived:(NSData *)notice fromUser:(NSData *)nick
  353. {
  354. [_delegate privateNoticeReceived:notice fromUser:nick];
  355. }
  356. - (void)noticeSent:(NSData *)notice toChannel:(NSData *)channelName byUser:(NSData *)nick
  357. {
  358. IRCClientChannel *channel = _channels[channelName];
  359. [channel noticeSent:notice byUser:nick];
  360. }
  361. - (void)serverNoticeReceivedFrom:(NSData *)origin params:(NSArray *)params
  362. {
  363. [_delegate serverNoticeReceivedFrom:origin params:params];
  364. }
  365. - (void)invitedToChannel:(NSData *)channelName by:(NSData *)nick
  366. {
  367. [_delegate invitedToChannel:channelName by:nick];
  368. }
  369. - (void)CTCPRequestReceived:(NSData *)request fromUser:(NSData *)nick
  370. {
  371. const char* the_nick = getNickFromNickUserHost(nick).bytes;
  372. const char* the_request = request.bytes;
  373. if (strstr(the_request, "PING") == the_request)
  374. {
  375. irc_cmd_ctcp_reply(_irc_session, the_nick, the_request);
  376. }
  377. else if (!strcmp (the_request, "VERSION"))
  378. {
  379. NSMutableData* versionReply = [NSMutableData dataWithLength:8 + _version.length];
  380. sprintf(versionReply.mutableBytes, "VERSION %s", _version.bytes);
  381. irc_cmd_ctcp_reply (_irc_session, the_nick, versionReply.bytes);
  382. }
  383. else if (!strcmp (the_request, "FINGER"))
  384. {
  385. NSMutableData* fingerReply = [NSMutableData dataWithLength:25 + _username.length + _realname.length];
  386. sprintf(fingerReply.mutableBytes, "FINGER %s (%s) Idle 0 seconds)", _username.bytes, _realname.bytes);
  387. irc_cmd_ctcp_reply (_irc_session, the_nick, fingerReply.bytes);
  388. }
  389. else if (!strcmp (the_request, "TIME"))
  390. {
  391. 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]] cStringUsingEncoding:_encoding]);
  392. }
  393. else
  394. {
  395. if ([_delegate respondsToSelector:@selector(CTCPRequestReceived:ofType:fromUser:)])
  396. {
  397. char* request_string = malloc(request.length);
  398. [request getBytes:request_string length:request.length];
  399. char* request_type = strtok(request_string, " ");
  400. char* request_body = strtok(NULL, " " );
  401. NSData* requestTypeData = [NSData dataWithBytes:request_body length:strlen(request_body) + 1];
  402. NSData* requestBodyData = [NSData dataWithBytes:request_type length:strlen(request_type) + 1];
  403. [_delegate CTCPRequestReceived:requestBodyData ofType:requestTypeData fromUser:nick];
  404. free(request_string);
  405. }
  406. }
  407. }
  408. - (void)CTCPReplyReceived:(NSData *)reply fromUser:(NSData *)nick
  409. {
  410. [_delegate CTCPReplyReceived:reply fromUser:nick];
  411. }
  412. - (void)CTCPActionPerformed:(NSData *)action byUser:(NSData *)nick atTarget:(NSData *)target
  413. {
  414. IRCClientChannel* channel = _channels[target];
  415. if(channel != nil)
  416. {
  417. // An action on a channel we're on
  418. [channel actionPerformed:action byUser:nick];
  419. }
  420. else
  421. {
  422. // An action in a private message
  423. [_delegate privateCTCPActionReceived:action fromUser:nick];
  424. }
  425. }
  426. - (void)unknownEventReceived:(NSData *)event from:(NSData *)origin params:(NSArray *)params
  427. {
  428. [_delegate unknownEventReceived:event from:origin params:params];
  429. }
  430. -(void)numericEventReceived:(NSUInteger)event from:(NSData *)origin params:(NSArray *)params
  431. {
  432. [_delegate numericEventReceived:event from:origin params:params];
  433. }
  434. @end
  435. /*************************************/
  436. #pragma mark - Useful helper functions
  437. /*************************************/
  438. NSData* getNickFromNickUserHost(NSData *nuh)
  439. {
  440. char* nick_user_host_buf = malloc(nuh.length);
  441. [nuh getBytes:nick_user_host_buf];
  442. char *nick_buf;
  443. nick_buf = strtok(nick_user_host_buf, "!@");
  444. NSData* nick = (nick_buf != NULL) ? [NSData dataWithBytes:nick_buf length:strlen(nick_buf) + 1] : nil;
  445. free(nick_user_host_buf);
  446. return nick;
  447. }
  448. NSData* getUserFromNickUserHost(NSData *nuh)
  449. {
  450. char* nick_user_host_buf = malloc(nuh.length);
  451. [nuh getBytes:nick_user_host_buf];
  452. char *nick_buf, *user_buf;
  453. nick_buf = strtok(nick_user_host_buf, "!@");
  454. user_buf = strtok(NULL, "!@");
  455. NSData* user = (user_buf != NULL) ? [NSData dataWithBytes:user_buf length:strlen(user_buf) + 1] : nil;
  456. free(nick_user_host_buf);
  457. return user;
  458. }
  459. NSData* getHostFromNickUserHost(NSData *nuh)
  460. {
  461. char* nick_user_host_buf = malloc(nuh.length);
  462. [nuh getBytes:nick_user_host_buf];
  463. char *nick_buf, *user_buf, *host_buf;
  464. nick_buf = strtok(nick_user_host_buf, "!@");
  465. user_buf = strtok(NULL, "!@");
  466. host_buf = strtok(NULL, "!@");
  467. NSData* host = (host_buf != NULL) ? [NSData dataWithBytes:host_buf length:strlen(host_buf) + 1] : nil;
  468. free(nick_user_host_buf);
  469. return host;
  470. }
  471. /***********************************************/
  472. #pragma mark - Callback function implementations
  473. /***********************************************/
  474. /*!
  475. * The "on_connect" event is triggered when the client successfully
  476. * connects to the server, and could send commands to the server.
  477. * No extra params supplied; \a params is 0.
  478. */
  479. static void onConnect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  480. {
  481. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  482. [clientSession connectionSucceeded];
  483. }
  484. /*!
  485. * The "nick" event is triggered when the client receives a NICK message,
  486. * meaning that someone (including you) on a channel with the client has
  487. * changed their nickname.
  488. *
  489. * \param origin the person, who changes the nick. Note that it can be you!
  490. * \param params[0] mandatory, contains the new nick.
  491. */
  492. static void onNick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  493. {
  494. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  495. NSData *oldNick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  496. NSData *newNick = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  497. [clientSession nickChangedFrom:oldNick to:newNick];
  498. }
  499. /*!
  500. * The "quit" event is triggered upon receipt of a QUIT message, which
  501. * means that someone on a channel with the client has disconnected.
  502. *
  503. * \param origin the person, who is disconnected
  504. * \param params[0] optional, contains the reason message (user-specified).
  505. */
  506. static void onQuit(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  507. {
  508. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  509. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  510. NSData *reason = (count > 0) ? [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1] : [NSData dataWithBytes:"\0" length:1];
  511. [clientSession userQuit:nick withReason:reason];
  512. }
  513. /*!
  514. * The "join" event is triggered upon receipt of a JOIN message, which
  515. * means that someone has entered a channel that the client is on.
  516. *
  517. * \param origin the person, who joins the channel. By comparing it with
  518. * your own nickname, you can check whether your JOIN
  519. * command succeed.
  520. * \param params[0] mandatory, contains the channel name.
  521. */
  522. static void onJoinChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  523. {
  524. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  525. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  526. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  527. [clientSession userJoined:nick channel:channelName];
  528. }
  529. /*!
  530. * The "part" event is triggered upon receipt of a PART message, which
  531. * means that someone has left a channel that the client is on.
  532. *
  533. * \param origin the person, who leaves the channel. By comparing it with
  534. * your own nickname, you can check whether your PART
  535. * command succeed.
  536. * \param params[0] mandatory, contains the channel name.
  537. * \param params[1] optional, contains the reason message (user-defined).
  538. */
  539. static void onPartChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  540. {
  541. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  542. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  543. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  544. NSData *reason = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBytes:"\0" length:1];
  545. [clientSession userParted:nick channel:channelName withReason:reason];
  546. }
  547. /*!
  548. * The "mode" event is triggered upon receipt of a channel MODE message,
  549. * which means that someone on a channel with the client has changed the
  550. * channel's parameters.
  551. *
  552. * \param origin the person, who changed the channel mode.
  553. * \param params[0] mandatory, contains the channel name.
  554. * \param params[1] mandatory, contains the changed channel mode, like
  555. * '+t', '-i' and so on.
  556. * \param params[2] optional, contains the mode argument (for example, a
  557. * key for +k mode, or user who got the channel operator status for
  558. * +o mode)
  559. */
  560. static void onMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  561. {
  562. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  563. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  564. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  565. NSData *mode = [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1];
  566. NSData *modeParams = (count > 2) ? [NSData dataWithBytes:params[2] length:strlen(params[2]) + 1] : [NSData dataWithBytes:"\0" length:1];
  567. [clientSession modeSet:mode withParams:modeParams forChannel:channelName by:nick];
  568. }
  569. /*!
  570. * The "umode" event is triggered upon receipt of a user MODE message,
  571. * which means that your user mode has been changed.
  572. *
  573. * \param origin the person, who changed the channel mode.
  574. * \param params[0] mandatory, contains the user changed mode, like
  575. * '+t', '-i' and so on.
  576. */
  577. static void onUserMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  578. {
  579. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  580. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  581. NSData *mode = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  582. [clientSession modeSet:mode by:nick];
  583. }
  584. /*!
  585. * The "topic" event is triggered upon receipt of a TOPIC message, which
  586. * means that someone on a channel with the client has changed the
  587. * channel's topic.
  588. *
  589. * \param origin the person, who changes the channel topic.
  590. * \param params[0] mandatory, contains the channel name.
  591. * \param params[1] optional, contains the new topic.
  592. */
  593. static void onTopic(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  594. {
  595. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  596. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  597. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  598. NSData *topic = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBytes:"\0" length:1];
  599. [clientSession topicSet:topic forChannel:channelName by:nick];
  600. }
  601. /*!
  602. * The "kick" event is triggered upon receipt of a KICK message, which
  603. * means that someone on a channel with the client (or possibly the
  604. * client itself!) has been forcibly ejected.
  605. *
  606. * \param origin the person, who kicked the poor.
  607. * \param params[0] mandatory, contains the channel name.
  608. * \param params[1] optional, contains the nick of kicked person.
  609. * \param params[2] optional, contains the kick text
  610. */
  611. static void onKick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  612. {
  613. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  614. NSData *byNick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  615. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  616. NSData *nick = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : nil;
  617. NSData *reason = (count > 2) ? [NSData dataWithBytes:params[2] length:strlen(params[2]) + 1] : [NSData dataWithBytes:"\0" length:1];
  618. [clientSession userKicked:nick fromChannel:channelName by:byNick withReason:reason];
  619. }
  620. /*!
  621. * The "channel" event is triggered upon receipt of a PRIVMSG message
  622. * to an entire channel, which means that someone on a channel with
  623. * the client has said something aloud. Your own messages don't trigger
  624. * PRIVMSG event.
  625. *
  626. * \param origin the person, who generates the message.
  627. * \param params[0] mandatory, contains the channel name.
  628. * \param params[1] optional, contains the message text
  629. */
  630. static void onChannelPrvmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  631. {
  632. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  633. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  634. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  635. NSData *message = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBytes:"\0" length:1];
  636. [clientSession messageSent:message toChannel:channelName byUser:nick];
  637. }
  638. /*!
  639. * The "privmsg" event is triggered upon receipt of a PRIVMSG message
  640. * which is addressed to one or more clients, which means that someone
  641. * is sending the client a private message.
  642. *
  643. * \param origin the person, who generates the message.
  644. * \param params[0] mandatory, contains your nick.
  645. * \param params[1] optional, contains the message text
  646. */
  647. static void onPrivmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  648. {
  649. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  650. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  651. NSData *message = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBytes:"\0" length:1];
  652. [clientSession privateMessageReceived:message fromUser:nick];
  653. }
  654. /*!
  655. * The "privmsg" event is triggered upon receipt of a PRIVMSG message
  656. * which is addressed to no one in particular, but it sent to the client
  657. * anyway.
  658. *
  659. * \param origin the person, who generates the message.
  660. * \param params optional, contains who knows what.
  661. */
  662. static void onServerMsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  663. {
  664. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  665. NSData *sender = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  666. NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count];
  667. for (unsigned int i = 0; i < count; i++)
  668. {
  669. [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]];
  670. }
  671. [clientSession serverMessageReceivedFrom:sender params:paramsArray];
  672. }
  673. /*!
  674. * The "notice" event is triggered upon receipt of a NOTICE message
  675. * which means that someone has sent the client a public or private
  676. * notice. According to RFC 1459, the only difference between NOTICE
  677. * and PRIVMSG is that you should NEVER automatically reply to NOTICE
  678. * messages. Unfortunately, this rule is frequently violated by IRC
  679. * servers itself - for example, NICKSERV messages require reply, and
  680. * are NOTICEs.
  681. *
  682. * \param origin the person, who generates the message.
  683. * \param params[0] mandatory, contains your nick.
  684. * \param params[1] optional, contains the message text
  685. */
  686. static void onNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  687. {
  688. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  689. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  690. NSData *notice = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBytes:"\0" length:1];
  691. [clientSession privateNoticeReceived:notice fromUser:nick];
  692. }
  693. /*!
  694. * The "notice" event is triggered upon receipt of a NOTICE message
  695. * which means that someone has sent the client a public or private
  696. * notice. According to RFC 1459, the only difference between NOTICE
  697. * and PRIVMSG is that you should NEVER automatically reply to NOTICE
  698. * messages. Unfortunately, this rule is frequently violated by IRC
  699. * servers itself - for example, NICKSERV messages require reply, and
  700. * are NOTICEs.
  701. *
  702. * \param origin the person, who generates the message.
  703. * \param params[0] mandatory, contains the target channel name.
  704. * \param params[1] optional, contains the message text.
  705. */
  706. static void onChannelNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  707. {
  708. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  709. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  710. NSData *channelName = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  711. NSData *notice = (count > 1) ? [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1] : [NSData dataWithBytes:"\0" length:1];
  712. [clientSession noticeSent:notice toChannel:channelName byUser:nick];
  713. }
  714. /*!
  715. * The "server_notice" event is triggered upon receipt of a NOTICE
  716. * message which means that the server has sent the client a notice.
  717. * This notice is not necessarily addressed to the client's nick
  718. * (for example, AUTH notices, sent before the client's nick is known).
  719. * According to RFC 1459, the only difference between NOTICE
  720. * and PRIVMSG is that you should NEVER automatically reply to NOTICE
  721. * messages. Unfortunately, this rule is frequently violated by IRC
  722. * servers itself - for example, NICKSERV messages require reply, and
  723. * are NOTICEs.
  724. *
  725. * \param origin the person, who generates the message.
  726. * \param params optional, contains who knows what.
  727. */
  728. static void onServerNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  729. {
  730. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  731. NSData *sender = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  732. NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count];
  733. for (unsigned int i = 0; i < count; i++)
  734. {
  735. [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]];
  736. }
  737. [clientSession serverNoticeReceivedFrom:sender params:paramsArray];
  738. }
  739. /*!
  740. * The "invite" event is triggered upon receipt of an INVITE message,
  741. * which means that someone is permitting the client's entry into a +i
  742. * channel.
  743. *
  744. * \param origin the person, who INVITEs you.
  745. * \param params[0] mandatory, contains your nick.
  746. * \param params[1] mandatory, contains the channel name you're invited into.
  747. *
  748. * \sa irc_cmd_invite irc_cmd_chanmode_invite
  749. */
  750. static void onInvite(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  751. {
  752. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  753. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  754. NSData *channelName = [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1];
  755. [clientSession invitedToChannel:channelName by:nick];
  756. }
  757. /*!
  758. * The "ctcp" event is triggered when the client receives the CTCP
  759. * request. By default, the built-in CTCP request handler is used. The
  760. * build-in handler automatically replies on most CTCP messages, so you
  761. * will rarely need to override it.
  762. *
  763. * \param origin the person, who generates the message.
  764. * \param params[0] mandatory, the complete CTCP message, including its
  765. * arguments.
  766. *
  767. * Mirc generates PING, FINGER, VERSION, TIME and ACTION messages,
  768. * check the source code of \c libirc_event_ctcp_internal function to
  769. * see how to write your own CTCP request handler. Also you may find
  770. * useful this question in FAQ: \ref faq4
  771. */
  772. static void onCtcpRequest(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  773. {
  774. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  775. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  776. NSData *request = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  777. [clientSession CTCPRequestReceived:request fromUser:nick];
  778. }
  779. /*!
  780. * The "ctcp" event is triggered when the client receives the CTCP reply.
  781. *
  782. * \param origin the person, who generates the message.
  783. * \param params[0] mandatory, the CTCP message itself with its arguments.
  784. */
  785. static void onCtcpReply(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  786. {
  787. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  788. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  789. NSData *reply = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  790. [clientSession CTCPReplyReceived:reply fromUser:nick];
  791. }
  792. /*!
  793. * The "action" event is triggered when the client receives the CTCP
  794. * ACTION message. These messages usually looks like:\n
  795. * \code
  796. * [23:32:55] * Tim gonna sleep.
  797. * \endcode
  798. *
  799. * \param origin the person, who generates the message.
  800. * \param params[0] mandatory, the target of the message.
  801. * \param params[1] mandatory, the ACTION message.
  802. */
  803. static void onCtcpAction(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  804. {
  805. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  806. NSData *nick = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  807. NSData *target = [NSData dataWithBytes:params[0] length:strlen(params[0]) + 1];
  808. NSData *action = [NSData dataWithBytes:params[1] length:strlen(params[1]) + 1];
  809. [clientSession CTCPActionPerformed:action byUser:nick atTarget:target];
  810. }
  811. /*!
  812. * The "unknown" event is triggered upon receipt of any number of
  813. * unclassifiable miscellaneous messages, which aren't handled by the
  814. * library.
  815. */
  816. static void onUnknownEvent(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
  817. {
  818. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  819. NSData *eventType = [NSData dataWithBytes:event length:strlen(event) + 1];
  820. NSData *sender = (origin != NULL) ? [NSData dataWithBytes:origin length:strlen(origin) + 1] : [NSData dataWithBytes:"\0" length:1];
  821. NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count];
  822. for (unsigned int i = 0; i < count; i++)
  823. {
  824. [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]];
  825. }
  826. [clientSession unknownEventReceived:eventType from:sender params:paramsArray];
  827. }
  828. /*!
  829. * The "numeric" event is triggered upon receipt of any numeric response
  830. * from the server. There is a lot of such responses, see the full list
  831. * here: \ref rfcnumbers.
  832. *
  833. * See the params in ::irc_eventcode_callback_t specification.
  834. */
  835. static void onNumericEvent(irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count)
  836. {
  837. IRCClientSession *clientSession = (__bridge IRCClientSession *) irc_get_ctx(session);
  838. NSUInteger eventNumber = event;
  839. NSData *sender = [NSData dataWithBytes:origin length:strlen(origin) + 1];
  840. NSMutableArray *paramsArray = [NSMutableArray arrayWithCapacity:count];
  841. for (unsigned int i = 0; i < count; i++)
  842. {
  843. [paramsArray addObject:[NSData dataWithBytes:params[i] length:strlen(params[i]) + 1]];
  844. }
  845. [clientSession numericEventReceived:eventNumber from:sender params:paramsArray];
  846. }