change irc_chan.state into bool fields, move irc_cmd implementation from irc_conn.c into irc_cmd.c, remove irc_conn arg from irc_cmd_handler, add irc_conn.nickname tracking, and handle irc_chan JOINs
authorTero Marttila <terom@fixme.fi>
Thu, 12 Mar 2009 18:11:44 +0200
changeset 37 4fe4a3c4496e
parent 36 791d7a5532e2
child 38 0c2e0cb46c3a
change irc_chan.state into bool fields, move irc_cmd implementation from irc_conn.c into irc_cmd.c, remove irc_conn arg from irc_cmd_handler, add irc_conn.nickname tracking, and handle irc_chan JOINs
Makefile
src/error.c
src/error.h
src/irc_chan.c
src/irc_chan.h
src/irc_cmd.c
src/irc_cmd.h
src/irc_conn.c
src/irc_conn.h
src/irc_log.c
src/irc_net.c
--- a/Makefile	Thu Mar 12 18:08:27 2009 +0200
+++ b/Makefile	Thu Mar 12 18:11:44 2009 +0200
@@ -34,11 +34,11 @@
 # modules
 module_objs = $(patsubst src/%.c,obj/%.o,$(wildcard src/$(1)/*.c))
 
-CORE_OBJS = obj/error.o obj/log.o
+CORE_OBJS = obj/error.o obj/log.o obj/chain.o
 SOCK_OBJS = obj/sock.o obj/sock_tcp.o
 SOCK_GNUTLS_OBJS = obj/sock_gnutls.o
 LINEPROTO_OBJS = obj/line_proto.o
-IRC_OBJS = obj/irc_line.o obj/irc_conn.o obj/irc_net.o obj/irc_chan.o
+IRC_OBJS = obj/irc_line.o obj/irc_conn.o obj/irc_net.o obj/irc_chan.o obj/irc_cmd.o obj/irc_nm.o
 IRC_LOG_OBJS = obj/irc_log.o
 
 # XXX: not yet there
--- a/src/error.c	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/error.c	Thu Mar 12 18:11:44 2009 +0200
@@ -10,6 +10,7 @@
 
 struct error_desc _core_error_desc[] = {
     {   ERR_CALLOC,                         "calloc",                                   ERR_EXTRA_NONE      },
+    {   ERR_STRDUP,                         "strdup",                                   ERR_EXTRA_NONE      },
     {   ERR_GETADDRINFO,                    "getaddrinfo",                              ERR_EXTRA_GAI       },
     {   ERR_GETADDRINFO_EMPTY,              "getaddrinfo: no results",                  ERR_EXTRA_NONE      },
     {   ERR_EVENT_NEW,                      "event_new",                                ERR_EXTRA_NONE      },
@@ -39,6 +40,12 @@
     {   ERR_GNUTLS_RECORD_GET_DIRECTION,    "gnutls_record_get_direction",              ERR_EXTRA_GNUTLS    },
     {   _ERR_INVALID,                       NULL,                                       0                   }
 
+}, _irc_proto_error_desc[] = {
+    {   ERR_LINE_TOO_LONG,                  "IRC line is too long",                     ERR_EXTRA_NONE      },
+    {   ERR_LINE_INVALID_TOKEN,             "Illegal token value for IRC line",         ERR_EXTRA_NONE      },
+    {   ERR_INVALID_NM,                     "Invalid nickmask",                         ERR_EXTRA_NONE      },
+    {   ERR_INVALID_NICK_LENGTH,            "Nickname is too long",                     ERR_EXTRA_NONE      },
+    {   _ERR_INVALID,                       NULL,                                       0                   }
 };
 
 /**
@@ -48,6 +55,7 @@
     _core_error_desc,
     _sock_error_desc,
     _sock_gnutls_error_desc,
+    _irc_proto_error_desc,
     NULL
 };
 
--- a/src/error.h	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/error.h	Thu Mar 12 18:11:44 2009 +0200
@@ -40,6 +40,7 @@
     /** stdlib.h functions */
     _ERR_STDLIB     = 0x000100,
     ERR_CALLOC,
+    ERR_STRDUP,
     
     /** DNS resolver */
     _ERR_RESOLVER   = 0x000200,
@@ -61,10 +62,12 @@
     _ERR_EVSQL      = 0x000600,
     ERR_EVSQL_NEW_PQ,
 
-    /** irc_line errors */
+    /** irc_proto errors */
     _ERR_IRC_LINE   = 0x000700,
     ERR_LINE_TOO_LONG,
     ERR_LINE_INVALID_TOKEN,
+    ERR_INVALID_NM,
+    ERR_INVALID_NICK_LENGTH,
 
     /** irc_conn errors */
     _ERR_IRC_CONN   = 0x000800,
--- a/src/irc_chan.c	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_chan.c	Thu Mar 12 18:11:44 2009 +0200
@@ -1,4 +1,6 @@
 #include "irc_chan.h"
+#include "irc_nm.h"
+#include "log.h"
 
 #include <stdlib.h>
 #include <assert.h>
@@ -8,6 +10,33 @@
     return chan->info.channel;
 }
 
+/**
+ * :nm JOIN <channel>
+ */ 
+static void irc_chan_on_JOIN (const struct irc_line *line, void *arg)
+{
+    struct irc_chan *chan = arg;
+
+    // us?
+    if (irc_prefix_cmp_nick(line->prefix, chan->net->conn->nickname) == 0) {
+        // twiddle state
+        chan->state.joining = false;
+        chan->state.joined = true;
+
+        log_info("joined channel: %s", chan->info.channel);
+
+        // TODO: callbacks
+    }
+}
+
+/**
+ * Core command handlers
+ */
+struct irc_cmd_handler _cmd_handlers[] = {
+    {   "JOIN",     &irc_chan_on_JOIN   },
+    {   NULL,       NULL                }
+};
+
 err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, struct error_info *err)
 {
     struct irc_chan *chan;
@@ -19,12 +48,33 @@
     // store
     chan->net = net;
     chan->info = *info;
-    chan->state = IRC_CHAN_INIT;
+
+    // init
+    irc_cmd_init(&chan->handlers);
+    
+    // add handlers
+    if ((ERROR_CODE(err) = irc_cmd_add(&chan->handlers, _cmd_handlers, chan)))
+        goto error;
 
     // ok
     *chan_ptr = chan;
 
     return SUCCESS;
+
+error:
+    // cleanup
+    irc_chan_destroy(chan);
+
+    return ERROR_CODE(err);
+}
+
+void irc_chan_destroy (struct irc_chan *chan)
+{
+    // free command handlers
+    irc_cmd_free(&chan->handlers);
+
+    // free chan itself
+    free(chan);
 }
 
 err_t irc_chan_join (struct irc_chan *chan)
@@ -32,17 +82,16 @@
     err_t err;
 
     // XXX: error instead?
-    assert(chan->state == IRC_CHAN_INIT);
+    assert(!chan->state.joining && !chan->state.joined);
 
     // send JOIN message on the appropriate connection
     if ((err = irc_conn_JOIN(chan->net->conn, chan->info.channel)))
-        // XXX: state?
+        // XXX: error state?
         return err;
 
-    // ok, joining
-    chan->state = IRC_CHAN_JOINING;
+    // ok
+    chan->state.joining = true;
 
-    // done
     return SUCCESS;
 }
 
--- a/src/irc_chan.h	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_chan.h	Thu Mar 12 18:11:44 2009 +0200
@@ -10,6 +10,7 @@
 struct irc_chan;
 
 #include "irc_net.h"
+#include "irc_cmd.h"
 #include "error.h"
 #include <sys/queue.h>
 
@@ -23,17 +24,11 @@
 };
 
 /**
- * General IRC channel states
+ * Semantic IRC channel callbacks
  */
-enum irc_chan_state {
-    /** Initialized, but idle */
-    IRC_CHAN_INIT,
-
-    /** JOIN request sent */
-    IRC_CHAN_JOINING,
-
-    /** Currently joined to the channel */
-    IRC_CHAN_JOINED,
+struct irc_chan_callbacks {
+    /** Joined the channel */
+    void (*on_self_join) (struct irc_chan *chan, void *arg);
 };
 
 /**
@@ -49,8 +44,18 @@
     /** Our identifying info */
     struct irc_chan_info info;
     
-    /** Current state */
-    enum irc_chan_state state;
+    /** State flags */
+    struct {
+        /** JOIN request sent, waiting for reply */
+        bool joining;
+
+        /** Currently joined to channel */
+        bool joined;
+
+    } state;
+
+    /** General command handlers */
+    irc_cmd_handlers_t handlers;
 };
 
 /**
@@ -71,6 +76,11 @@
 err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, struct error_info *err);
 
 /**
+ * Destroy irc_chan state, without doing anything to the network
+ */
+void irc_chan_destroy (struct irc_chan *chan);
+
+/**
  * Send the initial JOIN message.
  *
  * The channel must be in the IRC_CHAN_INIT state, and will transition to the IRC_CHAN_JOINING state.
@@ -79,4 +89,5 @@
  */
 err_t irc_chan_join (struct irc_chan *chan);
 
+
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_cmd.c	Thu Mar 12 18:11:44 2009 +0200
@@ -0,0 +1,39 @@
+#include "irc_cmd.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void irc_cmd_init (irc_cmd_handlers_t *handlers)
+{
+    CHAIN_INIT(handlers);
+}
+
+err_t irc_cmd_add (irc_cmd_handlers_t *handlers, const struct irc_cmd_handler *list, void *arg)
+{
+    return chain_add(handlers, list, arg);
+}
+
+void irc_cmd_invoke (irc_cmd_handlers_t *handlers, const struct irc_line *line)
+{
+    struct chain_head *head;
+    const struct irc_cmd_handler *handler;
+    
+    CHAIN_FOREACH(handlers, head) {
+        // look up appropriate handler
+        for (handler = head->chain; handler->command; handler++) {
+            // the command is alpha-only, so normal case-insensitive cmp is fine
+            if (strcasecmp(handler->command, line->command) == 0) {
+                // invoke the func
+                handler->func(line, head->arg);
+
+                // ...only one per chain
+                break;
+            }
+        }
+    }
+}
+
+void irc_cmd_free (irc_cmd_handlers_t *handlers)
+{
+    chain_free(handlers);
+}
--- a/src/irc_cmd.h	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_cmd.h	Thu Mar 12 18:11:44 2009 +0200
@@ -7,9 +7,8 @@
  * Flexible command handlers callback lists for use with irc_lines
  */
 
-#include "irc_conn.h"
 #include "irc_line.h"
-#include <sys/queue.h>
+#include "chain.h"
 
 /**
  * Single command -> handler mapping for lookup
@@ -19,27 +18,46 @@
     const char *command;
 
     /** The handler function */
-    void (*func) (struct irc_conn *conn, const struct irc_line *line, void *arg);
+    void (*func) (const struct irc_line *line, void *arg);
 };
 
 /**
- * List item for a chain of irc_cmd_handler entries, with the context pointer
+ * @struct irc_cmd_handlers
+ *
+ * A dynamic list of irc_cmd_chain handlers
  */
-struct irc_cmd_chain {
-    /** The list of handler lookup entries */
-    struct irc_cmd_handler *handlers;
+typedef struct chain_list irc_cmd_handlers_t;
 
-    /** Opaque context arg */
-    void *arg;
+/**
+ * Initialize a irc_cmd_handlers list.
+ */
+void irc_cmd_init (irc_cmd_handlers_t *handlers);
 
-    /** Linked list */
-    STAILQ_ENTRY(irc_cmd_chain) node;
-};
+/**
+ * Add an irc_cmd_chain to the irc_cmd_handlers list for the given handlers/arg
+ */
+err_t irc_cmd_add (irc_cmd_handlers_t *handlers, const struct irc_cmd_handler *list, void *arg);
 
-/*
- * IRC command numerics
+/**
+ * Trigger irc_cmd_chain callbacks for the given irc_line
+ */
+void irc_cmd_invoke (irc_cmd_handlers_t *handlers, const struct irc_line *line);
+
+/**
+ * Cleanup an irc_cmd_handlers list
+ */
+void irc_cmd_free (irc_cmd_handlers_t *handlers);
+
+/**
+ * @group IRC command numerics
+ * @{
+ */
+
+/**
+ * 001 <nick> :Welcome to the Internet Relay Network <nick>!<user>@<host>
  */
 #define IRC_RPL_WELCOME         "001"
 
+// @}
 
 #endif /* IRC_CMD_H */
--- a/src/irc_conn.c	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_conn.c	Thu Mar 12 18:11:44 2009 +0200
@@ -1,47 +1,109 @@
 #include "irc_conn.h"
 #include "irc_cmd.h"
+#include "irc_nm.h"
 #include "log.h"
 
 #include <stdlib.h>
 #include <string.h>
 
-/*
- * "Welcome to the Internet Relay Network <nick>!<user>@<host>"
+/**
+ * @group Forward-declerations
+ * @{
  */
-static void on_RPL_WELCOME (struct irc_conn *conn, const struct irc_line *line, void *arg)
+
+/**
+ * Handle an event-based error on this IRC connection.
+ */
+static void irc_conn_handle_error (struct irc_conn *conn, struct error_info *err);
+
+/**
+ * Update irc_conn.nickname
+ */
+static err_t irc_conn_set_nickname (struct irc_conn *conn, const char *nickname)
 {
-    (void) line;
-    (void) arg;
+    struct error_info err;
+
+    // strdup
+    if ((conn->nickname = strdup(nickname)) == NULL) {
+        SET_ERROR(&err, ERR_STRDUP);
+
+        // notify
+        irc_conn_handle_error(conn, &err);
+
+        return ERROR_CODE(&err);
+    }
+
+    // ok
+    return SUCCESS;
+}
+
+// @}
+
+/**
+ * 001 <nick> :Welcome to the Internet Relay Network <nick>!<user>@<host>
+ */
+static void on_RPL_WELCOME (const struct irc_line *line, void *arg)
+{
+    struct irc_conn *conn = arg;
 
     // update state
     conn->registering = false;
     conn->registered = true;
+    
+    // set our real nickname from the message target
+    if (irc_conn_set_nickname(conn, line->args[0]))
+        return;
 
     // trigger callback
     if (conn->callbacks.on_registered)
         conn->callbacks.on_registered(conn, conn->cb_arg);
 }
 
-/*
+/**
  * PING <server1> [ <server2> ]
  *
  * Send a 'PONG <server1>` reply right away.
  */ 
-static void on_PING (struct irc_conn *conn, const struct irc_line *line, void *arg)
+static void on_PING (const struct irc_line *line, void *arg)
 {
-    (void) arg;
+    struct irc_conn *conn = arg;
 
     // just reply
     irc_conn_PONG(conn, line->args[0]);
 }
 
-/*
+/**
+ * NICK <nickname>
+ *
+ * If the prefix is us, then update our nickname
+ */
+static void on_NICK (const struct irc_line *line, void *arg)
+{
+    struct irc_conn *conn = arg;
+    char nickname[IRC_NICK_MAX];
+
+    // parse nickname, ignoring errors
+    if (irc_prefix_parse_nick(line->prefix, nickname)) {
+        log_warn("invalid prefix: %s", line->prefix);
+        return;
+    }
+
+    // ignore if it's not us
+    if (irc_cmp_nick(nickname, conn->nickname))
+        return;
+
+    // update our nickname
+    irc_conn_set_nickname(conn, nickname);
+}
+
+/**
  * Our command handlers
  */
-struct irc_cmd_handler _cmd_handlers[] = {
-    { IRC_RPL_WELCOME,  on_RPL_WELCOME      },
-    { "PING",           on_PING             },
-    { NULL,             NULL,               },
+static struct irc_cmd_handler _cmd_handlers[] = {
+    {   IRC_RPL_WELCOME,    &on_RPL_WELCOME     },
+    {   "PING",             &on_PING            },
+    {   "NICK",             &on_NICK            },
+    {   NULL,               NULL,               },
 };
 
 /**
@@ -51,8 +113,6 @@
 {
     struct irc_conn *conn = arg;
     struct irc_line line;
-    struct irc_cmd_chain *chain;
-    struct irc_cmd_handler *handler;
     int err;
     
     // log
@@ -63,21 +123,9 @@
         log_warn("invalid line: %s: %s\n", line_buf, error_name(err));
         return;
     }
-    
-    // run each handler chain
-    STAILQ_FOREACH(chain, &conn->handlers, node) {
-        // look up appropriate handler
-        for (handler = chain->handlers; handler->command; handler++) {
-            // the command is alpha-only, so normal case-insensitive cmp is fine
-            if (strcasecmp(handler->command, line.command) == 0) {
-                // invoke the func
-                handler->func(conn, &line, chain->arg);
 
-                // ...only one per chain
-                break;
-            }
-        }
-    }
+    // invoke command handlers
+    irc_cmd_invoke(&conn->handlers, &line);
 }
 
 /**
@@ -89,8 +137,9 @@
 
     // log
     log_err_info(err, "line_proto error");
-
-    // XXX: notify user
+    
+    // 'handle'
+    irc_conn_handle_error(conn, err);
 }
 
 static struct line_proto_callbacks _lp_callbacks = {
@@ -98,6 +147,11 @@
     .on_error       = &irc_conn_on_error,
 };
 
+static void irc_conn_handle_error (struct irc_conn *conn, struct error_info *err)
+{
+    // XXX: notify user callback
+}
+
 err_t irc_conn_create (struct irc_conn **conn_ptr, struct sock_stream *sock, const struct irc_conn_callbacks *callbacks, 
         void *cb_arg, struct error_info *err)
 {
@@ -112,10 +166,10 @@
     conn->cb_arg = cb_arg;
 
     // initialize command handlers
-    STAILQ_INIT(&conn->handlers);
+    irc_cmd_init(&conn->handlers);
     
     // add the core handlers 
-    if ((ERROR_CODE(err) = irc_conn_register_handler_chain(conn, _cmd_handlers, NULL)))
+    if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers(conn, _cmd_handlers, conn)))
         goto error;
 
     // create the line_proto, with our on_line handler
@@ -136,44 +190,21 @@
 
 void irc_conn_destroy (struct irc_conn *conn)
 {
-    struct irc_cmd_chain *next = STAILQ_FIRST(&conn->handlers);
-
     // release the line_proto
     if (conn->lp)
         line_proto_release(conn->lp);
-
-    // clean up any handler chains
-    while (next) {
-        struct irc_cmd_chain *node = next;
-
-        // update next
-        next = STAILQ_NEXT(node, node);
-
-        // free
-        free(node);
-    }
+    
+    // free the command handlers
+    irc_cmd_free(&conn->handlers);
 
     // free the irc_conn itself
     free(conn);
 }
 
-err_t irc_conn_register_handler_chain (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg)
+err_t irc_conn_add_cmd_handlers (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg)
 {
-    struct irc_cmd_chain *item;
-
-    // allocate the chain item
-    if ((item = calloc(1, sizeof(*item))) == NULL)
-        return ERR_CALLOC;
-
-    // store
-    item->handlers = handlers;
-    item->arg = arg;
-
-    // append
-    STAILQ_INSERT_TAIL(&conn->handlers, item, node);
-
-    // ok
-    return SUCCESS;
+    // use the irc_cmd stuff
+    return irc_cmd_add(&conn->handlers, handlers, arg);
 }
 
 err_t irc_conn_register (struct irc_conn *conn, const struct irc_conn_register_info *info)
--- a/src/irc_conn.h	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_conn.h	Thu Mar 12 18:11:44 2009 +0200
@@ -55,14 +55,20 @@
     /** Opaque argument for callbacks */
     void *cb_arg;
 
+    /** @group flags @{ */
     /** Registration request sent */
     bool registering;
 
-    /* Registered (as in, we have a working nickname)? */
+    /** Registered (as in, we have a working nickname)? */
     bool registered;
+    
+    // @}
+    
+    /** Our current real nickname */
+    char *nickname;
 
-    /* Command handlers */
-    STAILQ_HEAD(irc_conn_handlers, irc_cmd_chain) handlers;
+    /** Command handlers */
+    irc_cmd_handlers_t handlers;
 };
 
 /**
@@ -98,7 +104,7 @@
  * @param chain the array of irc_cmd_handler structs, terminated with a NULL entry
  * @param arg the context argument to use for the callbacks
  */
-err_t irc_conn_register_handler_chain (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg);
+err_t irc_conn_add_cmd_handlers (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg);
 
 /**
  * Register with the IRC server using the given registration info (initial nickname etc.)
--- a/src/irc_log.c	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_log.c	Thu Mar 12 18:11:44 2009 +0200
@@ -14,11 +14,10 @@
 
 } _ctx;
 
-static void on_PRIVMSG (struct irc_conn *conn, const struct irc_line *line, void *arg)
+static void on_PRIVMSG (const struct irc_line *line, void *arg)
 {
     struct irc_log_ctx *ctx = arg;
 
-    (void) conn;
     (void) ctx;
 
     // log it! :P
@@ -49,7 +48,7 @@
 
     // register for events
     // XXX: need irc_chan API for this
-    if ((err = irc_conn_register_handler_chain(info->channel->net->conn, _cmd_handlers, ctx)))
+    if ((err = irc_conn_add_cmd_handlers(info->channel->net->conn, _cmd_handlers, ctx)))
         return err;
 
     // ok
--- a/src/irc_net.c	Thu Mar 12 18:08:27 2009 +0200
+++ b/src/irc_net.c	Thu Mar 12 18:11:44 2009 +0200
@@ -27,6 +27,32 @@
     .on_registered  = &irc_net_conn_registered,
 };
 
+/**
+ * :nm JOIN <channel>
+ */ 
+static void irc_net_on_JOIN (const struct irc_line *line, void *arg)
+{
+    struct irc_net *net = arg;
+    struct irc_chan *chan;
+
+    // look up channel
+    if ((chan = irc_net_get_chan(net, line->args[0])) == NULL) {
+        log_warn("unkown channel: %s", line->args[0]);
+        return;
+    }
+
+    // propagate
+    irc_cmd_invoke(&chan->handlers, line);
+}
+
+/**
+ * Our irc_cmd handler list
+ */
+static struct irc_cmd_handler _cmd_handlers[] = {
+    {   "JOIN",     &irc_net_on_JOIN        },
+    {   NULL,       NULL                    }
+};
+
 err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
 {
     struct irc_net *net;
@@ -58,6 +84,10 @@
     if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err))
         goto error;
 
+    // add our command handlers
+    if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net)))
+        goto error;
+
     // register
     log_info("connected, registering");