--- 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:
--- 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 <channel> [<message>]
- */
-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 <channel> <message>
*/
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 <channel> [<message>]
+ */
+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 <channel> <target> [<reason>]
+ *
+ * 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 [<message>]
*
* 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 }
};
--- 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 */
--- 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 },