--- a/src/irc_conn.h Thu Apr 23 21:05:27 2009 +0300
+++ b/src/irc_conn.h Thu Apr 23 21:05:52 2009 +0300
@@ -209,6 +209,14 @@
err_t irc_conn_register (struct irc_conn *conn, const struct irc_conn_register_info *info);
/**
+ * Compare the given nickname against our current nickname, returning true if it represents ourselves.
+ *
+ * As a convenience function, this is NULL-safe - it can safely be called with a NULL conn, NULL nickname, or a
+ * conn that doesn't yet have a nickname, and it will simply return false.
+ */
+bool irc_conn_self (struct irc_conn *conn, const char *nickname);
+
+/**
* Send a generic IRC message by formatting the given irc_line and sending it as a line.
*
* Use the irc_conn_COMMAND functions defined below for a more convenient interface.
--- a/src/irc_net.c Thu Apr 23 21:05:27 2009 +0300
+++ b/src/irc_net.c Thu Apr 23 21:05:52 2009 +0300
@@ -218,6 +218,25 @@
}
/**
+ * :nm MODE <target> [<stuff> [ ... ]]
+ *
+ * If target is ourselves, handle as a private umode, otherwise, propagate to channel
+ */
+static void irc_net_on_MODE (const struct irc_line *line, void *arg)
+{
+ struct irc_net *net = arg;
+
+ if (irc_conn_self(net->conn, line->args[0]))
+ // don't really care about our umode...
+ log_info("mode set: %s -> %s %s %s...", line->source->nickname, line->args[1], line->args[2], line->args[3]);
+
+ else
+ // it's a channel mode
+ irc_net_propagate_chan(net, line, line->args[0]);
+
+}
+
+/**
* Our irc_cmd handler list
*/
static struct irc_cmd_handler _cmd_handlers[] = {
@@ -225,7 +244,7 @@
{ "QUIT", &irc_net_on_chanuser },
{ "JOIN", &irc_net_on_chan0 },
{ "PART", &irc_net_on_chan0 },
- { "MODE", &irc_net_on_chan0 },
+ { "MODE", &irc_net_on_MODE },
{ "TOPIC", &irc_net_on_chan0 },
{ "KICK", &irc_net_on_chan0 },
{ IRC_RPL_NAMREPLY, &irc_net_on_chan2 },
@@ -242,26 +261,45 @@
};
/**
- * The given socket is now connected, so create the irc_conn and bind it in to us
+ * The given socket is now connected, so create the irc_conn and bind it in to us.
+ *
+ * If this fails, this will clean up any partial state, including sock.
*/
static err_t irc_net_connected (struct irc_net *net, struct sock_stream *sock, struct error_info *err)
{
// create the irc connection state
if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err))
- return ERROR_CODE(err);
+ goto error;
// add our command handlers
if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net)))
- return ERROR_CODE(err);
+ goto error;
// register
if ((ERROR_CODE(err) = irc_conn_register(net->conn, &net->info.register_info)))
- return ERROR_CODE(err);
+ goto error;
// ok
return SUCCESS;
+
+error:
+ if (!net->conn) {
+ // cleanup sock ourselves
+ sock_stream_release(sock);
+
+ } else {
+ // cleanup the partial stuff
+ irc_conn_destroy(net->conn);
+
+ net->conn = NULL;
+ }
+
+ return ERROR_CODE(err);
}
+/**
+ * Our sock_*_connect_async callback
+ */
static void irc_net_on_connect (struct sock_stream *sock, struct error_info *conn_err, void *arg)
{
struct irc_net *net = arg;
@@ -280,21 +318,18 @@
// XXX: cleanup
}
-err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
+/**
+ * Connect and create a new irc_conn based on our irc_net_info.
+ */
+static err_t irc_net_connect (struct irc_net *net, struct error_info *err)
{
- struct irc_net *net;
+ struct irc_net_info *info = &net->info;
struct sock_stream *sock = NULL;
-
- // allocate
- if ((net = calloc(1, sizeof(*net))) == NULL)
- return SET_ERROR(err, ERR_CALLOC);
- // initialize
- // XXX: info shouldn't be copied directly
- net->info = *info;
- TAILQ_INIT(&net->channels);
- LIST_INIT(&net->users);
+ // sanity check
+ assert(!net->conn);
+ // connect based on what's known
if (info->raw_sock) {
log_debug("connected using raw socket: %p", info->raw_sock);
@@ -308,12 +343,12 @@
} else if (info->ssl_cred) {
// aquire a ref
// NOTE: before any error handling
- sock_ssl_client_cred_get(net->info.ssl_cred);
+ sock_ssl_client_cred_get(info->ssl_cred);
log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
// connect
- if (sock_ssl_connect_async(&sock, info->hostname, info->service, net->info.ssl_cred, &irc_net_on_connect, net, err))
+ if (sock_ssl_connect_async(&sock, info->hostname, info->service, info->ssl_cred, &irc_net_on_connect, net, err))
goto error;
} else {
@@ -326,15 +361,36 @@
}
// ok
+ return SUCCESS;
+
+error:
+ return ERROR_CODE(err);
+}
+
+err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
+{
+ struct irc_net *net;
+
+ // allocate
+ if ((net = calloc(1, sizeof(*net))) == NULL)
+ return SET_ERROR(err, ERR_CALLOC);
+
+ // initialize
+ // XXX: we need to copy *info's fields
+ net->info = *info;
+ TAILQ_INIT(&net->channels);
+ LIST_INIT(&net->users);
+
+ // initial connect
+ if (irc_net_connect(net, err))
+ goto error;
+
+ // ok
*net_ptr = net;
return SUCCESS;
error:
- if (sock && !net->conn)
- // we need to clean up the socket ourself
- sock_stream_release(sock);
-
// cleanup main
irc_net_destroy(net);