IRC client framework (wrapper around libircclient library).
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

IRCClientSession.m 37KB

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