📄 message.c
字号:
/* * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> * Copyright (C) 1999-2006 Brendan Cully <brendan@kublai.com> * * 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 */#if HAVE_CONFIG_H# include "config.h"#endif#include <errno.h>#include <stdlib.h>#include <ctype.h>#include "mutt.h"#include "imap_private.h"#include "message.h"#include "mx.h"#ifdef HAVE_PGP#include "pgp.h"#endif#if USE_HCACHE#include "hcache.h"#endif#include "bcache.h"static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h);static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h);static void flush_buffer(char* buf, size_t* len, CONNECTION* conn);static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp);static int msg_parse_fetch (IMAP_HEADER* h, char* s);static char* msg_parse_flags (IMAP_HEADER* h, char* s);/* imap_read_headers: * Changed to read many headers instead of just one. It will return the * msgno of the last message read. It will return a value other than * msgend if mail comes in while downloading headers (in theory). */int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend){ CONTEXT* ctx; char buf[LONG_STRING]; char hdrreq[STRING]; FILE *fp; char tempfile[_POSIX_PATH_MAX]; int msgno; IMAP_HEADER h; IMAP_STATUS* status; int rc, mfhrc, oldmsgcount; int fetchlast = 0; 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";#if USE_HCACHE header_cache_t *hc = NULL; unsigned int *uid_validity = NULL; unsigned int *uidnext = NULL; int evalhc = 0; char uid_buf[64];#endif /* USE_HCACHE */ ctx = idata->ctx; if (mutt_bit_isset (idata->capabilities,IMAP4REV1)) { snprintf (hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : ""); } else if (mutt_bit_isset (idata->capabilities,IMAP4)) { snprintf (hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s%s%s)", want_headers, ImapHeaders ? " " : "", ImapHeaders ? 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; } /* instead of downloading all headers and then parsing them, we parse them * as they come in. */ mutt_mktemp (tempfile); if (!(fp = safe_fopen (tempfile, "w+"))) { mutt_error (_("Could not create temporary file %s"), tempfile); mutt_sleep (2); return -1; } unlink (tempfile); /* make sure context has room to hold the mailbox */ while ((msgend) >= idata->ctx->hdrmax) mx_alloc_memory (idata->ctx); oldmsgcount = ctx->msgcount; idata->reopen &= ~IMAP_NEWMAIL_PENDING; idata->newMailCount = 0;#if USE_HCACHE if (!msgbegin) hc = mutt_hcache_open (HeaderCache, ctx->path); if (hc) { uid_validity = mutt_hcache_fetch_raw (hc, "/UIDVALIDITY", imap_hcache_keylen); uidnext = mutt_hcache_fetch_raw (hc, "/UIDNEXT", imap_hcache_keylen); if (uid_validity && uidnext && *uid_validity == idata->uid_validity && *uidnext > 0) evalhc = 1; FREE (&uid_validity); } if (evalhc) { snprintf (buf, sizeof (buf), "UID FETCH 1:%u (UID FLAGS)", *uidnext - 1); FREE (&uidnext); imap_cmd_start (idata, buf); for (msgno = msgbegin; msgno <= msgend ; msgno++) { if (ReadInc && (!msgno || ((msgno+1) % ReadInc == 0))) mutt_message (_("Evaluating cache... [%d/%d]"), msgno + 1, msgend + 1); memset (&h, 0, sizeof (h)); h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); do { mfhrc = 0; rc = imap_cmd_step (idata); if (rc != IMAP_CMD_CONTINUE) { imap_free_header_data ((void**) &h.data); break; } if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, NULL)) == -1) continue; else if (mfhrc < 0) { imap_free_header_data ((void**) &h.data); break; } sprintf(uid_buf, "/%u", h.data->uid); /* XXX --tg 21:41 04-07-11 */ uid_validity = (unsigned int*)mutt_hcache_fetch (hc, uid_buf, &imap_hcache_keylen); if (uid_validity != NULL && *uid_validity == idata->uid_validity) { ctx->hdrs[msgno] = mutt_hcache_restore((unsigned char *) uid_validity, 0); ctx->hdrs[msgno]->index = h.sid - 1; if (h.sid != ctx->msgcount + 1) dprint (1, (debugfile, "imap_read_headers: msgcount and sequence ID are inconsistent!")); /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[msgno]->active = 1; ctx->hdrs[msgno]->read = h.data->read; ctx->hdrs[msgno]->old = h.data->old; ctx->hdrs[msgno]->deleted = h.data->deleted; ctx->hdrs[msgno]->flagged = h.data->flagged; ctx->hdrs[msgno]->replied = h.data->replied; ctx->hdrs[msgno]->changed = h.data->changed; /* ctx->hdrs[msgno]->received is restored from mutt_hcache_restore */ ctx->hdrs[msgno]->data = (void *) (h.data); ctx->msgcount++; } else /* bad header in the cache, we'll have to refetch. * TODO: consider the possibility of a holey cache. */ imap_free_header_data((void**) &h.data); FREE(&uid_validity); } while (rc != IMAP_CMD_OK && mfhrc == -1); if (rc == IMAP_CMD_OK) break; if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) { imap_free_header_data ((void**) &h.data); fclose (fp); mutt_hcache_close (hc); return -1; } } fetchlast = msgbegin; }#endif /* USE_HCACHE */ for (msgno = msgbegin; msgno <= msgend ; msgno++) { if (ctx->hdrs[msgno]) { msgbegin++; continue; } if (ReadInc && (msgno == msgbegin || ((msgno+1) % ReadInc == 0))) mutt_message (_("Fetching message headers... [%d/%d]"), msgno + 1, msgend + 1); if (msgno + 1 > fetchlast) { fetchlast = msgno + 1; while((fetchlast <= msgend) && (! ctx->hdrs[fetchlast])) fetchlast++; /* * Make one request for everything. This makes fetching headers an * order of magnitude faster if you have a large mailbox. * * If we get more messages while doing this, we make another * request for all the new messages. */ snprintf (buf, sizeof (buf), "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1, fetchlast, hdrreq); imap_cmd_start (idata, buf); } /* freshen fp, h */ rewind (fp); memset (&h, 0, sizeof (h)); h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); /* this DO loop does two things: * 1. handles untagged messages, so we can try again on the same msg * 2. fetches the tagged response at the end of the last message. */ do { mfhrc = 0; rc = imap_cmd_step (idata); if (rc != IMAP_CMD_CONTINUE) break; if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, fp)) == -1) continue; else if (mfhrc < 0) break; /* make sure we don't get remnants from older larger message headers */ fputs ("\n\n", fp); /* update context with message header */ ctx->hdrs[msgno] = mutt_new_header (); ctx->hdrs[msgno]->index = h.sid - 1; if (h.sid != ctx->msgcount + 1) dprint (1, (debugfile, "imap_read_headers: msgcount and sequence ID are inconsistent!")); /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[msgno]->active = 1; ctx->hdrs[msgno]->read = h.data->read; ctx->hdrs[msgno]->old = h.data->old; ctx->hdrs[msgno]->deleted = h.data->deleted; ctx->hdrs[msgno]->flagged = h.data->flagged; ctx->hdrs[msgno]->replied = h.data->replied; ctx->hdrs[msgno]->changed = h.data->changed; ctx->hdrs[msgno]->received = h.received; ctx->hdrs[msgno]->data = (void *) (h.data); if (maxuid < h.data->uid) maxuid = h.data->uid; rewind (fp); /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends * on h.received being set */ ctx->hdrs[msgno]->env = mutt_read_rfc822_header (fp, ctx->hdrs[msgno], 0, 0); /* content built as a side-effect of mutt_read_rfc822_header */ ctx->hdrs[msgno]->content->length = h.content_length;#if USE_HCACHE sprintf(uid_buf, "/%u", h.data->uid); mutt_hcache_store(hc, uid_buf, ctx->hdrs[msgno], idata->uid_validity, &imap_hcache_keylen);#endif /* USE_HCACHE */ ctx->msgcount++; } while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) || ((msgno + 1) >= fetchlast))); if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) { imap_free_header_data ((void**) &h.data); fclose (fp);#if USE_HCACHE mutt_hcache_close (hc);#endif /* USE_HCACHE */ return -1; } /* in case we get new mail while fetching the headers */ if (idata->reopen & IMAP_NEWMAIL_PENDING) { msgend = idata->newMailCount - 1; while ((msgend) >= ctx->hdrmax) mx_alloc_memory (ctx); idata->reopen &= ~IMAP_NEWMAIL_PENDING; idata->newMailCount = 0; } } if (maxuid && (status = imap_mboxcache_get (idata, idata->mailbox))) status->uidnext = maxuid + 1;#if USE_HCACHE mutt_hcache_store_raw (hc, "/UIDVALIDITY", &idata->uid_validity, sizeof (idata->uid_validity), imap_hcache_keylen); if (maxuid && idata->uidnext < maxuid + 1) { dprint (2, (debugfile, "Overriding UIDNEXT: %u -> %u\n", idata->uidnext, maxuid + 1)); idata->uidnext = maxuid + 1; } if (idata->uidnext > 1) mutt_hcache_store_raw (hc, "/UIDNEXT", &idata->uidnext, sizeof (idata->uidnext), imap_hcache_keylen); mutt_hcache_close (hc);#endif /* USE_HCACHE */ fclose(fp); if (ctx->msgcount > oldmsgcount) { mx_alloc_memory(ctx); mx_update_context (ctx, ctx->msgcount - oldmsgcount); } return msgend;}int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno){ IMAP_DATA* idata; HEADER* h; ENVELOPE* newenv; char buf[LONG_STRING]; char path[_POSIX_PATH_MAX]; char *pc; long bytes; progress_t progressbar; int uid; int cacheno; IMAP_CACHE *cache; int read; int rc; /* Sam's weird courier server returns an OK response even when FETCH * fails. Thanks Sam. */ short fetched = 0; idata = (IMAP_DATA*) ctx->data; h = ctx->hdrs[msgno]; if ((msg->fp = msg_cache_get (idata, h))) { if (HEADER_DATA(h)->parsed) return 0; else goto parsemsg; } /* we still do some caching even if imap_cachedir is unset */ /* see if we already have the message in our cache */ cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN; cache = &idata->cache[cacheno]; if (cache->path) { /* don't treat cache errors as fatal, just fall back. */ if (cache->uid == HEADER_DATA(h)->uid && (msg->fp = fopen (cache->path, "r"))) return 0; else { unlink (cache->path); FREE (&cache->path); } } if (!isendwin())
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -