]> code.citadel.org Git - citadel.git/commitdiff
* IMAP RFC822 FETCH caching
authorArt Cancro <ajc@citadel.org>
Tue, 9 Mar 2004 15:28:29 +0000 (15:28 +0000)
committerArt Cancro <ajc@citadel.org>
Tue, 9 Mar 2004 15:28:29 +0000 (15:28 +0000)
citadel/imap_fetch.c
citadel/imap_misc.c
citadel/serv_imap.c
citadel/serv_imap.h

index 007db16832461ecafaca02c33f7aca256360d723..a4e84adf17edee0cd6825e0a3275633e22bf4cf2 100644 (file)
@@ -63,8 +63,6 @@ struct imap_fetch_part {
  * Individual field functions for imap_do_fetch_msg() ...
  */
 
-
-
 void imap_fetch_uid(int seq) {
        cprintf("UID %ld", IMAP->msgids[seq-1]);
 }
@@ -115,31 +113,51 @@ void imap_fetch_internaldate(struct CtdlMessage *msg) {
  *     "RFC822.SIZE"   size of translated message
  *     "RFC822.TEXT"   body only (without leading blank line)
  */
-void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
+void imap_fetch_rfc822(long msgnum, char *whichfmt) {
        char buf[1024];
        char *ptr;
        long headers_size, text_size, total_size;
        long bytes_remaining = 0;
        long blocksize;
-       FILE *tmp;
+       FILE *tmp = NULL;
 
-       tmp = tmpfile();
-       if (tmp == NULL) {
-               lprintf(CTDL_CRIT, "Cannot open temp file: %s\n",
-                               strerror(errno));
-               return;
+       /* Cache the most recent RFC822 FETCH because some clients like to
+        * fetch in pieces, and we don't want to have to go back to the
+        * message store for each piece.
+        */
+       if ((IMAP->cached_fetch != NULL) && (IMAP->cached_msgnum == msgnum)) {
+               /* Good to go! */
+               tmp = IMAP->cached_fetch;
+       }
+       else if ((IMAP->cached_fetch != NULL) && (IMAP->cached_msgnum != msgnum)) {
+               /* Some other message is cached -- free it */
+               fclose(IMAP->cached_fetch);
+               IMAP->cached_fetch == NULL;
+               IMAP->cached_msgnum = (-1);
+               tmp = NULL;
        }
 
-       /*
-        * Load the message into a temp file for translation
-        * and measurement
+       /* At this point, we now can fetch and convert the message iff it's not
+        * the one we had cached.
         */
-       CtdlRedirectOutput(tmp, -1);
-       CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
-                               HEADERS_ALL, 0, 1);
-       CtdlRedirectOutput(NULL, -1);
-       if (!is_valid_message(msg)) {
-               lprintf(CTDL_ERR, "WARNING: output clobbered the message!\n");
+       if (tmp == NULL) {
+               tmp = tmpfile();
+               if (tmp == NULL) {
+                       lprintf(CTDL_CRIT, "Cannot open temp file: %s\n",
+                                       strerror(errno));
+                       return;
+               }
+       
+               /*
+               * Load the message into a temp file for translation
+               * and measurement
+               */
+               CtdlRedirectOutput(tmp, -1);
+               CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1);
+               CtdlRedirectOutput(NULL, -1);
+
+               IMAP->cached_fetch = tmp;
+               IMAP->cached_msgnum = msgnum;
        }
 
        /*
@@ -163,7 +181,6 @@ void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
 
        if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
                cprintf("RFC822.SIZE %ld", total_size);
-               fclose(tmp);
                return;
        }
 
@@ -191,7 +208,6 @@ void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
                bytes_remaining = bytes_remaining - blocksize;
        }
 
-       fclose(tmp);
 }
 
 
@@ -839,22 +855,35 @@ void imap_fetch_bodystructure (long msgnum, char *item,
 }
 
 
-
-
-
-
 /*
  * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
- * individual message, once it has been successfully loaded from disk.
+ * individual message, once it has been selected for output.
  */
-void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
+void imap_do_fetch_msg(int seq,
                        int num_items, char **itemlist) {
        int i;
+       struct CtdlMessage *msg = NULL;
+
 
        cprintf("* %d FETCH (", seq);
 
        for (i=0; i<num_items; ++i) {
 
+               if (!strcasecmp(itemlist[i], "RFC822")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+               else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+               else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+               else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+
+               /* Not an RFC822.* fetch item?  Load the message into memory. */
+               msg = CtdlFetchMessage(IMAP->msgids[seq-1]);
                if (!strncasecmp(itemlist[i], "BODY[", 5)) {
                        imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
                                        0, msg);
@@ -876,18 +905,6 @@ void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
                else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
                        imap_fetch_internaldate(msg);
                }
-               else if (!strcasecmp(itemlist[i], "RFC822")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
-               }
-               else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
-               }
-               else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
-               }
-               else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
-               }
                else if (!strcasecmp(itemlist[i], "UID")) {
                        imap_fetch_uid(seq);
                }
@@ -896,6 +913,9 @@ void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
        }
 
        cprintf(")\r\n");
+       if (msg != NULL) {
+               CtdlFreeMessage(msg);
+       }
 }
 
 
@@ -906,18 +926,12 @@ void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
  */
 void imap_do_fetch(int num_items, char **itemlist) {
        int i;
-       struct CtdlMessage *msg;
-
-       if (IMAP->num_msgs > 0)
-        for (i = 0; i < IMAP->num_msgs; ++i)
-         if (IMAP->flags[i] & IMAP_SELECTED) {
-               msg = CtdlFetchMessage(IMAP->msgids[i]);
-               if (msg != NULL) {
-                       imap_do_fetch_msg(i+1, msg, num_items, itemlist);
-                       CtdlFreeMessage(msg);
-               }
-               else {
-                       cprintf("* %d FETCH <internal error>\r\n", i+1);
+
+       if (IMAP->num_msgs > 0) {
+               for (i = 0; i < IMAP->num_msgs; ++i) {
+                       if (IMAP->flags[i] & IMAP_SELECTED) {
+                               imap_do_fetch_msg(i+1, num_items, itemlist);
+                       }
                }
        }
 }
index dfa41e7d4eb772474cbbf360d790258a91704b59..e9d33ad950d2c2a7a24003e58b6850fa277c1f27 100644 (file)
@@ -211,8 +211,11 @@ void imap_print_express_messages(void) {
  */
 void imap_append(int num_parms, char *parms[]) {
        size_t literal_length;
+       size_t bytes_transferred;
+       size_t stripped_length = 0;
        struct CtdlMessage *msg;
-       int ret;
+       int ret = 0;
+       size_t blksize;
        char roomname[ROOMNAMELEN];
        char buf[SIZ];
        char savedroom[ROOMNAMELEN];
@@ -249,7 +252,27 @@ void imap_append(int num_parms, char *parms[]) {
        cprintf("+ Transmit message now.\r\n");
        lprintf(CTDL_DEBUG, "imap_append() expecting %d bytes\n",
                literal_length);
-       ret = client_read(IMAP->transmitted_message, literal_length);
+
+       bytes_transferred = 0;
+
+       do {
+               blksize = literal_length - bytes_transferred;
+               if (blksize > SIZ) blksize = SIZ;
+
+               ret = client_read(&IMAP->transmitted_message[bytes_transferred], blksize);
+               if (ret < 1) {
+                       bytes_transferred = literal_length;     /* bail out */
+               }
+               else {
+                       bytes_transferred += blksize;           /* keep going */
+               }
+               lprintf(CTDL_DEBUG, "Received %d of %d bytes (%d%%)\n",
+                       bytes_transferred,
+                       literal_length,
+                       ((bytes_transferred * 100) / literal_length)
+               );
+       } while (bytes_transferred < literal_length);
+
        IMAP->transmitted_message[literal_length] = 0;
        if (ret != 1) {
                cprintf("%s NO Read failed.\r\n", parms[0]);
@@ -266,13 +289,13 @@ void imap_append(int num_parms, char *parms[]) {
 
        /* Convert RFC822 newlines (CRLF) to Unix newlines (LF) */
        lprintf(CTDL_DEBUG, "Converting newline format\n");
+       stripped_length = 0;
        for (i=0; i<literal_length; ++i) {
-               if (!strncmp(&IMAP->transmitted_message[i], "\r\n", 2)) {
-                       strcpy(&IMAP->transmitted_message[i],
-                               &IMAP->transmitted_message[i+1]);
-                       --literal_length;
+               if (strncmp(&IMAP->transmitted_message[i], "\r\n", 2)) {
+                       IMAP->transmitted_message[stripped_length++] = IMAP->transmitted_message[i];
                }
        }
+       literal_length = stripped_length;
 
        lprintf(CTDL_DEBUG, "Converting message format\n");
         msg = convert_internet_message(IMAP->transmitted_message);
index 95c2ce7d19de96a78584ef621405069583f19e25..201d2fe7d190941b59f220571f5cab1d60b29d69 100644 (file)
@@ -308,6 +308,13 @@ void imap_cleanup_function(void)
        lprintf(CTDL_DEBUG, "Performing IMAP cleanup hook\n");
        imap_free_msgids();
        imap_free_transmitted_message();
+
+       if (IMAP->cached_fetch != NULL) {
+               fclose(IMAP->cached_fetch);
+               IMAP->cached_fetch = NULL;
+               IMAP->cached_msgnum = (-1);
+       }
+
        lprintf(CTDL_DEBUG, "Finished IMAP cleanup hook\n");
 }
 
@@ -322,6 +329,8 @@ void imap_greeting(void)
        strcpy(CC->cs_clientname, "IMAP session");
        CtdlAllocUserData(SYM_IMAP, sizeof(struct citimap));
        IMAP->authstate = imap_as_normal;
+       IMAP->cached_fetch = NULL;
+       IMAP->cached_msgnum = (-1);
 
        cprintf("* OK %s Citadel/UX IMAP4rev1 server ready\r\n",
                config.c_fqdn);
index 081bfb839a8e01edeee1e09020942fe350868422..b98cf105c12e5384ecbde4c5ac0d7d288b75687e 100644 (file)
@@ -20,6 +20,8 @@ struct citimap {
        unsigned int *flags;
        char *transmitted_message;      /* for APPEND command... */
        size_t transmitted_length;
+       FILE *cached_fetch;             /* cache our most recent RFC822 FETCH */
+       long cached_msgnum;             /* because the client might ask for it in pieces */
 };
 
 /*