add irc_conn_PRIVMSG/irc_chan_PRIVMSG and lua bindings
authorTero Marttila <terom@fixme.fi>
Tue, 31 Mar 2009 20:57:07 +0300
changeset 97 d3bc82ee76cb
parent 96 a07d917adec1
child 98 f357f835f0d5
add irc_conn_PRIVMSG/irc_chan_PRIVMSG and lua bindings
src/error.h
src/irc_chan.c
src/irc_chan.h
src/irc_conn.c
src/irc_conn.h
src/irc_net.c
src/lua_objs.c
src/test.c
--- 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                        }
 };