# HG changeset patch # User Tero Marttila # Date 1238413514 -10800 # Node ID 68345a9b99a306cf0f0d27dc5678074beb524f9d # Parent 233916a00429d0442717d3a979a565ceac5c04b3 irc_chan_on_KICK diff -r 233916a00429 -r 68345a9b99a3 TODO --- a/TODO Mon Mar 30 01:47:44 2009 +0300 +++ b/TODO Mon Mar 30 14:45:14 2009 +0300 @@ -4,7 +4,6 @@ * tests... irc_conn: - * support for CTCP messages, e.g. ACTION * ratelimit queue for outgoing messages irc_net: diff -r 233916a00429 -r 68345a9b99a3 src/irc_chan.c --- a/src/irc_chan.c Mon Mar 30 01:47:44 2009 +0300 +++ b/src/irc_chan.c Mon Mar 30 14:45:14 2009 +0300 @@ -105,42 +105,6 @@ } /** - * Someone, or us ourselves, left a channel - * - * :nm PART [] - */ -static void irc_chan_on_PART (const struct irc_line *line, void *arg) -{ - struct irc_chan *chan = arg; - - const char *msg = line->args[1]; - - // us? - if (irc_cmp_nick(line->source->nickname, chan->net->conn->nickname) == 0) { - // twiddle state - chan->joined = false; - chan->parted = true; - - // invoke callback - IRC_CHAN_INVOKE_CALLBACK(chan, on_self_part); - - } else { - // someone else - struct irc_chan_user *chan_user; - - // invoke callback (source, msg) - IRC_CHAN_INVOKE_CALLBACK(chan, on_part, line->source, msg); - - // look up the irc_chan_user - if ((chan_user = irc_chan_get_user(chan, line->source->nickname)) == NULL) - return log_warn("PART'd user not on channel: %s, %s", irc_chan_name(chan), line->source->nickname); - - // remove them - irc_chan_remove_user(chan, chan_user); - } -} - -/** * :nm PRIVMSG */ static void irc_chan_on_PRIVMSG (const struct irc_line *line, void *arg) @@ -202,6 +166,81 @@ } /** + * Someone, or us ourselves, left a channel + * + * :nm PART [] + */ +static void irc_chan_on_PART (const struct irc_line *line, void *arg) +{ + struct irc_chan *chan = arg; + + const char *msg = line->args[1]; + + // us? + if (irc_cmp_nick(line->source->nickname, chan->net->conn->nickname) == 0) { + // twiddle state + chan->joined = false; + chan->parted = true; + + // invoke callback + IRC_CHAN_INVOKE_CALLBACK(chan, on_self_part); + + // XXX: cleanup + + } else { + // someone else + struct irc_chan_user *chan_user; + + // invoke callback (source, msg) + IRC_CHAN_INVOKE_CALLBACK(chan, on_part, line->source, msg); + + // look up the irc_chan_user + if ((chan_user = irc_chan_get_user(chan, line->source->nickname)) == NULL) + return log_warn("PART'd user not on channel: %s, %s", irc_chan_name(chan), line->source->nickname); + + // remove them + irc_chan_remove_user(chan, chan_user); + } +} + +/** + * :nm KICK [] + * + * User kicked some other user, might be us + */ +static void irc_chan_on_KICK (const struct irc_line *line, void *arg) +{ + struct irc_chan *chan = arg; + struct irc_chan_user *target_user; + + const char *target = line->args[1]; + const char *msg = line->args[2]; + + // us? + if (irc_cmp_nick(target, chan->net->conn->nickname) == 0) { + // twiddle state + chan->joined = false; + chan->kicked = true; + + // invoke callback (source, msg) + IRC_CHAN_INVOKE_CALLBACK(chan, on_self_kicked, line->source, msg); + + // XXX: cleanup + + } else { + // look up the target irc_chan_user + if ((target_user = irc_chan_get_user(chan, target)) == NULL) + return log_warn("KICK'd user not on channel: %s, %s", irc_chan_name(chan), target); + + // invoke callback (source, target_user, msg) + IRC_CHAN_INVOKE_CALLBACK(chan, on_kick, line->source, target_user, msg); + + // remove them + irc_chan_remove_user(chan, target_user); + } +} + +/** * :nm QUIT [] * * User quit, so remove them from our users list @@ -233,6 +272,7 @@ { IRC_RPL_ENDOFNAMES, &irc_chan_on_RPL_ENDOFNAMES }, { "PRIVMSG", &irc_chan_on_PRIVMSG }, { "PART", &irc_chan_on_PART }, + { "KICK", &irc_chan_on_KICK }, { "QUIT", &irc_chan_on_QUIT }, { NULL, NULL } }; diff -r 233916a00429 -r 68345a9b99a3 src/irc_chan.h --- a/src/irc_chan.h Mon Mar 30 01:47:44 2009 +0300 +++ b/src/irc_chan.h Mon Mar 30 14:45:14 2009 +0300 @@ -26,6 +26,19 @@ }; /** + * Per-channel-user state. This is used to store the list of irc_user's on an irc_chan. + * + * The nickname is accessible via user->nickname. + */ +struct irc_chan_user { + /** The per-network user info */ + struct irc_user *user; + + /** The irc_chan list */ + LIST_ENTRY(irc_chan_user) chan_users; +}; + +/** * High-level IRC channel callbacks for convenience. * * These are based on the normal irc_chan::handlers interface, and you can use both freely. @@ -42,12 +55,18 @@ /** We parted the channel */ void (*on_self_part) (struct irc_chan *chan, void *arg); + /** We were kicked from the channel */ + void (*on_self_kicked) (struct irc_chan *chan, const struct irc_nm *source, const char *msg, void *arg); + /** Someone joined the channel */ void (*on_join) (struct irc_chan *chan, const struct irc_nm *source, void *arg); /** Someone parted the channel. The irc_chan_user is still present */ void (*on_part) (struct irc_chan *chan, const struct irc_nm *source, const char *msg, void *arg); + /** Someone was kicked from the channel. The irc_chan_user is still present */ + void (*on_kick) (struct irc_chan *chan, const struct irc_nm *source, struct irc_chan_user *target, const char *msg, void *arg); + /** Someone quit the channel. The irc_chan_user is still present */ void (*on_quit) (struct irc_chan *chan, const struct irc_nm *source, const char *msg, void *arg); @@ -56,19 +75,6 @@ }; /** - * Per-channel-user state. This is used to store the list of irc_user's on an irc_chan. - * - * The nickname is accessible via user->nickname. - */ -struct irc_chan_user { - /** The per-network user info */ - struct irc_user *user; - - /** The irc_chan list */ - LIST_ENTRY(irc_chan_user) chan_users; -}; - -/** * Persistent IRC channel state, part of irc_net. * * This stores the channel's info, status flags, users list, and handlers/callbacks. @@ -98,6 +104,9 @@ /** We PART'd the channel */ bool parted; + /** We were KICK'd from the channel */ + bool kicked; + // @} /** List of users on channel */ diff -r 233916a00429 -r 68345a9b99a3 src/test.c --- a/src/test.c Mon Mar 30 01:47:44 2009 +0300 +++ b/src/test.c Mon Mar 30 14:45:14 2009 +0300 @@ -878,6 +878,40 @@ irc_net_destroy(net); } +void test_irc_chan_user_kick (void) +{ + struct test_chan_ctx ctx; + struct sock_test *sock = setup_sock_test(); + struct irc_net *net = setup_irc_net(sock); + struct irc_chan *chan = setup_irc_chan(sock, net, "#test", &ctx); + + // kick a user + log_info("test irc_chan_on_KICK (other)"); + test_sock_push(sock, ":userA!someone@somewhere KICK %s userB\r\n", "#test"); + check_chan_user(chan, "userA", true); + check_chan_user(chan, "userB", false); + + // cleanup + irc_net_destroy(net); +} + +void test_irc_chan_self_kick (void) +{ + struct test_chan_ctx ctx; + struct sock_test *sock = setup_sock_test(); + struct irc_net *net = setup_irc_net(sock); + struct irc_chan *chan = setup_irc_chan(sock, net, "#test", &ctx); + + // kick a user + log_info("test irc_chan_on_KICK (self)"); + test_sock_push(sock, ":userA!someone@somewhere KICK %s mynick foobar\r\n", "#test"); + assert(!chan->joined); + assert(chan->kicked); + + // cleanup + irc_net_destroy(net); +} + void test_irc_chan_user_nick (void) { struct test_chan_ctx ctx; @@ -968,6 +1002,8 @@ { "irc_chan_namreply", &test_irc_chan_namreply }, { "irc_chan_user_join", &test_irc_chan_user_join }, { "irc_chan_user_part", &test_irc_chan_user_part }, + { "irc_chan_user_kick", &test_irc_chan_user_kick }, + { "irc_chan_self_kick", &test_irc_chan_self_kick }, { "irc_chan_user_nick", &test_irc_chan_user_nick }, { "irc_chan_user_quit", &test_irc_chan_user_quit }, { "irc_chan_CTCP_ACTION", &test_irc_chan_CTCP_ACTION },