From 29a5598ab4ccdb0c9a6646f26932cc37889a781d Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Tue, 28 Dec 2010 17:30:37 +0200
Subject: [PATCH] imap4d: speed up asynchronous mailbox notifications.

* imap4d/imap4d.h (util_format_attribute_flags): New prototype.
* imap4d/sync.c: Rewrite using flag table to keep attribute flags,
mu_mailbox_translate to convert uids to message numbers and
MU_EVT_MAILBOX_MESSAGE_EXPUNGE event to report expunged messages.
* imap4d/util.c  (_imap4d_attrlist): Remove \Recent
(util_attribute_to_type): Handle \Recent separately.
(util_format_attribute_flags): New function.
(util_print_flags): Rewrite as an extra entry point to the above.

mailboxes: New event MU_EVT_MAILBOX_MESSAGE_EXPUNGE.

* include/mailutils/observer.h: Rearrange and partially rename
event constants (all uses updated):
MU_EVT_MESSAGE_APPEND   =>   MU_EVT_MAILBOX_MESSAGE_APPEND
MU_EVT_AUTHORITY_FAILED =>   MU_EVT_FOLDER_AUTHORITY_FAILED
(MU_EVT_MAILBOX_MESSAGE_EXPUNGE): New event.
* libproto/mbox/mbox.c: Generate MU_EVT_MAILBOX_MESSAGE_EXPUNGE.
* libproto/pop/mbox.c: Likewise.
* libmailutils/base/amd.c: Likewise.
---
 imap4d/imap4d.h                |   1 +
 imap4d/sync.c                  | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 imap4d/util.c                  |  68 ++++++++++++++++++++++----------------------------------------------
 include/mailutils/observer.h   |  31 ++++++++++++++++++++-----------
 libmailutils/base/amd.c        |  10 +++++++++-
 libmailutils/mailbox/message.c |   4 +---
 libproto/imap/folder.c         |   2 +-
 libproto/mbox/mbox.c           |  11 +++++++++--
 libproto/pop/mbox.c            |   6 ++++++
 maidag/deliver.c               |   5 +++--
 10 files changed, 141 insertions(+), 258 deletions(-)

diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index 5026812..08575f1 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -370,6 +370,7 @@ extern char *util_localname (void);
 
 extern int util_wcard_match (const char *string, const char *pattern,
 			     const char *delim);
+int util_format_attribute_flags (mu_stream_t str, int flags);
 void util_print_flags (mu_attribute_t attr);
 int util_attribute_to_type (const char *item, int *type);
 int util_type_to_attribute (int type, char **attr_str);
diff --git a/imap4d/sync.c b/imap4d/sync.c
index 5901a53..1f2f8e2 100644
--- a/imap4d/sync.c
+++ b/imap4d/sync.c
@@ -21,163 +21,24 @@
 /*
 
  */
-struct _uid_table
-{
-  size_t uid;
-  size_t msgno;
-  int notify;
-  mu_attribute_t attr;
-};
-
-static struct _uid_table *uid_table;
-static size_t uid_table_count;
-static int uid_table_loaded;
-
-static void
-add_flag (char **pbuf, const char *f)
-{
-  char *abuf = *pbuf;
-  abuf = realloc (abuf, strlen (abuf) + strlen (f) + 2);
-  if (abuf == NULL)
-    imap4d_bye (ERR_NO_MEM);
-  if (*abuf)
-    strcat (abuf, " ");
-  strcat (abuf, "\\Seen");
-  *pbuf = abuf;
-}
-
-static void
-notify_flag (size_t msgno, mu_attribute_t oattr)
-{
-  mu_message_t msg = NULL;
-  mu_attribute_t nattr = NULL;
-  int status ;
-  mu_mailbox_get_message (mbox, msgno, &msg);
-  mu_message_get_attribute (msg, &nattr);
-  status = mu_attribute_is_equal (oattr, nattr);
-
-  if (status == 0)
-    {
-      char *abuf = malloc (1);;
-      if (!abuf)
-	imap4d_bye (ERR_NO_MEM);
-      *abuf = '\0';
-      if (mu_attribute_is_seen (nattr) && mu_attribute_is_read (nattr))
-	if (!mu_attribute_is_seen (oattr) && !mu_attribute_is_read (oattr))
-	  {
-	    mu_attribute_set_seen (oattr);
-	    mu_attribute_set_read (oattr);
-	    add_flag (&abuf, "\\Seen");
-	  }
-      if (mu_attribute_is_answered (nattr))
-	if (!mu_attribute_is_answered (oattr))
-	  {
-	    mu_attribute_set_answered (oattr);
-	    add_flag (&abuf, "\\Answered");
-	  }
-      if (mu_attribute_is_flagged (nattr))
-	if (!mu_attribute_is_flagged (oattr))
-	  {
-	    mu_attribute_set_flagged (oattr);
-	    add_flag (&abuf, "\\Flagged");
-	  }
-      if (mu_attribute_is_deleted (nattr))
-	if (!mu_attribute_is_deleted (oattr))
-	  {
-	    mu_attribute_set_deleted (oattr);
-	    add_flag (&abuf, "\\Deleted");
-	  }
-      if (mu_attribute_is_draft (nattr))
-	if (!mu_attribute_is_draft (oattr))
-	  {
-	    mu_attribute_set_draft (oattr);
-	    add_flag (&abuf, "\\Draft");
-	  }
-      if (mu_attribute_is_recent (nattr))
-	if (!mu_attribute_is_recent (oattr))
-	  {
-	    mu_attribute_set_recent (oattr);
-	    add_flag (&abuf, "\\Recent");
-	  }
-      if (*abuf)
-	io_untagged_response (RESP_NONE, "%lu FETCH FLAGS (%s)",
-		                 (unsigned long) msgno, abuf);
-      free (abuf);
-    }
-}
-
-/* The EXPUNGE response reports that the specified message sequence
-   number has been permanently removed from the mailbox.  The message
-   sequence number for each successive message in the mailbox is
-   immediately decremented by 1, and this decrement is reflected in
-   message sequence numbers in subsequent responses (including other
-   untagged EXPUNGE responses). */
-static void
-notify_deleted (void)
-{
-  if (uid_table)
-    {
-      size_t i;
-      size_t decr = 0;
-      for (i = 0; i < uid_table_count; i++)
-	{
-	  if (!(uid_table[i].notify))
-	    {
-	      io_untagged_response (RESP_NONE, "%lu EXPUNGED",
-			               (unsigned long) uid_table[i].msgno-decr);
-	      uid_table[i].notify = 1;
-	      decr++;
-	    }
-	}
-    }
-}
-
-
-static int
-notify_uid (size_t uid)
-{
-  if (uid_table)
-    {
-      size_t i;
-      for (i = 0; i < uid_table_count; i++)
-	{
-	  if (uid_table[i].uid == uid)
-	    {
-	      notify_flag (uid_table[i].msgno, uid_table[i].attr);
-	      uid_table[i].notify = 1;
-	      return 1;
-	    }
-	}
-    }
-  return 0;
-}
+static int *attr_table;
+static size_t attr_table_count;
+static int attr_table_loaded;
 
 static void
 free_uids (void)
 {
-  if (uid_table)
+  if (attr_table)
     {
-      size_t i;
-      for (i = 0; i < uid_table_count; i++)
-	mu_attribute_destroy (&(uid_table[i].attr), NULL);
-      free (uid_table);
-      uid_table = NULL;
+      free (attr_table);
+      attr_table = NULL;
     }
-  uid_table_count = 0;
-  uid_table_loaded = 0;
-}
-
-static void
-reset_notify (void)
-{
-  size_t i;
-
-  for (i = 0; i < uid_table_count; i++)
-    uid_table[i].notify = 0;
+  attr_table_count = 0;
+  attr_table_loaded = 0;
 }
 
 static void
-reset_uids (void)
+reread_attributes (void)
 {
   size_t total = 0;
   size_t i;
@@ -185,26 +46,24 @@ reset_uids (void)
   free_uids ();
 
   mu_mailbox_messages_count (mbox, &total);
+  if (total > attr_table_count)
+    {
+      attr_table = realloc (attr_table, sizeof (*attr_table) * total);
+      if (!attr_table)
+	imap4d_bye (ERR_NO_MEM);
+      attr_table_count = total;
+    }
+  
   for (i = 1; i <= total; i++)
     {
       mu_message_t msg = NULL;
       mu_attribute_t attr = NULL;
-      size_t uid = 0;
-      uid_table = realloc (uid_table, sizeof (*uid_table) *
-			   (uid_table_count + 1));
-      if (!uid_table)
-	imap4d_bye (ERR_NO_MEM);
+
       mu_mailbox_get_message (mbox, i, &msg);
       mu_message_get_attribute (msg, &attr);
-      mu_message_get_uid (msg, &uid);
-      uid_table[uid_table_count].uid = uid;
-      uid_table[uid_table_count].msgno = i;
-      uid_table[uid_table_count].notify = 0;
-      mu_attribute_create (&(uid_table[uid_table_count].attr), NULL);
-      mu_attribute_copy (uid_table[uid_table_count].attr, attr);
-      uid_table_count++;
+      mu_attribute_get_flags (attr, &attr_table[i-1]);
     }
-  uid_table_loaded = 1;
+  attr_table_loaded = 1;
 }
 
 static void
@@ -216,25 +75,34 @@ notify (void)
   
   mu_mailbox_messages_count (mbox, &total);
 
-  if (!uid_table)
+  if (!attr_table)
     {
       reset = 1;
-      reset_uids ();
+      reread_attributes ();
+      mu_mailbox_messages_recent (mbox, &recent);
     }
-  
-  if (uid_table)
+  else if (attr_table)
     {
       size_t i;
 
       for (i = 1; i <= total; i++)
 	{
 	  mu_message_t msg = NULL;
-	  size_t uid = 0;
+	  mu_attribute_t nattr = NULL;
+	  int nflags;
+	  
 	  mu_mailbox_get_message (mbox, i, &msg);
-	  mu_message_get_uid (msg, &uid);
-	  notify_uid (uid);
+	  mu_message_get_attribute (msg, &nattr);
+	  mu_attribute_get_flags (nattr, &nflags);
+
+	  if (nflags != attr_table[i-1])
+	    {
+	      io_sendf ("* %lu FETCH FLAGS (",  (unsigned long) i);
+	      util_format_attribute_flags (iostream, nflags);
+	      io_sendf (")\n");
+	      attr_table[i-1] = nflags;
+	    }
 	}
-      notify_deleted ();
       mu_mailbox_messages_recent (mbox, &recent);
     }
 
@@ -242,35 +110,28 @@ notify (void)
   io_untagged_response (RESP_NONE, "%lu RECENT", (unsigned long) recent);
 
   if (!reset)
-    reset_uids ();
-  else
-    reset_notify ();
+    reread_attributes ();
 }
 
 size_t
 uid_to_msgno (size_t uid)
 {
-  size_t i;
-  for (i = 0; i < uid_table_count; i++)
-    if (uid_table[i].uid == uid)
-      return uid_table[i].msgno;
-  return 0;
+  size_t msgno;
+  mu_mailbox_translate (mbox, MU_MAILBOX_UID_TO_MSGNO, uid, &msgno);
+  return msgno;
 }
 
 int
 imap4d_sync_flags (size_t msgno)
 {
-  size_t i;
-  for (i = 0; i < uid_table_count; i++)
-    if (uid_table[i].msgno == msgno)
-      {
-	mu_message_t msg = NULL;
-	mu_attribute_t attr = NULL;
-	mu_mailbox_get_message (mbox, msgno, &msg);
-	mu_message_get_attribute (msg, &attr);
-	mu_attribute_copy (uid_table[i].attr, attr);
-	break;
-      }
+  if (attr_table)
+    {
+      mu_message_t msg = NULL;
+      mu_attribute_t attr = NULL;
+      mu_mailbox_get_message (mbox, msgno, &msg);
+      mu_message_get_attribute (msg, &attr);
+      mu_attribute_get_flags (attr, &attr_table[msgno-1]);
+    }
   return 0;
 }
 
@@ -288,6 +149,19 @@ action (mu_observer_t observer, size_t type, void *data, void *action_data)
     case MU_EVT_MAILBOX_DESTROY:
       mailbox_corrupt = 0;
       break;
+
+    case MU_EVT_MAILBOX_MESSAGE_EXPUNGE:
+      /* The EXPUNGE response reports that the specified message sequence
+	 number has been permanently removed from the mailbox.  The message
+	 sequence number for each successive message in the mailbox is
+	 immediately decremented by 1, and this decrement is reflected in
+	 message sequence numbers in subsequent responses (including other
+	 untagged EXPUNGE responses). */
+      {
+	size_t *exp = data;
+	io_untagged_response (RESP_NONE, "%lu EXPUNGED",
+			      (unsigned long) (exp[0] - exp[1]));
+      }
     }
   return 0;
 }
@@ -301,8 +175,11 @@ imap4d_set_observer (mu_mailbox_t mbox)
   mu_observer_create (&observer, mbox);
   mu_observer_set_action (observer, action, mbox);
   mu_mailbox_get_observable (mbox, &observable);
-  mu_observable_attach (observable, MU_EVT_MAILBOX_CORRUPT|MU_EVT_MAILBOX_DESTROY,
-		     observer);
+  mu_observable_attach (observable,
+			MU_EVT_MAILBOX_CORRUPT|
+			MU_EVT_MAILBOX_DESTROY|
+			MU_EVT_MAILBOX_MESSAGE_EXPUNGE,
+			observer);
   mailbox_corrupt = 0;
 }
 
@@ -314,7 +191,7 @@ imap4d_sync (void)
      If it was a close we do not send any notification.  */
   if (mbox == NULL)
     free_uids ();
-  else if (!uid_table_loaded || !mu_mailbox_is_updated (mbox))
+  else if (!attr_table_loaded || !mu_mailbox_is_updated (mbox))
     {
       if (mailbox_corrupt)
 	{
@@ -338,7 +215,7 @@ imap4d_sync (void)
     {
       size_t count = 0;
       mu_mailbox_messages_count (mbox, &count);
-      if (count != uid_table_count)
+      if (count != attr_table_count)
 	notify ();
     }
   return 0;
diff --git a/imap4d/util.c b/imap4d/util.c
index 95a46d9..9c819e9 100644
--- a/imap4d/util.c
+++ b/imap4d/util.c
@@ -402,8 +402,7 @@ _imap4d_attrlist[] =
   { "\\Flagged", MU_ATTRIBUTE_FLAGGED },
   { "\\Deleted", MU_ATTRIBUTE_DELETED },
   { "\\Draft", MU_ATTRIBUTE_DRAFT },
-  { "\\Seen", MU_ATTRIBUTE_READ },
-  { "\\Recent", MU_ATTRIBUTE_RECENT },
+  { "\\Seen", MU_ATTRIBUTE_SEEN|MU_ATTRIBUTE_READ },
 };
 
 #define NATTR sizeof(_imap4d_attrlist)/sizeof(_imap4d_attrlist[0])
@@ -414,6 +413,13 @@ int
 util_attribute_to_type (const char *item, int *type)
 {
   int i;
+
+  if (mu_c_strcasecmp (item, "\\Recent") == 0)
+    {
+      *type = MU_ATTRIBUTE_RECENT;
+      return 0;
+    }
+  
   for (i = 0; i < _imap4d_nattr; i++)
     if (mu_c_strcasecmp (item, _imap4d_attrlist[i].name) == 0)
       {
@@ -423,68 +429,38 @@ util_attribute_to_type (const char *item, int *type)
   return 1;
 }
 
-/* Note: currently unused. Not needed, possibly? */
 int
-util_type_to_attribute (int type, char **attr_str)
+util_format_attribute_flags (mu_stream_t str, int flags)
 {
-  char *attr_list[NATTR];
-  int nattr = 0;
   int i;
-  size_t len = 0;
-
-  if (MU_ATTRIBUTE_IS_UNSEEN (type))
-    *attr_str = strdup ("\\Recent");
-  else
-    *attr_str = NULL;
-
+  int delim = 0;
+  
   for (i = 0; i < _imap4d_nattr; i++)
-    if (type & _imap4d_attrlist[i].flag)
+    if (flags & _imap4d_attrlist[i].flag)
       {
-	attr_list[nattr++] = _imap4d_attrlist[i].name;
-	len += 1 + strlen (_imap4d_attrlist[i].name);
+	if (delim)
+	  mu_stream_printf (str, " ");
+	mu_stream_printf (str, "%s", _imap4d_attrlist[i].name);
+	delim = 1;
       }
 
-  *attr_str = malloc (len + 1);
-  (*attr_str)[0] = 0;
-  if (*attr_str)
+  if (MU_ATTRIBUTE_IS_UNSEEN (flags))
     {
-      for (i = 0; i < nattr; i++)
-	{
-	  strcat (*attr_str, attr_list[i]);
-	  if (i != nattr - 1)
-	    strcat (*attr_str, " ");
-	}
+      if (delim)
+	mu_stream_printf (str, " ");
+      mu_stream_printf (str, "\\Recent");
     }
-
-  if (!*attr_str)
-    imap4d_bye (ERR_NO_MEM);
+  
   return 0;
 }
 
 void
 util_print_flags (mu_attribute_t attr)
 {
-  int i;
   int flags = 0;
-  int space = 0;
 
   mu_attribute_get_flags (attr, &flags);
-  for (i = 0; i < _imap4d_nattr; i++)
-    if (flags & _imap4d_attrlist[i].flag)
-      {
-	if (space)
-	  io_sendf (" ");
-	else
-	  space = 1;
-	io_sendf (_imap4d_attrlist[i].name);
-      }
-
-  if (MU_ATTRIBUTE_IS_UNSEEN (flags))
-    {
-      if (space)
-	io_sendf (" ");
-      io_sendf ("\\Recent");
-    }
+  util_format_attribute_flags (iostream, flags);
 }
 
 int
diff --git a/include/mailutils/observer.h b/include/mailutils/observer.h
index f4d6dc9..f1abe20 100644
--- a/include/mailutils/observer.h
+++ b/include/mailutils/observer.h
@@ -24,17 +24,26 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
-                                          /* Call data type: */
-#define MU_EVT_MAILBOX_DESTROY     0x001  /*  mu_mailbox_t */
-#define MU_EVT_FOLDER_DESTROY      0x002  /*  mu_folder_t */ 
-#define MU_EVT_MAILER_DESTROY      0x004  /*  mu_mailer_t */
-#define MU_EVT_MESSAGE_DESTROY     0x008  /*  mu_message_t */
-#define MU_EVT_MESSAGE_ADD         0x010  /*  size_t *: FIXME */
-#define MU_EVT_MAILBOX_PROGRESS    0x020  /*  NULL: FIXME? */
-#define MU_EVT_AUTHORITY_FAILED    0x030  /*  NULL */
-#define MU_EVT_MAILBOX_CORRUPT     0x040  /*  mu_mailbox_t */
-#define MU_EVT_MAILER_MESSAGE_SENT 0x080  /*  mu_message_t */
-#define MU_EVT_MESSAGE_APPEND      0x100  /*  mu_message_qid_t: FIXME */ 
+  /* Mailbox events */
+                                               /* Call data type: */
+#define MU_EVT_MAILBOX_DESTROY          0x001  /*  mu_mailbox_t */
+#define MU_EVT_MAILBOX_PROGRESS         0x002  /*  NULL: FIXME? */
+#define MU_EVT_MAILBOX_CORRUPT          0x004  /*  mu_mailbox_t */
+#define MU_EVT_MAILBOX_MESSAGE_APPEND   0x008  /*  mu_message_qid_t: FIXME */ 
+#define MU_EVT_MAILBOX_MESSAGE_EXPUNGE  0x010  /*  size_t [2]
+					           (message number/number of 
+					            messages removed so far) */
+  /* Folder events */
+#define MU_EVT_FOLDER_DESTROY           0x020  /*  mu_folder_t */
+#define MU_EVT_FOLDER_AUTHORITY_FAILED  0x040  /*  NULL */
+
+  /* Message events */
+#define MU_EVT_MESSAGE_DESTROY          0x080  /*  mu_message_t */
+#define MU_EVT_MESSAGE_ADD              0x100  /*  size_t *: FIXME */
+
+  /* Mailer events */
+#define MU_EVT_MAILER_DESTROY           0x200  /*  mu_mailer_t */
+#define MU_EVT_MAILER_MESSAGE_SENT      0x400  /*  mu_message_t */
   
 #define MU_OBSERVER_NO_CHECK 1
 
diff --git a/libmailutils/base/amd.c b/libmailutils/base/amd.c
index 96d55cc..0bda673 100644
--- a/libmailutils/base/amd.c
+++ b/libmailutils/base/amd.c
@@ -900,7 +900,8 @@ amd_append_message (mu_mailbox_t mailbox, mu_message_t msg)
       char *qid;
       if (amd->cur_msg_file_name (mhm, &qid) == 0)
 	{
-	  mu_observable_notify (mailbox->observable, MU_EVT_MESSAGE_APPEND,
+	  mu_observable_notify (mailbox->observable, 
+	                        MU_EVT_MAILBOX_MESSAGE_APPEND,
 				qid);
 	  free (qid);
 	}
@@ -1148,6 +1149,7 @@ amd_expunge (mu_mailbox_t mailbox)
   struct _amd_message *mhm;
   size_t i;
   int updated = amd->has_new_msg;
+  size_t expcount = 0;
   
   if (amd == NULL)
     return EINVAL;
@@ -1175,9 +1177,15 @@ amd_expunge (mu_mailbox_t mailbox)
 
 	  if (amd->delete_msg)
 	    {
+	      size_t expevt[2] = { i + 1, expcount };
 	      rc = amd->delete_msg (amd, mhm);
 	      if (rc)
 		return rc;
+
+	      mu_observable_notify (mailbox->observable,
+				    MU_EVT_MAILBOX_MESSAGE_EXPUNGE,
+				    expevt);
+	      ++expcount;
 	    }
 	  else
 	    {
diff --git a/libmailutils/mailbox/message.c b/libmailutils/mailbox/message.c
index 644cd00..684fe39 100644
--- a/libmailutils/mailbox/message.c
+++ b/libmailutils/mailbox/message.c
@@ -534,9 +534,7 @@ mu_message_destroy (mu_message_t *pmsg, void *owner)
 	 is very unfortunate.
 
 	 The `owner' stuff is a leftover from older mailutils versions.
-	 There is an ongoing attempt to remove it in the stream-cleanup
-	 branch. When it is ready, it will be merged to the HEAD and this
-	 will finally resolve this issue. */
+	 We are heading to removing it altogether. */
       if (msg->ref > 0)
 	msg->ref--;
       if ((msg->owner && msg->owner == owner)
diff --git a/libproto/imap/folder.c b/libproto/imap/folder.c
index ce15084..8f750b3 100644
--- a/libproto/imap/folder.c
+++ b/libproto/imap/folder.c
@@ -2589,7 +2589,7 @@ imap_parse (f_imap_t f_imap)
 		{
 		  mu_observable_t observable = NULL;
 		  mu_folder_get_observable (f_imap->folder, &observable);
-		  mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED,
+		  mu_observable_notify (observable, MU_EVT_FOLDER_AUTHORITY_FAILED,
 					NULL);
 		  status = MU_ERR_AUTH_FAILURE;
 		}
diff --git a/libproto/mbox/mbox.c b/libproto/mbox/mbox.c
index 6e7cbed..4b1028c 100644
--- a/libproto/mbox/mbox.c
+++ b/libproto/mbox/mbox.c
@@ -1143,7 +1143,8 @@ mbox_append_message (mu_mailbox_t mailbox, mu_message_t msg)
     {
       char *buf = NULL;
       mu_asprintf (&buf, "%lu", (unsigned long) size);
-      mu_observable_notify (mailbox->observable, MU_EVT_MESSAGE_APPEND, buf);
+      mu_observable_notify (mailbox->observable, 
+                            MU_EVT_MAILBOX_MESSAGE_APPEND, buf);
       free (buf);
     }
   
@@ -1184,7 +1185,8 @@ mbox_expunge_unlocked (mu_mailbox_t mailbox, size_t dirty, int remove_deleted,
   size_t save_imapbase = 0;  /* uidvalidity is save in the first message.  */
   mu_off_t start_off;
   mu_off_t size;
-
+  size_t expcount = 0;
+  
   /* Set the marker position.  */
   start_off = mud->umessages[dirty]->envel_from;
 
@@ -1194,6 +1196,11 @@ mbox_expunge_unlocked (mu_mailbox_t mailbox, size_t dirty, int remove_deleted,
       
       if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
 	{
+	  size_t expevt[2] = { i + 1, expcount };
+	  mu_observable_notify (mailbox->observable,
+				MU_EVT_MAILBOX_MESSAGE_EXPUNGE,
+				expevt);
+	  expcount++;
 	  mu_message_destroy (&mum->message, mum);
 	  /* We save the uidvalidity in the first message, if it is being
 	     deleted we need to move the uidvalidity to the first available
diff --git a/libproto/pop/mbox.c b/libproto/pop/mbox.c
index 30570f1..8311152 100644
--- a/libproto/pop/mbox.c
+++ b/libproto/pop/mbox.c
@@ -864,6 +864,7 @@ pop_expunge (mu_mailbox_t mbox)
   struct _pop3_mailbox *mpd = mbox->data;
   int status = 0;
   size_t i;
+  size_t expcount = 0;
   
   if (mpd == NULL)
     return EINVAL;
@@ -879,9 +880,14 @@ pop_expunge (mu_mailbox_t mbox)
 	  (mpm->flags & _POP3_MSG_ATTRSET) &&
 	  (mpm->attr_flags & MU_ATTRIBUTE_DELETED))
 	{
+	  size_t expevt[2] = { i + 1, expcount };
 	  status = mu_pop3_dele (mpd->pop3, mpm->num);
 	  if (status)
 	    break;
+	  mu_observable_notify (mbox->observable,
+				MU_EVT_MAILBOX_MESSAGE_EXPUNGE,
+				&expevt);
+	  ++expcount;
 	}
     }
   return 0;
diff --git a/maidag/deliver.c b/maidag/deliver.c
index 568ec51..057bf11 100644
--- a/maidag/deliver.c
+++ b/maidag/deliver.c
@@ -132,7 +132,7 @@ static const char *biff_user_name;
 static int
 notify_action (mu_observer_t obs, size_t type, void *data, void *action_data)
 {
-  if (type == MU_EVT_MESSAGE_APPEND && biff_user_name)
+  if (type == MU_EVT_MAILBOX_MESSAGE_APPEND && biff_user_name)
     {
       mu_message_qid_t qid = data;
       mu_mailbox_t mbox = mu_observer_get_owner (obs);
@@ -183,7 +183,8 @@ attach_notify (mu_mailbox_t mbox)
       mu_observer_create (&observer, mbox);
       mu_observer_set_action (observer, notify_action, mbox);
       mu_mailbox_get_observable (mbox, &observable);
-      mu_observable_attach (observable, MU_EVT_MESSAGE_APPEND, observer);
+      mu_observable_attach (observable, MU_EVT_MAILBOX_MESSAGE_APPEND, 
+                            observer);
     }
 }  
 
--
libgit2 0.24.0