special-case recieved user-mode messages, add irc_conn_self function
authorTero Marttila <terom@fixme.fi>
Thu, 23 Apr 2009 21:05:52 +0300
changeset 150 e8018446b336
parent 149 549913bbe0d2
child 151 b0b01420b99d
special-case recieved user-mode messages, add irc_conn_self function
src/irc_conn.c
src/irc_conn.h
src/irc_net.c
--- a/src/irc_conn.c	Thu Apr 23 21:05:27 2009 +0300
+++ b/src/irc_conn.c	Thu Apr 23 21:05:52 2009 +0300
@@ -331,6 +331,11 @@
     return SUCCESS;
 }
 
+bool irc_conn_self (struct irc_conn *conn, const char *nickname)
+{
+    return conn && nickname && conn->nickname && (irc_cmp_nick(conn->nickname, nickname) == 0);
+}
+
 err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line)
 {
     // just proxy off to irc_quuee
--- 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);