X-Git-Url: https://git.llucax.com/software/mutt-debian.git/blobdiff_plain/14c29200cb58d3c4a0830265f2433849781858d0..9275549093fd4bae96f4119e7b66dd3cc6478e78:/imap/message.c?ds=sidebyside diff --git a/imap/message.c b/imap/message.c index a610f78..e3cb0ac 100644 --- a/imap/message.c +++ b/imap/message.c @@ -1,22 +1,22 @@ /* * Copyright (C) 1996-9 Brandon Long - * Copyright (C) 1999-2007 Brendan Cully - * + * Copyright (C) 1999-2009 Brendan Cully + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - */ + */ /* message parsing/updating functions */ @@ -62,7 +62,7 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) { CONTEXT* ctx; char buf[LONG_STRING]; - char hdrreq[STRING]; + char *hdrreq = NULL; FILE *fp; char tempfile[_POSIX_PATH_MAX]; int msgno, idx; @@ -73,10 +73,12 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) int maxuid = 0; const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL"; progress_t progress; + int retval = -1; #if USE_HCACHE unsigned int *uid_validity = NULL; - unsigned int *uidnext = NULL; + unsigned int *puidnext = NULL; + unsigned int uidnext = 0; int evalhc = 0; #endif /* USE_HCACHE */ @@ -84,29 +86,29 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) if (mutt_bit_isset (idata->capabilities,IMAP4REV1)) { - snprintf (hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", - want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : ""); - } + safe_asprintf (&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", + want_headers, ImapHeaders ? " " : "", NONULL (ImapHeaders)); + } else if (mutt_bit_isset (idata->capabilities,IMAP4)) { - snprintf (hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s%s%s)", - want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : ""); + safe_asprintf (&hdrreq, "RFC822.HEADER.LINES (%s%s%s)", + want_headers, ImapHeaders ? " " : "", NONULL (ImapHeaders)); } else { /* Unable to fetch headers for lower versions */ mutt_error _("Unable to fetch headers from this IMAP server version."); mutt_sleep (2); /* pause a moment to let the user see the error */ - return -1; + goto error_out_0; } /* instead of downloading all headers and then parsing them, we parse them * as they come in. */ - mutt_mktemp (tempfile); + mutt_mktemp (tempfile, sizeof (tempfile)); if (!(fp = safe_fopen (tempfile, "w+"))) { mutt_error (_("Could not create temporary file %s"), tempfile); mutt_sleep (2); - return -1; + goto error_out_0; } unlink (tempfile); @@ -124,12 +126,14 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) if (idata->hcache && !msgbegin) { uid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", imap_hcache_keylen); - uidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen); - if (uid_validity && uidnext && *uid_validity == idata->uid_validity - && *uidnext > 0) + puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen); + if (puidnext) + { + uidnext = *puidnext; + FREE (&puidnext); + } + if (uid_validity && uidnext && *uid_validity == idata->uid_validity) evalhc = 1; - else - FREE (&uidnext); FREE (&uid_validity); } if (evalhc) @@ -138,15 +142,15 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) M_PROGRESS_MSG, ReadInc, msgend + 1); snprintf (buf, sizeof (buf), - "UID FETCH 1:%u (UID FLAGS)", *uidnext - 1); - FREE (&uidnext); - + "UID FETCH 1:%u (UID FLAGS)", uidnext - 1); + imap_cmd_start (idata, buf); - - for (msgno = msgbegin; msgno <= msgend ; msgno++) + + rc = IMAP_CMD_CONTINUE; + for (msgno = msgbegin; rc == IMAP_CMD_CONTINUE; msgno++) { mutt_progress_update (&progress, msgno + 1, -1); - + memset (&h, 0, sizeof (h)); h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); do @@ -173,12 +177,20 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) break; } + if (!h.data->uid) + { + dprint (2, (debugfile, "imap_read_headers: skipping hcache FETCH " + "response for unknown message number %d\n", h.sid)); + mfhrc = -1; + continue; + } + idx = h.sid - 1; ctx->hdrs[idx] = imap_hcache_get (idata, h.data->uid); if (ctx->hdrs[idx]) { ctx->hdrs[idx]->index = idx; - /* messages which have not been expunged are ACTIVE (borrowed from mh + /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[idx]->active = 1; ctx->hdrs[idx]->read = h.data->read; @@ -209,8 +221,7 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) if (h.data) imap_free_header_data ((void**) (void*) &h.data); imap_hcache_close (idata); - fclose (fp); - return -1; + goto error_out_1; } } /* could also look for first null header in case hcache is holey */ @@ -228,13 +239,13 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) /* we may get notification of new mail while fetching headers */ if (msgno + 1 > fetchlast) { - fetchlast = msgend + 1; + char *cmd; - snprintf (buf, sizeof (buf), - "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1, - fetchlast, hdrreq); - - imap_cmd_start (idata, buf); + fetchlast = msgend + 1; + safe_asprintf (&cmd, "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", + msgno + 1, fetchlast, hdrreq); + imap_cmd_start (idata, cmd); + FREE (&cmd); } rewind (fp); @@ -258,14 +269,36 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) else if (mfhrc < 0) break; + if (!ftello (fp)) + { + dprint (2, (debugfile, "msg_fetch_header: ignoring fetch response with no body\n")); + mfhrc = -1; + continue; + } + /* make sure we don't get remnants from older larger message headers */ fputs ("\n\n", fp); idx = h.sid - 1; + if (idx > msgend) + { + dprint (1, (debugfile, "imap_read_headers: skipping FETCH response for " + "unknown message number %d\n", h.sid)); + mfhrc = -1; + continue; + } + /* May receive FLAGS updates in a separate untagged response (#2935) */ + if (idx < ctx->msgcount) + { + dprint (2, (debugfile, "imap_read_headers: message %d is not new\n", + h.sid)); + continue; + } + ctx->hdrs[idx] = mutt_new_header (); ctx->hdrs[idx]->index = h.sid - 1; - /* messages which have not been expunged are ACTIVE (borrowed from mh + /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[idx]->active = 1; ctx->hdrs[idx]->read = h.data->read; @@ -305,8 +338,7 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) #if USE_HCACHE imap_hcache_close (idata); #endif - fclose (fp); - return -1; + goto error_out_1; } /* in case we get new mail while fetching the headers */ @@ -338,8 +370,6 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) imap_hcache_close (idata); #endif /* USE_HCACHE */ - fclose(fp); - if (ctx->msgcount > oldmsgcount) { mx_alloc_memory(ctx); @@ -347,7 +377,16 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) } idata->reopen |= IMAP_REOPEN_ALLOW; - return msgend; + + retval = msgend; + +error_out_1: + safe_fclose (&fp); + +error_out_0: + FREE (&hdrreq); + + return retval; } int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) @@ -404,7 +443,7 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) if (!(msg->fp = msg_cache_put (idata, h))) { cache->uid = HEADER_DATA(h)->uid; - mutt_mktemp (path); + mutt_mktemp (path, sizeof (path)); cache->path = safe_strdup (path); if (!(msg->fp = safe_fopen (path, "w+"))) { @@ -417,7 +456,7 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) * also try to update it. HACK until all this code can be moved into the * command handler */ h->active = 0; - + snprintf (buf, sizeof (buf), "UID FETCH %u %s", HEADER_DATA(h)->uid, (mutt_bit_isset (idata->capabilities, IMAP4REV1) ? (option (OPTIMAPPEEK) ? "BODY.PEEK[]" : "BODY[]") : @@ -490,7 +529,7 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) mutt_perror (cache->path); goto bail; } - + if (rc != IMAP_CMD_OK) goto bail; @@ -560,7 +599,8 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) FILE *fp; char buf[LONG_STRING]; char mbox[LONG_STRING]; - char mailbox[LONG_STRING]; + char mailbox[LONG_STRING]; + char internaldate[IMAP_DATELEN]; size_t len; progress_t progressbar; size_t sent; @@ -574,7 +614,9 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) return -1; imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); - + if (!*mailbox) + strfcpy (mailbox, "INBOX", sizeof (mailbox)); + if ((fp = fopen (msg->path, "r")) == NULL) { mutt_perror (msg->path); @@ -600,12 +642,14 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) M_PROGRESS_SIZE, NetInc, len); imap_munge_mbox_name (mbox, sizeof (mbox), mailbox); - snprintf (buf, sizeof (buf), "APPEND %s (%s%s%s%s%s) {%lu}", mbox, + imap_make_date (internaldate, msg->received); + snprintf (buf, sizeof (buf), "APPEND %s (%s%s%s%s%s) \"%s\" {%lu}", mbox, msg->flags.read ? "\\Seen" : "", msg->flags.read && (msg->flags.replied || msg->flags.flagged) ? " " : "", msg->flags.replied ? "\\Answered" : "", msg->flags.replied && msg->flags.flagged ? " " : "", msg->flags.flagged ? "\\Flagged" : "", + internaldate, (unsigned long) len); imap_cmd_start (idata, buf); @@ -626,7 +670,7 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); - fclose (fp); + safe_fclose (&fp); goto fail; } @@ -644,12 +688,12 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) mutt_progress_update (&progressbar, sent, -1); } } - + if (len) flush_buffer(buf, &len, idata->conn); mutt_socket_write (idata->conn, "\r\n"); - fclose (fp); + safe_fclose (&fp); do rc = imap_cmd_step (idata); @@ -687,13 +731,14 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) { IMAP_DATA* idata; BUFFER cmd, sync_cmd; - char uid[11]; char mbox[LONG_STRING]; char mmbox[LONG_STRING]; + char prompt[LONG_STRING]; int rc; int n; IMAP_MBOX mx; int err_continue = M_NO; + int triedcreate = 0; idata = (IMAP_DATA*) ctx->data; @@ -716,95 +761,110 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); return 1; } - - imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); - memset (&sync_cmd, 0, sizeof (sync_cmd)); - memset (&cmd, 0, sizeof (cmd)); - mutt_buffer_addstr (&cmd, "UID COPY "); + imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); + if (!*mbox) + strfcpy (mbox, "INBOX", sizeof (mbox)); + imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox); - /* Null HEADER* means copy tagged messages */ - if (!h) + /* loop in case of TRYCREATE */ + do { - /* if any messages have attachments to delete, fall through to FETCH - * and APPEND. TODO: Copy what we can with COPY, fall through for the - * remainder. */ - for (n = 0; n < ctx->msgcount; n++) + memset (&sync_cmd, 0, sizeof (sync_cmd)); + memset (&cmd, 0, sizeof (cmd)); + + /* Null HEADER* means copy tagged messages */ + if (!h) { - if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del) + /* if any messages have attachments to delete, fall through to FETCH + * and APPEND. TODO: Copy what we can with COPY, fall through for the + * remainder. */ + for (n = 0; n < ctx->msgcount; n++) { - dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); - return 1; + if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del) + { + dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); + return 1; + } + + if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active && + ctx->hdrs[n]->changed) + { + rc = imap_sync_message (idata, ctx->hdrs[n], &sync_cmd, &err_continue); + if (rc < 0) + { + dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); + goto out; + } + } } - if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active && - ctx->hdrs[n]->changed) + rc = imap_exec_msgset (idata, "UID COPY", mmbox, M_TAG, 0, 0); + if (!rc) { - rc = imap_sync_message (idata, ctx->hdrs[n], &sync_cmd, &err_continue); - if (rc < 0) - { - dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); - goto fail; - } + dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n")); + rc = -1; + goto out; + } + else if (rc < 0) + { + dprint (1, (debugfile, "could not queue copy\n")); + goto out; } + else + mutt_message (_("Copying %d messages to %s..."), rc, mbox); } - - rc = imap_make_msg_set (idata, &cmd, M_TAG, 0, 0); - if (!rc) + else { - dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n")); - goto fail; - } - mutt_message (_("Copying %d messages to %s..."), rc, mbox); - } - else - { - mutt_message (_("Copying message %d to %s..."), h->index+1, mbox); - snprintf (uid, sizeof (uid), "%u", HEADER_DATA (h)->uid); - mutt_buffer_addstr (&cmd, uid); + mutt_message (_("Copying message %d to %s..."), h->index+1, mbox); + mutt_buffer_printf (&cmd, "UID COPY %u %s", HEADER_DATA (h)->uid, mmbox); - if (h->active && h->changed) - { - rc = imap_sync_message (idata, h, &sync_cmd, &err_continue); - if (rc < 0) + if (h->active && h->changed) + { + rc = imap_sync_message (idata, h, &sync_cmd, &err_continue); + if (rc < 0) + { + dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); + goto out; + } + } + if ((rc = imap_exec (idata, cmd.data, IMAP_CMD_QUEUE)) < 0) { - dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); - goto fail; + dprint (1, (debugfile, "could not queue copy\n")); + goto out; } } - } - /* let's get it on */ - mutt_buffer_addstr (&cmd, " "); - imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox); - mutt_buffer_addstr (&cmd, mmbox); - - rc = imap_exec (idata, cmd.data, IMAP_CMD_FAIL_OK); - if (rc == -2) - { - /* bail out if command failed for reasons other than nonexistent target */ - if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) + /* let's get it on */ + rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); + if (rc == -2) { - imap_error ("imap_copy_messages", idata->buf); - goto fail; - } - dprint (2, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n")); - snprintf (mmbox, sizeof (mmbox), _("Create %s?"), mbox); - if (option (OPTCONFIRMCREATE) && mutt_yesorno (mmbox, 1) < 1) - { - mutt_clear_error (); - goto fail; + if (triedcreate) + { + dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox)); + break; + } + /* bail out if command failed for reasons other than nonexistent target */ + if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) + break; + dprint (3, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n")); + snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox); + if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1) + { + mutt_clear_error (); + goto out; + } + if (imap_create_mailbox (idata, mbox) < 0) + break; + triedcreate = 1; } - if (imap_create_mailbox (idata, mbox) < 0) - goto fail; - - /* try again */ - rc = imap_exec (idata, cmd.data, 0); } + while (rc == -2); + if (rc != 0) { imap_error ("imap_copy_messages", idata->buf); - goto fail; + goto out; } /* cleanup */ @@ -828,20 +888,16 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) } } - if (cmd.data) - FREE (&cmd.data); - if (sync_cmd.data) - FREE (&sync_cmd.data); - FREE (&mx.mbox); - return 0; + rc = 0; - fail: + out: if (cmd.data) FREE (&cmd.data); if (sync_cmd.data) FREE (&sync_cmd.data); FREE (&mx.mbox); - return -1; + + return rc < 0 ? -1 : rc; } static body_cache_t *msg_cache_open (IMAP_DATA *idata) @@ -983,14 +1039,14 @@ char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s) dprint (2, (debugfile, "imap_fetch_message: parsing FLAGS\n")); if ((s = msg_parse_flags (&newh, s)) == NULL) return NULL; - + /* YAUH (yet another ugly hack): temporarily set context to * read-write even if it's read-only, so *server* updates of * flags can be processed by mutt_set_flag. ctx->changed must * be restored afterwards */ readonly = ctx->readonly; ctx->readonly = 0; - + mutt_set_flag (ctx, h, M_NEW, !(hd->read || hd->old)); mutt_set_flag (ctx, h, M_OLD, hd->old); mutt_set_flag (ctx, h, M_READ, hd->read); @@ -1024,7 +1080,7 @@ static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp) if (buf[0] != '*') return rc; - + /* skip to message number */ buf = imap_next_word (buf); h->sid = atoi (buf); @@ -1041,9 +1097,9 @@ static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp) /* FIXME: current implementation - call msg_parse_fetch - if it returns -2, * read header lines and call it again. Silly. */ - if ((rc = msg_parse_fetch (h, buf) != -2) || !fp) + if ((rc = msg_parse_fetch (h, buf)) != -2 || !fp) return rc; - + if (imap_get_literal_count (buf, &bytes) == 0) { imap_read_literal (fp, idata, bytes, NULL); @@ -1054,13 +1110,13 @@ static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp) * interchangeably at any time. */ if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) return rc; - + if (msg_parse_fetch (h, idata->buf) == -1) return rc; } rc = 0; /* success */ - + /* subtract headers from message size - unfortunately only the subset of * headers we've requested. */ h->content_length -= bytes;