1/* 2 * Copyright (C) 2009 Nathan Ollerenshaw chrome@stupendous.net 3 * 4 * This library is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or (at your 7 * option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 12 * License for more details. 13 */ 14 15#define IRCCLIENTVERSION "1.0" 16 17#import "IRCClientSession.h" 18#import "NSObject+DDExtensions.h" 19#import "IRCClientChannel.h" 20#include "string.h" 21 22static void onConnect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 23static void onNick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 24static void onQuit(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 25static void onJoinChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 26static void onPartChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 27static void onMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 28static void onUserMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 29static void onTopic(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 30static void onKick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 31static void onChannelPrvmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 32static void onPrivmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 33static void onNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 34static void onInvite(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 35static void onCtcpRequest(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 36static void onCtcpReply(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 37static void onCtcpAction(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 38static void onUnknownEvent(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); 39static void onNumericEvent(irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count); 40 41@implementation IRCClientSession 42 43@synthesize delegate; 44@synthesize session; 45@synthesize version; 46@synthesize server; 47@synthesize port; 48@synthesize password; 49@synthesize nickname; 50@synthesize username; 51@synthesize realname; 52@synthesize channels; 53@synthesize encoding; 54 55-(id)init 56{ 57 if ((self = [super init])) { 58 callbacks.event_connect = onConnect; 59 callbacks.event_nick = onNick; 60 callbacks.event_quit = onQuit; 61 callbacks.event_join = onJoinChannel; 62 callbacks.event_part = onPartChannel; 63 callbacks.event_mode = onMode; 64 callbacks.event_umode = onUserMode; 65 callbacks.event_topic = onTopic; 66 callbacks.event_kick = onKick; 67 callbacks.event_channel = onChannelPrvmsg; 68 callbacks.event_privmsg = onPrivmsg; 69 callbacks.event_notice = onNotice; 70 callbacks.event_invite = onInvite; 71 callbacks.event_ctcp_req = onCtcpRequest; 72 callbacks.event_ctcp_rep = onCtcpReply; 73 callbacks.event_ctcp_action = onCtcpAction; 74 callbacks.event_unknown = onUnknownEvent; 75 callbacks.event_numeric = onNumericEvent; 76 callbacks.event_dcc_chat_req = NULL; 77 callbacks.event_dcc_send_req = NULL; 78 79 session = irc_create_session(&callbacks); 80 81 if (!session) { 82 NSLog(@"Could not create irc_session."); 83 return nil; 84 } 85 86 irc_set_ctx(session, self); 87 88 unsigned int high, low; 89 irc_get_version (&high, &low); 90 91 [self setVersion:[NSString stringWithFormat:@"IRCClient Framework v%s (Nathan Ollerenshaw) - libirc v%d.%d (Georgy Yunaev)", IRCCLIENTVERSION, high, low]]; 92 93 channels = [[[NSMutableDictionary alloc] init] retain]; 94 encoding = NSASCIIStringEncoding; 95 } 96 return self; 97} 98 99-(void)dealloc 100{ 101 if (irc_is_connected(session)) 102 NSLog(@"Warning: IRC Session is not disconnected on dealloc"); 103 104 irc_destroy_session(session); 105 106 [channels release]; 107 108 [super dealloc]; 109} 110 111- (int)connect; 112{ 113 unsigned short sPort = [port intValue]; 114 115 return irc_connect(session, [server cStringUsingEncoding:encoding], sPort, [password length] > 0 ? [password cStringUsingEncoding:encoding] : NULL , [nickname cStringUsingEncoding:encoding], [username cStringUsingEncoding:encoding], [realname cStringUsingEncoding:encoding]); 116} 117 118- (void)disconnect 119{ 120 irc_disconnect(session); 121} 122 123- (bool)isConnected 124{ 125 return irc_is_connected(session); 126} 127 128- (void)startThread 129{ 130 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 131 132 irc_run(session); 133 134 [pool drain]; 135} 136 137- (void)run 138{ 139 if (thread) { 140 NSLog(@"Thread already running!"); 141 return; 142 } 143 144 thread = [[NSThread alloc] initWithTarget:self selector:@selector(startThread) object:nil]; 145 [thread retain]; 146 [thread start]; 147} 148 149- (int)sendRawWithFormat:(NSString *)format, ... 150{ 151 va_list ap; 152 153 va_start(ap, format); 154 NSString *line = [[NSString alloc] initWithFormat:format arguments:ap]; 155 va_end(ap); 156 157 return irc_send_raw(session, [line cStringUsingEncoding:encoding]); 158} 159 160- (int)quit:(NSString *)reason 161{ 162 return irc_cmd_quit(session, [reason cStringUsingEncoding:encoding]); 163} 164 165- (int)join:(NSString *)channel key:(NSString *)key 166{ 167 NSLog(@"Joining %@", channel); 168 169 if (!key || ![key length] > 0) 170 return irc_cmd_join(session, [channel cStringUsingEncoding:encoding], NULL); 171 172 return irc_cmd_join(session, [channel cStringUsingEncoding:encoding], [key cStringUsingEncoding:encoding]); 173} 174 175- (int)list:(NSString *)channel 176{ 177 return irc_cmd_list(session, [channel cStringUsingEncoding:encoding]); 178} 179 180- (int)userMode:(NSString *)mode 181{ 182 return irc_cmd_user_mode(session, [mode cStringUsingEncoding:encoding]); 183} 184 185- (int)nick:(NSString *)newnick 186{ 187 return irc_cmd_nick(session, [newnick cStringUsingEncoding:encoding]); 188} 189 190- (int)whois:(NSString *)nick 191{ 192 return irc_cmd_whois(session, [nick cStringUsingEncoding:encoding]); 193} 194 195- (int)message:(NSString *)message to:(NSString *)target 196{ 197 return irc_cmd_msg(session, [target cStringUsingEncoding:encoding], [message cStringUsingEncoding:encoding]); 198} 199 200- (int)action:(NSString *)action to:(NSString *)target 201{ 202 return irc_cmd_me(session, [target cStringUsingEncoding:encoding], [action cStringUsingEncoding:encoding]); 203} 204 205- (int)notice:(NSString *)notice to:(NSString *)target 206{ 207 return irc_cmd_notice(session, [target cStringUsingEncoding:encoding], [notice cStringUsingEncoding:encoding]); 208} 209 210- (int)ctcpRequest:(NSString *)request target:(NSString *)target 211{ 212 return irc_cmd_ctcp_request(session, [target cStringUsingEncoding:encoding], [request cStringUsingEncoding:encoding]); 213} 214 215- (int)ctcpReply:(NSString *)reply target:(NSString *)target 216{ 217 return irc_cmd_ctcp_reply(session, [target cStringUsingEncoding:encoding], [reply cStringUsingEncoding:encoding]); 218} 219 220 221 222@end 223 224NSString * 225getNickFromNickUserHost(NSString *nuh) 226{ 227 NSArray *nuhArray = [nuh componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; 228 229 if ([nuhArray count] == 3) 230 return [NSString stringWithString:[nuhArray objectAtIndex:0]]; 231 else 232 return [NSString stringWithString:nuh]; 233} 234 235NSString * 236getUserFromNickUserHost(NSString *nuh) 237{ 238 NSArray *nuhArray = [nuh componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; 239 240 if ([nuhArray count] == 3) 241 return [NSString stringWithString:[nuhArray objectAtIndex:1]]; 242 else 243 return nil; 244} 245 246NSString * 247getHostFromNickUserHost(NSString *nuh) 248{ 249 NSArray *nuhArray = [nuh componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; 250 251 if ([nuhArray count] == 3) 252 return [NSString stringWithString:[nuhArray objectAtIndex:2]]; 253 else 254 return nil; 255} 256 257/*! 258 * The "on_connect" event is triggered when the client successfully 259 * connects to the server, and could send commands to the server. 260 * No extra params supplied; \a params is 0. 261 */ 262static void onConnect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 263{ 264 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 265 266 if ([[client delegate] respondsToSelector:@selector(onConnect)]) 267 [[[client delegate] dd_invokeOnMainThread] onConnect]; 268} 269 270/*! 271 * The "nick" event is triggered when the client receives a NICK message, 272 * meaning that someone (including you) on a channel with the client has 273 * changed their nickname. 274 * 275 * \param origin the person, who changes the nick. Note that it can be you! 276 * \param params[0] mandatory, contains the new nick. 277 */ 278static void onNick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 279{ 280 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 281 282 NSString *nick = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 283 NSString *oldNick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 284 285 if ([[client nickname] compare:oldNick] == NSOrderedSame) { 286 [client setNickname:nick]; 287 } 288 289 if ([[client delegate] respondsToSelector:@selector(onNick:oldNick:)]) 290 [[[client delegate] dd_invokeOnMainThread] onNick:nick oldNick:oldNick]; 291} 292 293/*! 294 * The "quit" event is triggered upon receipt of a QUIT message, which 295 * means that someone on a channel with the client has disconnected. 296 * 297 * \param origin the person, who is disconnected 298 * \param params[0] optional, contains the reason message (user-specified). 299 */ 300static void onQuit(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 301{ 302 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 303 304 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 305 NSString *reason = [NSString stringWithCString:params[0] encoding:[client encoding]]; 306 307 if ([[client delegate] respondsToSelector:@selector(onQuit:reason:)]) 308 [[[client delegate] dd_invokeOnMainThread] onQuit:nick reason:reason]; 309} 310 311/*! 312 * The "join" event is triggered upon receipt of a JOIN message, which 313 * means that someone has entered a channel that the client is on. 314 * 315 * \param origin the person, who joins the channel. By comparing it with 316 * your own nickname, you can check whether your JOIN 317 * command succeed. 318 * \param params[0] mandatory, contains the channel name. 319 */ 320static void onJoinChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 321{ 322 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 323 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 324 NSString *channel = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 325 326 NSString *nickOnly = getNickFromNickUserHost(nick); 327 328 if ([[client nickname] compare:nickOnly] == NSOrderedSame) { 329 // We just joined a channel; allocate an IRCClientChannel object and send it 330 // to the main thread. 331 332 IRCClientChannel *newChannel = [[IRCClientChannel alloc] initWithName:channel]; 333 [[client channels] setObject:newChannel forKey:channel]; 334 335 if ([[client delegate] respondsToSelector:@selector(onJoinChannel:)]) 336 [[[client delegate] dd_invokeOnMainThread] onJoinChannel:newChannel]; 337 } else { 338 // Someone joined a channel we're on. 339 340 IRCClientChannel *currentChannel = [[client channels] objectForKey:channel]; 341 [currentChannel onJoin:nick]; 342 } 343} 344 345/*! 346 * The "part" event is triggered upon receipt of a PART message, which 347 * means that someone has left a channel that the client is on. 348 * 349 * \param origin the person, who leaves the channel. By comparing it with 350 * your own nickname, you can check whether your PART 351 * command succeed. 352 * \param params[0] mandatory, contains the channel name. 353 * \param params[1] optional, contains the reason message (user-defined). 354 */ 355static void onPartChannel(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 356{ 357 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 358 359 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 360 NSString *channel = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 361 NSData *reason = nil; 362 IRCClientChannel *currentChannel = nil; 363 364 if (count > 1) 365 reason = [NSData dataWithBytes:params[1] length:strlen(params[1])]; 366 367 if ([[client nickname] compare:nick] == NSOrderedSame) { 368 // We just left a channel; remove it from the channels dict. 369 370 currentChannel = [[client channels] objectForKey:channel]; 371 [[client channels] removeObjectForKey:channel]; 372 } else { 373 // Someone left a channel we're on. 374 375 currentChannel = [[client channels] objectForKey:channel]; 376 } 377 378 [currentChannel onPart:nick reason:[[NSString alloc] initWithData:reason encoding:[currentChannel encoding]]]; 379} 380 381/*! 382 * The "mode" event is triggered upon receipt of a channel MODE message, 383 * which means that someone on a channel with the client has changed the 384 * channel's parameters. 385 * 386 * \param origin the person, who changed the channel mode. 387 * \param params[0] mandatory, contains the channel name. 388 * \param params[1] mandatory, contains the changed channel mode, like 389 * '+t', '-i' and so on. 390 * \param params[2] optional, contains the mode argument (for example, a 391 * key for +k mode, or user who got the channel operator status for 392 * +o mode) 393 */ 394static void onMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 395{ 396 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 397 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 398 NSString *channel = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 399 NSString *mode = [NSString stringWithCString:params[1] encoding:NSASCIIStringEncoding]; 400 NSString *modeParams = nil; 401 402 if (count > 2) 403 modeParams = [NSString stringWithCString:params[2] encoding:NSASCIIStringEncoding]; 404 405 IRCClientChannel *currentChannel = [[client channels] objectForKey:channel]; 406 407 [currentChannel onMode:mode params:modeParams nick:nick]; 408} 409 410/*! 411 * The "umode" event is triggered upon receipt of a user MODE message, 412 * which means that your user mode has been changed. 413 * 414 * \param origin the person, who changed the channel mode. 415 * \param params[0] mandatory, contains the user changed mode, like 416 * '+t', '-i' and so on. 417 */ 418static void onUserMode(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 419{ 420 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 421 NSString *mode = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 422 423 if ([[client delegate] respondsToSelector:@selector(onMode:)]) 424 [[[client delegate] dd_invokeOnMainThread] onMode:mode]; 425} 426 427/*! 428 * The "topic" event is triggered upon receipt of a TOPIC message, which 429 * means that someone on a channel with the client has changed the 430 * channel's topic. 431 * 432 * \param origin the person, who changes the channel topic. 433 * \param params[0] mandatory, contains the channel name. 434 * \param params[1] optional, contains the new topic. 435 */ 436static void onTopic(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 437{ 438 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 439 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 440 NSString *channel = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 441 NSData *topic = nil; 442 443 if (count > 1) 444 topic = [NSData dataWithBytes:params[1] length:strlen(params[1])]; 445 446 IRCClientChannel *currentChannel = [[client channels] objectForKey:channel]; 447 448 [currentChannel onTopic:[[NSString alloc] initWithData:topic encoding:[currentChannel encoding]] nick:nick]; 449} 450 451/*! 452 * The "kick" event is triggered upon receipt of a KICK message, which 453 * means that someone on a channel with the client (or possibly the 454 * client itself!) has been forcibly ejected. 455 * 456 * \param origin the person, who kicked the poor. 457 * \param params[0] mandatory, contains the channel name. 458 * \param params[1] optional, contains the nick of kicked person. 459 * \param params[2] optional, contains the kick text 460 */ 461static void onKick(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 462{ 463 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 464 NSString *byNick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 465 NSString *channel = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 466 NSString *nick = nil; 467 NSData *reason = nil; 468 469 if (count > 1) 470 nick = [NSString stringWithCString:params[1] encoding:NSASCIIStringEncoding]; 471 472 if (count > 2) 473 reason = [NSData dataWithBytes:params[2] length:strlen(params[2])]; 474 475 if (nick == nil) { 476 // we got kicked 477 IRCClientChannel *currentChannel = [[client channels] objectForKey:channel]; 478 [[client channels] removeObjectForKey:channel]; 479 480 [currentChannel onKick:[client nickname] reason:[[NSString alloc] initWithData:reason encoding:[currentChannel encoding]] byNick:byNick]; 481 } else { 482 // someone else got booted 483 IRCClientChannel *currentChannel = [[client channels] objectForKey:channel]; 484 485 [currentChannel onKick:nick reason:[[NSString alloc] initWithData:reason encoding:[currentChannel encoding]] byNick:byNick]; 486 } 487} 488 489/*! 490 * The "channel" event is triggered upon receipt of a PRIVMSG message 491 * to an entire channel, which means that someone on a channel with 492 * the client has said something aloud. Your own messages don't trigger 493 * PRIVMSG event. 494 * 495 * \param origin the person, who generates the message. 496 * \param params[0] mandatory, contains the channel name. 497 * \param params[1] optional, contains the message text 498 */ 499static void onChannelPrvmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 500{ 501 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 502 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 503 NSString *channel = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 504 NSData *message = nil; 505 506 if (count > 1) { 507 message = [NSData dataWithBytes:params[1] length:strlen(params[1])]; 508 509 IRCClientChannel *currentChannel = [[client channels] objectForKey:channel]; 510 511 [currentChannel onPrivmsg:[[NSString alloc] initWithData:message encoding:[currentChannel encoding]] nick:nick]; 512 } 513} 514 515/*! 516 * The "privmsg" event is triggered upon receipt of a PRIVMSG message 517 * which is addressed to one or more clients, which means that someone 518 * is sending the client a private message. 519 * 520 * \param origin the person, who generates the message. 521 * \param params[0] mandatory, contains your nick. 522 * \param params[1] optional, contains the message text 523 */ 524static void onPrivmsg(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 525{ 526 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 527 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 528 NSData *message = nil; 529 530 if (count > 1) { 531 message = [NSData dataWithBytes:params[1] length:strlen(params[1])]; 532 533 if ([[client delegate] respondsToSelector:@selector(onPrivmsg:nick:)]) 534 [[[client delegate] dd_invokeOnMainThread] onPrivmsg:message nick:nick]; 535 } 536 537 // we eat privmsgs with no message 538} 539 540/*! 541 * The "notice" event is triggered upon receipt of a NOTICE message 542 * which means that someone has sent the client a public or private 543 * notice. According to RFC 1459, the only difference between NOTICE 544 * and PRIVMSG is that you should NEVER automatically reply to NOTICE 545 * messages. Unfortunately, this rule is frequently violated by IRC 546 * servers itself - for example, NICKSERV messages require reply, and 547 * are NOTICEs. 548 * 549 * \param origin the person, who generates the message. 550 * \param params[0] mandatory, contains the channel name. 551 * \param params[1] optional, contains the message text 552 */ 553static void onNotice(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 554{ 555 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 556 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 557 NSString *target = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 558 NSData *notice = nil; 559 560 IRCClientChannel *currentChannel = [[client channels] objectForKey:target]; 561 562 if (count > 1) { 563 notice = [NSData dataWithBytes:params[1] length:strlen(params[1])]; 564 565 if (currentChannel != nil) { 566 [currentChannel onNotice:[[NSString alloc] initWithData:notice encoding:[currentChannel encoding]] nick:nick]; 567 } else { 568 if ([[client delegate] respondsToSelector:@selector(onNotice:nick:)]) 569 [[[client delegate] dd_invokeOnMainThread] onNotice:notice nick:nick]; 570 } 571 } 572 573 // we eat notices with no message 574} 575 576/*! 577 * The "invite" event is triggered upon receipt of an INVITE message, 578 * which means that someone is permitting the client's entry into a +i 579 * channel. 580 * 581 * \param origin the person, who INVITEs you. 582 * \param params[0] mandatory, contains your nick. 583 * \param params[1] mandatory, contains the channel name you're invited into. 584 * 585 * \sa irc_cmd_invite irc_cmd_chanmode_invite 586 */ 587static void onInvite(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 588{ 589 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 590 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 591 NSString *channel = [NSString stringWithCString:params[1] encoding:NSASCIIStringEncoding]; 592 593 if ([[client delegate] respondsToSelector:@selector(onInvite:nick:)]) 594 [[[client delegate] dd_invokeOnMainThread] onInvite:channel nick:nick]; 595} 596 597/*! 598 * The "ctcp" event is triggered when the client receives the CTCP 599 * request. By default, the built-in CTCP request handler is used. The 600 * build-in handler automatically replies on most CTCP messages, so you 601 * will rarely need to override it. 602 * 603 * \param origin the person, who generates the message. 604 * \param params[0] mandatory, the complete CTCP message, including its 605 * arguments. 606 * 607 * Mirc generates PING, FINGER, VERSION, TIME and ACTION messages, 608 * check the source code of \c libirc_event_ctcp_internal function to 609 * see how to write your own CTCP request handler. Also you may find 610 * useful this question in FAQ: \ref faq4 611 */ 612static void onCtcpRequest(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 613{ 614 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 615 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 616 617 if ( origin ) 618 { 619 char nickbuf[128]; 620 irc_target_get_nick (origin, nickbuf, sizeof(nickbuf)); 621 622 if ( strstr (params[0], "PING") == params[0] ) { 623 irc_cmd_ctcp_reply (session, nickbuf, params[0]); 624 } 625 else if ( !strcmp (params[0], "VERSION") ) 626 { 627 irc_cmd_ctcp_reply (session, nickbuf, [[NSString stringWithFormat:@"VERSION %@", [client version]] UTF8String]); 628 } 629 else if ( !strcmp (params[0], "FINGER") ) 630 { 631 irc_cmd_ctcp_reply (session, nickbuf, [[NSString stringWithFormat:@"FINGER %@ (%@) Idle 0 seconds", [client username], [client realname]] UTF8String]); 632 } 633 else if ( !strcmp (params[0], "TIME") ) 634 { 635 irc_cmd_ctcp_reply(session, nickbuf, [[[NSDate dateWithTimeIntervalSinceNow:0] descriptionWithCalendarFormat:@"TIME %a %b %e %H:%M:%S %Z %Y" timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]] UTF8String]); 636 } else { 637 if ([[client delegate] respondsToSelector:@selector(onCtcpRequest:type:nick:)]) { 638 NSString *requestString = [[NSString alloc] initWithData:[NSData dataWithBytes:params[0] length:strlen(params[0])] encoding:[client encoding]]; 639 640 NSRange firstSpace = [requestString rangeOfString:@" "]; 641 642 NSString *type = [requestString substringToIndex:firstSpace.location]; 643 NSString *request = [requestString substringFromIndex:(firstSpace.location + 1)]; 644 645 [[[client delegate] dd_invokeOnMainThread] onCtcpRequest:request type:type nick:nick]; 646 } 647 } 648 } 649} 650 651/*! 652 * The "ctcp" event is triggered when the client receives the CTCP reply. 653 * 654 * \param origin the person, who generates the message. 655 * \param params[0] mandatory, the CTCP message itself with its arguments. 656 */ 657static void onCtcpReply(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 658{ 659 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 660 661 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 662 NSData *reply = [NSData dataWithBytes:params[0] length:strlen(params[0])]; 663 664 if ([[client delegate] respondsToSelector:@selector(onCtcpReply:nick:)]) 665 [[[client delegate] dd_invokeOnMainThread] onCtcpReply:reply nick:nick]; 666} 667 668/*! 669 * The "action" event is triggered when the client receives the CTCP 670 * ACTION message. These messages usually looks like:\n 671 * \code 672 * [23:32:55] * Tim gonna sleep. 673 * \endcode 674 * 675 * \param origin the person, who generates the message. 676 * \param params[0] mandatory, the target of the message. 677 * \param params[1] mandatory, the ACTION message. 678 */ 679static void onCtcpAction(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 680{ 681 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 682 683 NSString *nick = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 684 NSString *target = [NSString stringWithCString:params[0] encoding:NSASCIIStringEncoding]; 685 NSData *action = [NSData dataWithBytes:params[1] length:strlen(params[1])]; 686 687 IRCClientChannel *currentChannel = [[client channels] objectForKey:target]; 688 689 if (currentChannel) { 690 // An action on a channel we're on 691 692 [currentChannel onAction:[[NSString alloc] initWithData:action encoding:[currentChannel encoding]] nick:nick]; 693 } else { 694 // An action in a private message 695 696 if ([[client delegate] respondsToSelector:@selector(onAction:nick:)]) 697 [[[client delegate] dd_invokeOnMainThread] onAction:action nick:nick]; 698 } 699} 700 701/*! 702 * The "unknown" event is triggered upon receipt of any number of 703 * unclassifiable miscellaneous messages, which aren't handled by the 704 * library. 705 */ 706static void onUnknownEvent(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) 707{ 708 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 709 NSString *eventString = [NSString stringWithCString:event encoding:NSASCIIStringEncoding]; 710 NSString *sender = nil; 711 712 if (origin != NULL) 713 sender = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 714 715 NSMutableArray *paramsArray = [[NSMutableArray alloc] init]; 716 717 for (unsigned int i = 0; i < count; i++) 718 [paramsArray addObject:[[NSString alloc] initWithData:[NSData dataWithBytes:params[i] length:strlen(params[i])] encoding:[client encoding]]]; 719 720 if ([[client delegate] respondsToSelector:@selector(onUnknownEvent:origin:params:)]) 721 [[[client delegate] dd_invokeOnMainThread] onUnknownEvent:eventString origin:sender params:paramsArray]; 722} 723 724/*! 725 * The "numeric" event is triggered upon receipt of any numeric response 726 * from the server. There is a lot of such responses, see the full list 727 * here: \ref rfcnumbers. 728 * 729 * See the params in ::irc_eventcode_callback_t specification. 730 */ 731static void onNumericEvent(irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count) 732{ 733 IRCClientSession *client = (IRCClientSession *) irc_get_ctx(session); 734 NSUInteger eventNumber = event; 735 NSString *originString = [NSString stringWithCString:origin encoding:NSASCIIStringEncoding]; 736 737 NSMutableArray *paramsArray = [[NSMutableArray alloc] init]; 738 739 for (unsigned int i = 0; i < count; i++) 740 [paramsArray addObject:[[NSString alloc] initWithData:[NSData dataWithBytes:params[i] length:strlen(params[i])] encoding:[client encoding]]]; 741 742 if ([[client delegate] respondsToSelector:@selector(onNumericEvent:origin:params:)]) 743 [[[client delegate] dd_invokeOnMainThread] onNumericEvent:eventNumber origin:originString params:paramsArray]; 744} 745