--- a/src/error.h Tue Mar 31 20:33:45 2009 +0300
+++ b/src/error.h Tue Mar 31 20:57:07 2009 +0300
@@ -81,7 +81,7 @@
/** irc_net errors */
_ERR_IRC_NET = 0x000900,
- ERR_IRC_NET_QUIT_STATE,
+ ERR_IRC_NET_STATE,
/** @see module_error_code */
_ERR_MODULE = 0x000a00,
@@ -95,6 +95,10 @@
ERR_LUA_MEM,
ERR_LUA_SYNTAX,
+ /** irc_chan errors */
+ _ERR_IRC_CHAN = 0x000d00,
+ ERR_IRC_CHAN_STATE,
+
/** General errors */
_ERR_GENERAL = 0xffff00,
ERR_CMD_OPT,
--- a/src/irc_chan.c Tue Mar 31 20:33:45 2009 +0300
+++ b/src/irc_chan.c Tue Mar 31 20:57:07 2009 +0300
@@ -161,6 +161,8 @@
{
struct irc_chan *chan = arg;
+ (void) line;
+
// XXX: update state
log_info("channel join sync complete");
}
@@ -358,10 +360,13 @@
err_t irc_chan_join (struct irc_chan *chan)
{
err_t err;
+
+ // correct state
+ if (chan->joining || chan->joined)
+ return ERR_IRC_CHAN_STATE;
- // XXX: error instead?
- assert(!chan->joining && !chan->joined);
- assert(chan->net->conn);
+ if (!chan->net->conn)
+ return ERR_IRC_NET_STATE;
// send JOIN message on the appropriate connection
if ((err = irc_conn_JOIN(chan->net->conn, chan->info.channel)))
@@ -374,3 +379,16 @@
return SUCCESS;
}
+err_t irc_chan_PRIVMSG (struct irc_chan *chan, const char *message)
+{
+ // correct state
+ if (!chan->joined)
+ return ERR_IRC_CHAN_STATE;
+
+ if (!chan->net->conn)
+ return ERR_IRC_NET_STATE;
+
+ // send the PRIVMSG message
+ return irc_conn_PRIVMSG(chan->net->conn, chan->info.channel, message);
+}
+
--- a/src/irc_chan.h Tue Mar 31 20:33:45 2009 +0300
+++ b/src/irc_chan.h Tue Mar 31 20:57:07 2009 +0300
@@ -172,4 +172,13 @@
*/
err_t irc_chan_join (struct irc_chan *chan);
+/**
+ * Send a normal PRIVMSG to the channel. If we're being pedantic, one should use NOTICE instead for messages sent in
+ * reply to PRIVMSG's, but the real world is different.
+ *
+ * @param chan the IRC channel
+ * @param message the message to send
+ */
+err_t irc_chan_PRIVMSG (struct irc_chan *chan, const char *message);
+
#endif
--- a/src/irc_conn.c Tue Mar 31 20:33:45 2009 +0300
+++ b/src/irc_conn.c Tue Mar 31 20:57:07 2009 +0300
@@ -341,7 +341,7 @@
{
// NICK <nickname>
struct irc_line line = {
- NULL, "NICK", { nickname, NULL }
+ NULL, "NICK", { nickname }
};
return irc_conn_send(conn, &line);
@@ -349,9 +349,9 @@
err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname)
{
- // USER <user> <mode> <unused> <realname>
+ // USER <user> <mode> * <realname>
struct irc_line line = {
- NULL, "USER", { username, "0", "*", realname, NULL }
+ NULL, "USER", { username, "0", "*", realname }
};
return irc_conn_send(conn, &line);
@@ -362,7 +362,7 @@
// PONG <server> [ <server2> ]
// params are actually the wrong way around now, but nobody cares
struct irc_line line = {
- NULL, "PONG", { target, NULL }
+ NULL, "PONG", { target }
};
return irc_conn_send(conn, &line);
@@ -370,9 +370,19 @@
err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel)
{
- // JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0"
+ // JOIN (<channel> [ "," <channel> [ ... ] ]) [<key> [ "," <key> [ ... ] ] ]
struct irc_line line = {
- NULL, "JOIN", { channel, NULL }
+ NULL, "JOIN", { channel }
+ };
+
+ return irc_conn_send(conn, &line);
+}
+
+err_t irc_conn_PRIVMSG (struct irc_conn *conn, const char *target, const char *message)
+{
+ // PRIVMSG <msgtarget> <message>
+ struct irc_line line = {
+ NULL, "PRIVMSG", { target, message }
};
return irc_conn_send(conn, &line);
@@ -383,7 +393,7 @@
err_t err;
struct irc_line line = {
- NULL, "QUIT", { message, NULL }
+ NULL, "QUIT", { message }
};
// state check
--- a/src/irc_conn.h Tue Mar 31 20:33:45 2009 +0300
+++ b/src/irc_conn.h Tue Mar 31 20:57:07 2009 +0300
@@ -296,6 +296,28 @@
err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel);
/**
+ * Send a PRIVMSG message to some target, usually a channel or another user (nickname).
+ *
+ * Note that the protocol limits messages to 512 bytes in length (total, including other protocol stuff), and messages
+ * can't contain newlines or NULs - this will return an ERR_IRC_LINE_* in both cases.
+ *
+ * If succesfull, this won't result in any reply. If the target is a nickname, the server supports AWAY, and the target
+ * has marked themselves as away, this may result in an RPL_AWAY reply.
+ *
+ * @param conn the IRC protocol connection
+ * @param target the message target, usually either a channel name or a nickname
+ * @param message the message to send
+ *
+ * Possible errors (from RFC2812):
+ * ERR_NORECIPIENT ERR_NOTEXTTOSEND
+ * ERR_CANNOTSENDTOCHAN ERR_NOTOPLEVEL
+ * ERR_WILDTOPLEVEL ERR_TOOMANYTARGETS
+ * ERR_NOSUCHNICK
+ *
+ */
+err_t irc_conn_PRIVMSG (struct irc_conn *conn, const char *target, const char *message);
+
+/**
* Send a QUIT message to the server. The server will reply with an ERROR message and close the connection.
*
* This updates our state as disconnecting, and once EOF is recieved, the irc_conn_callbacks::on_quit callback is
--- a/src/irc_net.c Tue Mar 31 20:33:45 2009 +0300
+++ b/src/irc_net.c Tue Mar 31 20:57:07 2009 +0300
@@ -449,7 +449,7 @@
err_t irc_net_quit (struct irc_net *net, const char *message)
{
if (!net->conn)
- return ERR_IRC_NET_QUIT_STATE;
+ return ERR_IRC_NET_STATE;
// send the QUIT message, and then we can wait for the reply
return irc_conn_QUIT(net->conn, message);
--- a/src/lua_objs.c Tue Mar 31 20:33:45 2009 +0300
+++ b/src/lua_objs.c Tue Mar 31 20:57:07 2009 +0300
@@ -88,9 +88,29 @@
return 1;
}
+/**
+ * Send a PRIVMSG to the channel
+ */
+static int lua_chan_say (lua_State *L)
+{
+ err_t err;
+
+ struct lua_chan *lua_chan = lua_obj_get_obj(L, __func__, "evirc.chan");
+
+ // the message
+ const char *message = luaL_checkstring(L, 2);
+
+ // send
+ if ((err = irc_chan_PRIVMSG(lua_chan->chan, message)))
+ return luaL_error(L, "irc_chan_PRIVMSG: '%s': %s", message, error_name(err));
+
+ // ok
+ return 0;
+}
static const struct luaL_Reg lua_chan_methods[] = {
{ "__tostring", &lua_chan_tostring },
+ { "say", &lua_chan_say },
{ NULL, NULL },
};
--- a/src/test.c Tue Mar 31 20:33:45 2009 +0300
+++ b/src/test.c Tue Mar 31 20:57:07 2009 +0300
@@ -1063,6 +1063,21 @@
irc_net_destroy(net);
}
+void test_irc_chan_privmsg (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);
+
+ // rename one of the users
+ log_info("test irc_chan_PRIVMSG");
+ assert_success(irc_chan_PRIVMSG(chan, "foobar quux"));
+ assert_sock_data(sock, "PRIVMSG #test :foobar quux\r\n");
+
+ // cleanup
+ irc_net_destroy(net);
+}
/**
* Test definition
@@ -1092,6 +1107,7 @@
{ "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 },
+ { "irc_chan_privmsg", &test_irc_chan_privmsg },
{ NULL, NULL }
};