IRC client framework (wrapper around libircclient library).
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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