src/irc_chan.c
changeset 74 11ec458d1cbf
parent 73 2780a73c71f3
child 75 ff6272398d2e
--- a/src/irc_chan.c	Thu Mar 26 22:03:20 2009 +0200
+++ b/src/irc_chan.c	Thu Mar 26 22:54:25 2009 +0200
@@ -27,7 +27,7 @@
     if (irc_chan_get_user(chan, nickname))
         return SUCCESS;
     
-    // lookup/create the irc_user state
+    // lookup/create the irc_user state, incrementing the refcount
     if ((err = irc_net_get_user(chan->net, &user, nickname)))
         return err;
 
@@ -48,6 +48,21 @@
 }
 
 /**
+ * Remove an irc_chan_user previously added by irc_chan_add_user().
+ */
+static void irc_chan_remove_user (struct irc_chan *chan, struct irc_chan_user *chan_user)
+{
+    // put the irc_user reference back, decrementing refcount
+    irc_net_put_user(chan->net, chan_user->user);
+
+    // remove from list
+    LIST_REMOVE(chan_user, chan_users);
+
+    // free
+    free(chan_user);
+}
+
+/**
  * :nm JOIN <channel>
  */ 
 static void irc_chan_on_JOIN (const struct irc_line *line, void *arg)
@@ -82,6 +97,49 @@
 }
 
 /**
+ * Someone, or us ourselves, left a channel
+ *
+ * :nm PART <channel> [<message>]
+ */
+static void irc_chan_on_PART (const struct irc_line *line, void *arg)
+{
+    struct irc_chan *chan = arg;
+    err_t err;
+
+    const char *msg = line->args[1];
+
+    // us?
+    if (irc_prefix_cmp_nick(line->prefix, chan->net->conn->nickname) == 0) {
+        // twiddle state
+        chan->joined = false;
+        chan->parted = true;
+
+        // invoke callback
+        IRC_CHAN_INVOKE_CALLBACK(chan, on_self_part);
+
+    } else {
+        // someone else
+        char prefix_buf[IRC_PREFIX_MAX];
+        struct irc_nm nm;
+        struct irc_chan_user *chan_user;
+        
+        // parse the nickname
+        if ((err = irc_nm_parse(&nm, prefix_buf, line->prefix)))
+            return log_warn("invalid prefix: %s", line->prefix);
+        
+        // invoke callback (source, msg)
+        IRC_CHAN_INVOKE_CALLBACK(chan, on_part, &nm, msg);
+
+        // look up the irc_chan_user
+        if ((chan_user = irc_chan_get_user(chan, nm.nickname)) == NULL)
+            return log_warn("PART'd user not on channel: %s, %s", irc_chan_name(chan), nm.nickname);
+
+        // remove them
+        irc_chan_remove_user(chan, chan_user);
+    }
+}
+
+/**
  * :nm PRIVMSG <channel> <message>
  */
 static void irc_chan_on_PRIVMSG (const struct irc_line *line, void *arg)
@@ -156,6 +214,7 @@
  */
 struct irc_cmd_handler _cmd_handlers[] = {
     {   "JOIN",             &irc_chan_on_JOIN           },
+    {   "PART",             &irc_chan_on_PART           },
     {   "PRIVMSG",          &irc_chan_on_PRIVMSG        },
     {   IRC_RPL_NAMREPLY,   &irc_chan_on_RPL_NAMREPLY   },
     {   IRC_RPL_ENDOFNAMES, &irc_chan_on_RPL_ENDOFNAMES },
@@ -197,7 +256,12 @@
 
 void irc_chan_destroy (struct irc_chan *chan)
 {
-    // XXX: free chan_users list
+    struct irc_chan_user *chan_user;
+
+    // free users
+    while ((chan_user = LIST_FIRST(&chan->users))) {
+        irc_chan_remove_user(chan, chan_user);
+    }
 
     // free command handlers
     irc_cmd_free(&chan->handlers);