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
--- 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");