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