# HG changeset patch # User Tero Marttila # Date 1236874304 -7200 # Node ID 4fe4a3c4496e206ef15982b83b05336f0255463c # Parent 791d7a5532e2a41cf6e61be37ecaf2eedd689c9f 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 diff -r 791d7a5532e2 -r 4fe4a3c4496e Makefile --- 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 diff -r 791d7a5532e2 -r 4fe4a3c4496e src/error.c --- 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 }; diff -r 791d7a5532e2 -r 4fe4a3c4496e src/error.h --- 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, diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_chan.c --- 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 #include @@ -8,6 +10,33 @@ return chan->info.channel; } +/** + * :nm JOIN + */ +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; } diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_chan.h --- 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 @@ -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 diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_cmd.c --- /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 +#include + +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); +} diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_cmd.h --- 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 +#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 :Welcome to the Internet Relay Network !@ */ #define IRC_RPL_WELCOME "001" +// @} #endif /* IRC_CMD_H */ diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_conn.c --- 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 #include -/* - * "Welcome to the Internet Relay Network !@" +/** + * @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 :Welcome to the Internet Relay Network !@ + */ +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 [ ] * * Send a 'PONG ` 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 + * + * 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) diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_conn.h --- 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.) diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_log.c --- 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 diff -r 791d7a5532e2 -r 4fe4a3c4496e src/irc_net.c --- 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 + */ +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");