# Small patch to Cyrus IMAP 2.2.4 which modifies \Seen state handling to # make it compatible with Outlook Express. OE makes two connections to a # given mailfolder: one generates indexes while the other fetches messages. # Unfortunately it gets confused if \Seen updates caused by the message # stream aren't immediately flushed and picked up by the index stream. # # This patch is a 2.2.4 port from the patch found here: # http://www-uxsup.csx.cam.ac.uk/~dpc22/cyrus/patches/2.1.16/OutLookExpress-seenstate.patch # diff -Naur cyrus-imapd-2.2.4.orig/imap/imapd.c cyrus-imapd-2.2.4/imap/imapd.c --- cyrus-imapd-2.2.4.orig/imap/imapd.c Thu May 6 20:46:21 2004 +++ cyrus-imapd-2.2.4/imap/imapd.c Fri May 21 02:10:25 2004 @@ -3063,6 +3063,10 @@ snprintf(mytime, sizeof(mytime), "%2.3f", (clock() - start) / (double) CLOCKS_PER_SEC); + /* Checkpoint \Seen immediately after each FETCH completes. Checks for + * changes from other processes at the same time */ + index_check_existing(imapd_mailbox, usinguid, 1); + if (r) { prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag, error_message(r), mytime); @@ -3184,7 +3188,7 @@ index_fetch(imapd_mailbox, msgno, 0, &fetchargs, &fetchedsomething); - index_check(imapd_mailbox, 0, 0); + index_check_existing(imapd_mailbox, 0, 1); if (fetchedsomething) { prot_printf(imapd_out, "%s OK %s\r\n", tag, @@ -3321,7 +3325,9 @@ flag, nflags); if (usinguid) { - index_check(imapd_mailbox, 1, 0); + index_check(imapd_mailbox, 1, 1); /* Check \Seen too */ + } else { + index_check_existing(imapd_mailbox, 0, 1); } if (r) { diff -Naur cyrus-imapd-2.2.4.orig/imap/imapd.h cyrus-imapd-2.2.4/imap/imapd.h --- cyrus-imapd-2.2.4.orig/imap/imapd.h Wed Oct 22 20:50:07 2003 +++ cyrus-imapd-2.2.4/imap/imapd.h Fri May 21 02:06:02 2004 @@ -233,6 +233,8 @@ extern void index_operatemailbox(struct mailbox *mailbox); extern void index_check(struct mailbox *mailbox, int usinguid, int checkseen); +extern void +index_check_existing(struct mailbox *mailbox, int usinguid, int checkseen); extern void index_checkseen(struct mailbox *mailbox, int quiet, int usinguid, int oldexists); diff -Naur cyrus-imapd-2.2.4.orig/imap/index.c cyrus-imapd-2.2.4/imap/index.c --- cyrus-imapd-2.2.4.orig/imap/index.c Wed Apr 21 19:40:48 2004 +++ cyrus-imapd-2.2.4/imap/index.c Fri May 21 02:06:02 2004 @@ -425,6 +425,45 @@ } } +/* Nasty hack to report system + user flags updates without checking for + * new mail or expunge (relies on index atomic rewrite+rename for expunge). + * + * Needed to keep Outlook Express happy without breaking IMAP concurrent + * access regime which (quite correctly) prohibits unsolicited EXPUNGE and + * EXIST responses for non-UID versions of FETCH and STORE. Otherwise you + * can end up with hilarous situations such as: + * + * . FETCH 2 fast + * * EXPUNGE 1 <-- from concurrent session. + * . FETCH (data relating to previous message _3_, if it exists) + * + */ + +void +index_check_existing(struct mailbox *mailbox, int usinguid, int checkseen) +{ + int msgno, i; + bit32 user_flags[MAX_USER_FLAGS/32]; + + if (imapd_exists == -1) + return; + + if (checkseen) + index_checkseen(mailbox, 0, usinguid, imapd_exists); + + for (msgno = 1; msgno <= imapd_exists; msgno++) { + if (flagreport[msgno] < LAST_UPDATED(msgno)) { + for (i = 0; i < VECTOR_SIZE(user_flags); i++) { + user_flags[i] = USER_FLAGS(msgno, i); + } + index_fetchflags(mailbox, msgno, SYSTEM_FLAGS(msgno), user_flags, + LAST_UPDATED(msgno)); + if (usinguid) prot_printf(imapd_out, " UID %u", UID(msgno)); + prot_printf(imapd_out, ")\r\n"); + } + } +} + /* * Checkpoint the user's \Seen state *