From f251110c5548ca0fe67e34d492351d48e292adf9 Mon Sep 17 00:00:00 2001 From: Bron Gondwana Date: Wed, 24 Dec 2008 23:30:38 +1100 Subject: [PATCH] GUID IMAP COMMANDS This patch factors out stuff we used to have in the old MD5UUIDs patch, the following FETCH responses in imapd: FETCH DIGEST.SHA1 => 40 character hex string (message sha1) FETCH RFC822.SHA1 => 40 character hex string (message sha1, calculated) FETCH RFC822.FILESIZE => size of actual file on disk (via stat or mmap) It also adds a capability string item: "DIGEST=SHA1" Totally non-standard of course, but way useful for our replication checking scripts. Embrace and extend 'r' us. Anyone feel like writing an RFC for fetching the digest of a message via IMAP? If the server calculated it on delivery and cached it then you'd have a great way to clean up after a UIDVALIDITY change or other destabilising event without having to fetch every message again. (that would be me - I'm going to RFC the "DIGEST.SHA1" bit) --- imap/imapd.c | 14 ++++++++++++++ imap/imapd.h | 5 ++++- imap/index.c | 34 +++++++++++++++++++++++++++++++++- imap/version.h | 2 +- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/imap/imapd.c b/imap/imapd.c index f20b40c..8acb36e 100644 --- a/imap/imapd.c +++ b/imap/imapd.c @@ -3963,6 +3963,13 @@ void cmd_fetch(char *tag, char *sequence, int usinguid) else goto badatt; break; + case 'D': + if (!strcmp(fetchatt.s, "DIGEST.SHA1")) { + fetchitems |= FETCH_GUID; + } + else goto badatt; + break; + case 'E': if (!strcmp(fetchatt.s, "ENVELOPE")) { fetchitems |= FETCH_ENVELOPE; @@ -3997,6 +4004,7 @@ void cmd_fetch(char *tag, char *sequence, int usinguid) } else goto badatt; break; + case 'R': if (!strcmp(fetchatt.s, "RFC822")) { fetchitems |= FETCH_RFC822|FETCH_SETSEEN; @@ -4013,6 +4021,12 @@ void cmd_fetch(char *tag, char *sequence, int usinguid) else if (!strcmp(fetchatt.s, "RFC822.TEXT")) { fetchitems |= FETCH_TEXT|FETCH_SETSEEN; } + else if (!strcmp(fetchatt.s, "RFC822.SHA1")) { + fetchitems |= FETCH_SHA1; + } + else if (!strcmp(fetchatt.s, "RFC822.FILESIZE")) { + fetchitems |= FETCH_FILESIZE; + } else if (!strcmp(fetchatt.s, "RFC822.TEXT.PEEK")) { fetchitems |= FETCH_TEXT; } diff --git a/imap/imapd.h b/imap/imapd.h index 6535369..ed34399 100644 --- a/imap/imapd.h +++ b/imap/imapd.h @@ -110,7 +110,10 @@ enum { FETCH_SETSEEN = (1<<10), /* FETCH_UNCACHEDHEADER = (1<<11) -- obsolete */ FETCH_IS_PARTIAL = (1<<12), /* this is the PARTIAL command */ - FETCH_MODSEQ = (1<<13) + FETCH_MODSEQ = (1<<13), + FETCH_GUID = (1<<14), + FETCH_SHA1 = (1<<15), + FETCH_FILESIZE = (1<<16) }; enum { diff --git a/imap/index.c b/imap/index.c index 556a130..f11e639 100644 --- a/imap/index.c +++ b/imap/index.c @@ -2520,7 +2520,7 @@ static int index_fetchreply(struct mailbox *mailbox, } /* Open the message file if we're going to need it */ - if ((fetchitems & (FETCH_HEADER|FETCH_TEXT|FETCH_RFC822)) || + if ((fetchitems & (FETCH_HEADER|FETCH_TEXT|FETCH_SHA1|FETCH_RFC822)) || fetchargs->cache_atleast > CACHE_VERSION(msgno) || fetchargs->binsections || fetchargs->sizesections || fetchargs->bodysections) { @@ -2557,6 +2557,11 @@ static int index_fetchreply(struct mailbox *mailbox, prot_printf(imapd_out, "%cUID %u", sepchar, UID(msgno)); sepchar = ' '; } + if (fetchitems & FETCH_GUID) { + prot_printf(imapd_out, "%cDIGEST.SHA1 %s", sepchar, message_guid_encode(GUID(msgno))); + sepchar = ' '; + } + if (fetchitems & FETCH_INTERNALDATE) { time_t msgdate = INTERNALDATE(msgno); char datebuf[30]; @@ -2576,6 +2581,33 @@ static int index_fetchreply(struct mailbox *mailbox, prot_printf(imapd_out, "%cRFC822.SIZE %u", sepchar, SIZE(msgno)); sepchar = ' '; } + if (fetchitems & FETCH_FILESIZE) { + if (msg_base) + prot_printf(imapd_out, "%cRFC822.FILESIZE %u", sepchar, msg_size); + else { + char fname[MAX_MAILBOX_PATH+1]; + struct stat sbuf; + /* Find the size of the message file */ + strlcpy(fname, mailbox->path, sizeof(fname)); + strlcat(fname, "/", sizeof(fname)); + mailbox_message_get_fname(mailbox, UID(msgno), + fname + strlen(fname), + sizeof(fname) - strlen(fname)); + if (stat(fname, &sbuf) == -1) { + syslog(LOG_ERR, "IOERROR: stat on %s: %m", fname); + prot_printf(imapd_out, "%cRFC822.FILESIZE NIL", sepchar); + } + else + prot_printf(imapd_out, "%cRFC822.FILESIZE %u", sepchar, sbuf.st_size); + } + sepchar = ' '; + } + if (fetchitems & FETCH_SHA1) { + struct message_guid tmpguid; + message_guid_generate(&tmpguid, msg_base, msg_size); + prot_printf(imapd_out, "%cRFC822.SHA1 %s", sepchar, message_guid_encode(&tmpguid)); + sepchar = ' '; + } if (fetchitems & FETCH_ENVELOPE) { if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) { prot_printf(imapd_out, "%cENVELOPE ", sepchar); diff --git a/imap/version.h b/imap/version.h index 268d2b2..83ca377 100644 --- a/imap/version.h +++ b/imap/version.h @@ -69,7 +69,7 @@ enum { "NO_ATOMIC_RENAME UNSELECT " \ "CHILDREN MULTIAPPEND BINARY " \ "SORT SORT=MODSEQ THREAD=ORDEREDSUBJECT THREAD=REFERENCES " \ - "ANNOTATEMORE CATENATE CONDSTORE SCAN" + "ANNOTATEMORE CATENATE CONDSTORE SCAN DIGEST=SHA1" /* Values for ID processing */ -- 1.5.6.5