mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net>
3 * Copyright (C) 1999-2009 Brendan Cully <brendan@kublai.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20
21/* message parsing/updating functions */
22
23#if HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include <errno.h>
28#include <stdlib.h>
29#include <ctype.h>
30#include <limits.h>
31
32#include "mutt.h"
33#include "imap_private.h"
34#include "mx.h"
35
36#ifdef HAVE_PGP
37#include "pgp.h"
38#endif
39
40#if USE_HCACHE
41#include "hcache.h"
42#endif
43
44#include "bcache.h"
45
46#if USE_HCACHE
47static int read_headers_normal_eval_cache (IMAP_DATA *idata,
48 unsigned int msn_end,
49 unsigned int uidnext,
50 int store_flag_updates,
51 int eval_condstore);
52static int read_headers_qresync_eval_cache (IMAP_DATA *idata,
53 char *uid_seqset);
54static int read_headers_condstore_qresync_updates (IMAP_DATA *idata,
55 unsigned int msn_end,
56 unsigned int uidnext,
57 unsigned long long hc_modseq,
58 int eval_qresync);
59#endif /* USE_HCACHE */
60
61static int read_headers_fetch_new (IMAP_DATA *idata, unsigned int msn_begin,
62 unsigned int msn_end, int evalhc,
63 unsigned int *maxuid, int initial_download);
64
65
66static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h);
67static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h);
68static int msg_cache_commit (IMAP_DATA* idata, HEADER* h);
69
70static int flush_buffer (char* buf, size_t* len, CONNECTION* conn);
71static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf,
72 FILE* fp);
73static int msg_parse_fetch (IMAP_HEADER* h, char* s);
74static char* msg_parse_flags (IMAP_HEADER* h, char* s);
75
76
77/* If the user hits ctrl-c during an initial header download for a mailbox,
78 * prompt whether to completely abort the download and close the mailbox.
79 */
80static int query_abort_header_download (IMAP_DATA *idata)
81{
82 int abort = 0;
83
84 mutt_flushinp ();
85 /* L10N: This prompt is made if the user hits Ctrl-C when opening
86 * an IMAP mailbox */
87 if (mutt_yesorno (_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
88 {
89 abort = 1;
90 imap_close_connection (idata);
91 }
92 SigInt = 0;
93
94 return abort;
95}
96
97
98static void imap_alloc_msn_index (IMAP_DATA *idata, unsigned int msn_count)
99{
100 unsigned int new_size;
101
102 if (msn_count <= idata->msn_index_size)
103 return;
104
105 /* This is a conservative check to protect against a malicious imap
106 * server. Most likely size_t is bigger than an unsigned int, but
107 * if msn_count is this big, we have a serious problem. */
108 if (msn_count >= (UINT_MAX / sizeof (HEADER *)))
109 {
110 mutt_error _("Integer overflow -- can't allocate memory.");
111 sleep (1);
112 mutt_exit (1);
113 }
114
115 /* Add a little padding, like mx_allloc_memory() */
116 new_size = msn_count + 25;
117
118 if (!idata->msn_index)
119 idata->msn_index = safe_calloc (new_size, sizeof (HEADER *));
120 else
121 {
122 safe_realloc (&idata->msn_index, sizeof (HEADER *) * new_size);
123 memset (idata->msn_index + idata->msn_index_size, 0,
124 sizeof (HEADER *) * (new_size - idata->msn_index_size));
125 }
126
127 idata->msn_index_size = new_size;
128}
129
130/* This function is run after imap_alloc_msn_index, so we skip the
131 * malicious msn_count size check.
132 */
133static void imap_alloc_uid_hash (IMAP_DATA *idata, unsigned int msn_count)
134{
135 if (!idata->uid_hash)
136 idata->uid_hash = int_hash_create (MAX (6 * msn_count / 5, 30), 0);
137}
138
139/* Generates a more complicated sequence set after using the header cache,
140 * in case there are missing MSNs in the middle.
141 */
142static unsigned int imap_fetch_msn_seqset (BUFFER *b, IMAP_DATA *idata, int evalhc,
143 unsigned int msn_begin, unsigned int msn_end,
144 unsigned int *fetch_msn_end)
145{
146 unsigned int max_headers_per_fetch = UINT_MAX;
147 int first_chunk = 1;
148 int state = 0; /* 1: single msn, 2: range of msn */
149 unsigned int msn, range_begin, range_end, msn_count = 0;
150
151 mutt_buffer_clear (b);
152 if (msn_end < msn_begin)
153 return 0;
154
155 if (ImapFetchChunkSize > 0)
156 max_headers_per_fetch = ImapFetchChunkSize;
157
158 if (!evalhc)
159 {
160 if (msn_end - msn_begin + 1 <= max_headers_per_fetch)
161 *fetch_msn_end = msn_end;
162 else
163 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
164 mutt_buffer_printf (b, "%u:%u", msn_begin, *fetch_msn_end);
165 return (*fetch_msn_end - msn_begin + 1);
166 }
167
168 for (msn = msn_begin; msn <= msn_end + 1; msn++)
169 {
170 if (msn_count < max_headers_per_fetch &&
171 msn <= msn_end &&
172 !idata->msn_index[msn-1])
173 {
174 msn_count++;
175
176 switch (state)
177 {
178 case 1: /* single: convert to a range */
179 state = 2;
180 /* fall through */
181 case 2: /* extend range ending */
182 range_end = msn;
183 break;
184 default:
185 state = 1;
186 range_begin = msn;
187 break;
188 }
189 }
190 else if (state)
191 {
192 if (first_chunk)
193 first_chunk = 0;
194 else
195 mutt_buffer_addch (b, ',');
196
197 if (state == 1)
198 mutt_buffer_add_printf (b, "%u", range_begin);
199 else if (state == 2)
200 mutt_buffer_add_printf (b, "%u:%u", range_begin, range_end);
201 state = 0;
202
203 if ((mutt_buffer_len (b) > 500) ||
204 msn_count >= max_headers_per_fetch)
205 break;
206 }
207 }
208
209 /* The loop index goes one past to terminate the range if needed. */
210 *fetch_msn_end = msn - 1;
211
212 return msn_count;
213}
214
215/* imap_read_headers:
216 * Changed to read many headers instead of just one. It will return the
217 * msn of the last message read. It will return a value other than
218 * msn_end if mail comes in while downloading headers (in theory).
219 */
220int imap_read_headers (IMAP_DATA* idata, unsigned int msn_begin, unsigned int msn_end,
221 int initial_download)
222{
223 CONTEXT* ctx;
224 IMAP_STATUS* status;
225 int oldmsgcount;
226 unsigned int maxuid = 0;
227 int retval = -1;
228 int evalhc = 0;
229
230#if USE_HCACHE
231 void *puid_validity = NULL;
232 unsigned int uid_validity = 0;
233 void *puidnext = NULL;
234 unsigned int uidnext = 0;
235 int has_condstore = 0;
236 int has_qresync = 0;
237 int eval_condstore = 0;
238 int eval_qresync = 0;
239 void *pmodseq = NULL;
240 unsigned long long hc_modseq = 0;
241 char *uid_seqset = NULL;
242#endif /* USE_HCACHE */
243
244 ctx = idata->ctx;
245
246 /* make sure context has room to hold the mailbox */
247 while (msn_end > ctx->hdrmax)
248 mx_alloc_memory (ctx);
249 imap_alloc_msn_index (idata, msn_end);
250 imap_alloc_uid_hash (idata, msn_end);
251
252 oldmsgcount = ctx->msgcount;
253 idata->reopen &= ~(IMAP_REOPEN_ALLOW|IMAP_NEWMAIL_PENDING);
254 idata->newMailCount = 0;
255
256#if USE_HCACHE
257 idata->hcache = imap_hcache_open (idata, NULL);
258
259 if (idata->hcache && initial_download)
260 {
261 puid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", imap_hcache_keylen);
262 if (puid_validity)
263 memcpy (&uid_validity, puid_validity, sizeof(unsigned int));
264 puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen);
265 if (puidnext)
266 {
267 memcpy (&uidnext, puidnext, sizeof(unsigned int));;
268 mutt_hcache_free ((void **)&puidnext);
269 }
270
271 if (idata->modseq)
272 {
273 if (mutt_bit_isset (idata->capabilities, CONDSTORE) && option (OPTIMAPCONDSTORE))
274 has_condstore = 1;
275
276 /* If mutt_bit_isset(QRESYNC) and option(OPTIMAPQRESYNC) then Mutt
277 * sends ENABLE QRESYNC. If we receive an ENABLED response back, then
278 * idata->qresync is set.
279 */
280 if (idata->qresync)
281 has_qresync = 1;
282 }
283
284 if (puid_validity && uidnext && (uid_validity == idata->uid_validity))
285 {
286 evalhc = 1;
287 pmodseq = mutt_hcache_fetch_raw (idata->hcache, "/MODSEQ", imap_hcache_keylen);
288 if (pmodseq)
289 {
290 memcpy (&hc_modseq, pmodseq, sizeof(unsigned long long));;
291 mutt_hcache_free ((void **)&pmodseq);
292 }
293 if (hc_modseq)
294 {
295 if (has_qresync)
296 {
297 uid_seqset = imap_hcache_get_uid_seqset (idata);
298 if (uid_seqset)
299 eval_qresync = 1;
300 }
301
302 if (!eval_qresync && has_condstore)
303 eval_condstore = 1;
304 }
305 }
306 mutt_hcache_free ((void **)&puid_validity);
307 }
308 if (evalhc)
309 {
310 if (eval_qresync)
311 {
312 if (read_headers_qresync_eval_cache (idata, uid_seqset) < 0)
313 goto bail;
314 }
315 else
316 {
317 if (read_headers_normal_eval_cache (idata, msn_end, uidnext,
318 has_condstore || has_qresync,
319 eval_condstore) < 0)
320 goto bail;
321 }
322
323 if ((eval_condstore || eval_qresync) && (hc_modseq != idata->modseq))
324 if (read_headers_condstore_qresync_updates (idata, msn_end, uidnext,
325 hc_modseq, eval_qresync) < 0)
326 goto bail;
327
328 /* Look for the first empty MSN and start there */
329 while (msn_begin <= msn_end)
330 {
331 if (!idata->msn_index[msn_begin -1])
332 break;
333 msn_begin++;
334 }
335 }
336#endif /* USE_HCACHE */
337
338 if (read_headers_fetch_new (idata, msn_begin, msn_end, evalhc, &maxuid,
339 initial_download) < 0)
340 goto bail;
341
342 if (maxuid && (status = imap_mboxcache_get (idata, idata->mailbox, 0)) &&
343 (status->uidnext < maxuid + 1))
344 status->uidnext = maxuid + 1;
345
346#if USE_HCACHE
347 mutt_hcache_store_raw (idata->hcache, "/UIDVALIDITY", &idata->uid_validity,
348 sizeof (idata->uid_validity), imap_hcache_keylen);
349 if (maxuid && idata->uidnext < maxuid + 1)
350 {
351 dprint (2, (debugfile, "Overriding UIDNEXT: %u -> %u\n", idata->uidnext, maxuid + 1));
352 idata->uidnext = maxuid + 1;
353 }
354 if (idata->uidnext > 1)
355 mutt_hcache_store_raw (idata->hcache, "/UIDNEXT", &idata->uidnext,
356 sizeof (idata->uidnext), imap_hcache_keylen);
357
358 /* We currently only sync CONDSTORE and QRESYNC on the initial download.
359 * To do it more often, we'll need to deal with flag updates combined with
360 * unsync'ed local flag changes. We'll also need to properly sync flags to
361 * the header cache on close. I'm not sure it's worth the added complexity.
362 */
363 if (initial_download)
364 {
365 if (has_condstore || has_qresync)
366 mutt_hcache_store_raw (idata->hcache, "/MODSEQ", &idata->modseq,
367 sizeof (idata->modseq), imap_hcache_keylen);
368 else
369 mutt_hcache_delete (idata->hcache, "/MODSEQ", imap_hcache_keylen);
370
371 if (has_qresync)
372 imap_hcache_store_uid_seqset (idata);
373 else
374 imap_hcache_clear_uid_seqset (idata);
375 }
376#endif /* USE_HCACHE */
377
378 if (ctx->msgcount > oldmsgcount)
379 {
380 /* TODO: it's not clear to me why we are calling mx_alloc_memory
381 * yet again. */
382 mx_alloc_memory(ctx);
383 mx_update_context (ctx, ctx->msgcount - oldmsgcount);
384 }
385
386 idata->reopen |= IMAP_REOPEN_ALLOW;
387
388 retval = msn_end;
389
390bail:
391#if USE_HCACHE
392 imap_hcache_close (idata);
393 FREE (&uid_seqset);
394#endif /* USE_HCACHE */
395
396 return retval;
397}
398
399#if USE_HCACHE
400/* Retrieve data from the header cache.
401 *
402 * Without CONDSTORE or QRESYNC, we need to query all the current
403 * UIDs and update their flag state and current MSN.
404 *
405 * For CONDSTORE, we still need to grab the existing UIDs and
406 * their MSN. The current flag state will be queried in
407 * read_headers_condstore_qresync_updates().
408 */
409static int read_headers_normal_eval_cache (IMAP_DATA *idata,
410 unsigned int msn_end,
411 unsigned int uidnext,
412 int store_flag_updates,
413 int eval_condstore)
414{
415 CONTEXT* ctx;
416 int idx, msgno, rc, mfhrc = 0;
417 progress_t progress;
418 IMAP_HEADER h;
419 char buf[LONG_STRING];
420
421 ctx = idata->ctx;
422 idx = ctx->msgcount;
423
424 /* L10N:
425 Comparing the cached data with the IMAP server's data */
426 mutt_progress_init (&progress, _("Evaluating cache..."),
427 MUTT_PROGRESS_MSG, ReadInc, msn_end);
428
429 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
430 * the flags in the header cache, and update them further below.
431 * Otherwise, we fetch the current state of the flags here. */
432 snprintf (buf, sizeof (buf),
433 "UID FETCH 1:%u (UID%s)", uidnext - 1,
434 eval_condstore ? "" : " FLAGS");
435
436 imap_cmd_start (idata, buf);
437
438 rc = IMAP_CMD_CONTINUE;
439 for (msgno = 1; rc == IMAP_CMD_CONTINUE; msgno++)
440 {
441 if (SigInt && query_abort_header_download (idata))
442 return -1;
443
444 mutt_progress_update (&progress, msgno, -1);
445
446 memset (&h, 0, sizeof (h));
447 h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
448 do
449 {
450 rc = imap_cmd_step (idata);
451 if (rc != IMAP_CMD_CONTINUE)
452 break;
453
454 if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, NULL)) < 0)
455 continue;
456
457 if (!h.data->uid)
458 {
459 dprint (2, (debugfile, "imap_read_headers: skipping hcache FETCH "
460 "response for message number %d missing a UID\n", h.data->msn));
461 continue;
462 }
463
464 if (h.data->msn < 1 || h.data->msn > msn_end)
465 {
466 dprint (1, (debugfile, "imap_read_headers: skipping hcache FETCH "
467 "response for unknown message number %d\n", h.data->msn));
468 continue;
469 }
470
471 if (idata->msn_index[h.data->msn - 1])
472 {
473 dprint (2, (debugfile, "imap_read_headers: skipping hcache FETCH "
474 "for duplicate message %d\n", h.data->msn));
475 continue;
476 }
477
478 ctx->hdrs[idx] = imap_hcache_get (idata, h.data->uid);
479 if (ctx->hdrs[idx])
480 {
481 idata->max_msn = MAX (idata->max_msn, h.data->msn);
482 idata->msn_index[h.data->msn - 1] = ctx->hdrs[idx];
483 int_hash_insert (idata->uid_hash, h.data->uid, ctx->hdrs[idx]);
484
485 ctx->hdrs[idx]->index = idx;
486 /* messages which have not been expunged are ACTIVE (borrowed from mh
487 * folders) */
488 ctx->hdrs[idx]->active = 1;
489 ctx->hdrs[idx]->changed = 0;
490 if (!eval_condstore)
491 {
492 ctx->hdrs[idx]->read = h.data->read;
493 ctx->hdrs[idx]->old = h.data->old;
494 ctx->hdrs[idx]->deleted = h.data->deleted;
495 ctx->hdrs[idx]->flagged = h.data->flagged;
496 ctx->hdrs[idx]->replied = h.data->replied;
497 }
498 else
499 {
500 h.data->read = ctx->hdrs[idx]->read;
501 h.data->old = ctx->hdrs[idx]->old;
502 h.data->deleted = ctx->hdrs[idx]->deleted;
503 h.data->flagged = ctx->hdrs[idx]->flagged;
504 h.data->replied = ctx->hdrs[idx]->replied;
505 }
506
507 /* ctx->hdrs[msgno]->received is restored from mutt_hcache_restore */
508 ctx->hdrs[idx]->data = (void *) (h.data);
509
510 ctx->msgcount++;
511 ctx->size += ctx->hdrs[idx]->content->length;
512
513 /* If this is the first time we are fetching, we need to
514 * store the current state of flags back into the header cache */
515 if (!eval_condstore && store_flag_updates)
516 imap_hcache_put (idata, ctx->hdrs[idx]);
517
518 h.data = NULL;
519 idx++;
520 }
521 }
522 while (mfhrc == -1);
523
524 imap_free_header_data (&h.data);
525
526 if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK)))
527 return -1;
528 }
529
530 return 0;
531}
532
533/* Retrieve data from the header cache.
534 *
535 * For QRESYNC, we grab the UIDs in order by MSN from the header
536 * cache.
537 *
538 * In read_headers_condstore_qresync_updates(). We will update change
539 * flags using CHANGEDSINCE and find out what UIDs have been expunged
540 * using VANISHED.
541 */
542static int read_headers_qresync_eval_cache (IMAP_DATA *idata, char *uid_seqset)
543{
544 CONTEXT* ctx;
545 int rc;
546 unsigned int uid, msn;
547 SEQSET_ITERATOR *iter;
548 HEADER *h;
549 IMAP_HEADER_DATA *ihd;
550
551 dprint (2, (debugfile, "Reading uid seqset from header cache\n"));
552 ctx = idata->ctx;
553 msn = 1;
554
555 iter = mutt_seqset_iterator_new (uid_seqset);
556 if (!iter)
557 return -1;
558
559 while ((rc = mutt_seqset_iterator_next (iter, &uid)) == 0)
560 {
561 /* The seqset may contain more headers than the fetch request, so
562 * we need to watch and reallocate the context and msn_index */
563 if (msn > idata->msn_index_size)
564 imap_alloc_msn_index (idata, msn);
565
566 h = imap_hcache_get (idata, uid);
567 if (h)
568 {
569 idata->max_msn = MAX (idata->max_msn, msn);
570 idata->msn_index[msn - 1] = h;
571
572 if (ctx->msgcount >= ctx->hdrmax)
573 mx_alloc_memory (ctx);
574
575 ihd = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
576 h->data = ihd;
577
578 h->index = ctx->msgcount;
579 h->active = 1;
580 h->changed = 0;
581 ihd->read = h->read;
582 ihd->old = h->old;
583 ihd->deleted = h->deleted;
584 ihd->flagged = h->flagged;
585 ihd->replied = h->replied;
586
587 ihd->msn = msn;
588 ihd->uid = uid;
589 int_hash_insert (idata->uid_hash, uid, h);
590
591 ctx->size += h->content->length;
592 ctx->hdrs[ctx->msgcount++] = h;
593
594 msn++;
595 }
596 }
597
598 mutt_seqset_iterator_free (&iter);
599
600 return rc;
601}
602
603/*
604 * Retrieve updates from the server.
605 *
606 * CONDSTORE and QRESYNC use FETCH extensions to grab updates.
607 */
608static int read_headers_condstore_qresync_updates (IMAP_DATA *idata,
609 unsigned int msn_end,
610 unsigned int uidnext,
611 unsigned long long hc_modseq,
612 int eval_qresync)
613{
614 CONTEXT* ctx;
615 progress_t progress;
616 int msgno, rc;
617 char buf[LONG_STRING];
618 unsigned int header_msn;
619 char *fetch_buf;
620
621 ctx = idata->ctx;
622
623 /* L10N:
624 Fetching IMAP flag changes, using the CONDSTORE extension */
625 mutt_progress_init (&progress, _("Fetching flag updates..."),
626 MUTT_PROGRESS_MSG, ReadInc, msn_end);
627
628 snprintf (buf, sizeof (buf),
629 "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
630 uidnext - 1, hc_modseq,
631 eval_qresync ? " VANISHED" : "");
632
633 imap_cmd_start (idata, buf);
634
635 rc = IMAP_CMD_CONTINUE;
636 for (msgno = 1; rc == IMAP_CMD_CONTINUE; msgno++)
637 {
638 if (SigInt && query_abort_header_download (idata))
639 return -1;
640
641 mutt_progress_update (&progress, msgno, -1);
642
643 /* cmd_parse_fetch will update the flags */
644 rc = imap_cmd_step (idata);
645 if (rc != IMAP_CMD_CONTINUE)
646 break;
647
648 /* so we just need to grab the header and persist it back into
649 * the header cache */
650 fetch_buf = idata->buf;
651 if (fetch_buf[0] != '*')
652 continue;
653
654 fetch_buf = imap_next_word (fetch_buf);
655 if (!isdigit ((unsigned char) *fetch_buf) ||
656 mutt_atoui (fetch_buf, &header_msn) < 0)
657 continue;
658
659 if (header_msn < 1 || header_msn > msn_end ||
660 !idata->msn_index[header_msn - 1])
661 {
662 dprint (1, (debugfile, "imap_read_headers: skipping CONDSTORE flag "
663 "update for unknown message number %u\n", header_msn));
664 continue;
665 }
666
667 imap_hcache_put (idata, idata->msn_index[header_msn - 1]);
668 }
669
670 /* The IMAP flag setting as part of cmd_parse_fetch() ends up
671 * flipping these on. */
672 idata->check_status &= ~IMAP_FLAGS_PENDING;
673 ctx->changed = 0;
674
675 /* VANISHED handling: we need to empty out the messages */
676 if (idata->reopen & IMAP_EXPUNGE_PENDING)
677 {
678 imap_hcache_close (idata);
679 imap_expunge_mailbox (idata);
680
681 /* undo expunge count updates.
682 * mx_update_context() will do this at the end of the header fetch. */
683 ctx->vcount = 0;
684 ctx->vsize = 0;
685 ctx->tagged = 0;
686 ctx->deleted = 0;
687 ctx->new = 0;
688 ctx->unread = 0;
689 ctx->changed = 0;
690 ctx->flagged = 0;
691
692 idata->hcache = imap_hcache_open (idata, NULL);
693 idata->reopen &= ~IMAP_EXPUNGE_PENDING;
694 }
695
696 return 0;
697}
698#endif /* USE_HCACHE */
699
700/* Retrieve new messages from the server
701 */
702static int read_headers_fetch_new (IMAP_DATA *idata, unsigned int msn_begin,
703 unsigned int msn_end, int evalhc,
704 unsigned int *maxuid, int initial_download)
705{
706 CONTEXT* ctx;
707 int idx, msgno, rc, mfhrc = 0, retval = -1;
708 unsigned int fetch_msn_end = 0;
709 progress_t progress;
710 char *hdrreq = NULL, *cmd;
711 BUFFER *tempfile = NULL;
712 FILE *fp = NULL;
713 IMAP_HEADER h;
714 BUFFER *b = NULL, *hdr_list = NULL;
715 static const char * const want_headers = "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL";
716
717 ctx = idata->ctx;
718 idx = ctx->msgcount;
719
720 hdr_list = mutt_buffer_pool_get ();
721 mutt_buffer_strcpy (hdr_list, want_headers);
722 if (ImapHeaders)
723 {
724 mutt_buffer_addch (hdr_list, ' ');
725 mutt_buffer_addstr (hdr_list, ImapHeaders);
726 }
727#ifdef USE_AUTOCRYPT
728 if (option (OPTAUTOCRYPT))
729 {
730 mutt_buffer_addch (hdr_list, ' ');
731 mutt_buffer_addstr (hdr_list, "AUTOCRYPT");
732 }
733#endif
734
735 if (mutt_bit_isset (idata->capabilities,IMAP4REV1))
736 {
737 safe_asprintf (&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]",
738 mutt_b2s (hdr_list));
739 }
740 else if (mutt_bit_isset (idata->capabilities,IMAP4))
741 {
742 safe_asprintf (&hdrreq, "RFC822.HEADER.LINES (%s)",
743 mutt_b2s (hdr_list));
744 }
745 else
746 { /* Unable to fetch headers for lower versions */
747 mutt_error _("Unable to fetch headers from this IMAP server version.");
748 mutt_sleep (2); /* pause a moment to let the user see the error */
749 goto bail;
750 }
751
752 mutt_buffer_pool_release (&hdr_list);
753
754 /* instead of downloading all headers and then parsing them, we parse them
755 * as they come in. */
756 tempfile = mutt_buffer_pool_get ();
757 mutt_buffer_mktemp (tempfile);
758 if (!(fp = safe_fopen (mutt_b2s (tempfile), "w+")))
759 {
760 mutt_error (_("Could not create temporary file %s"), mutt_b2s (tempfile));
761 mutt_sleep (2);
762 goto bail;
763 }
764 unlink (mutt_b2s (tempfile));
765 mutt_buffer_pool_release (&tempfile);
766
767 mutt_progress_init (&progress, _("Fetching message headers..."),
768 MUTT_PROGRESS_MSG, ReadInc, msn_end);
769
770 b = mutt_buffer_pool_get ();
771
772 /* NOTE:
773 * The (fetch_msn_end < msn_end) used to be important to prevent
774 * an infinite loop, in the event the server did not return all
775 * the headers (due to a pending expunge, for example).
776 *
777 * I believe the new chunking imap_fetch_msn_seqset()
778 * implementation and "msn_begin = fetch_msn_end + 1" assignment
779 * at the end of the loop makes the comparison unneeded, but to be
780 * cautious I'm keeping it.
781 */
782 while ((fetch_msn_end < msn_end) &&
783 imap_fetch_msn_seqset (b, idata, evalhc, msn_begin, msn_end,
784 &fetch_msn_end))
785 {
786 safe_asprintf (&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
787 mutt_b2s (b), hdrreq);
788 imap_cmd_start (idata, cmd);
789 FREE (&cmd);
790
791 rc = IMAP_CMD_CONTINUE;
792 for (msgno = msn_begin; rc == IMAP_CMD_CONTINUE; msgno++)
793 {
794 if (initial_download && SigInt &&
795 query_abort_header_download (idata))
796 goto bail;
797
798 mutt_progress_update (&progress, msgno, -1);
799
800 rewind (fp);
801 memset (&h, 0, sizeof (h));
802 h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
803
804 /* this DO loop does two things:
805 * 1. handles untagged messages, so we can try again on the same msg
806 * 2. fetches the tagged response at the end of the last message.
807 */
808 do
809 {
810 rc = imap_cmd_step (idata);
811 if (rc != IMAP_CMD_CONTINUE)
812 break;
813
814 if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, fp)) < 0)
815 continue;
816
817 if (!ftello (fp))
818 {
819 dprint (2, (debugfile, "msg_fetch_header: ignoring fetch response with no body\n"));
820 continue;
821 }
822
823 /* make sure we don't get remnants from older larger message headers */
824 fputs ("\n\n", fp);
825
826 if (h.data->msn < 1 || h.data->msn > fetch_msn_end)
827 {
828 dprint (1, (debugfile, "imap_read_headers: skipping FETCH response for "
829 "unknown message number %d\n", h.data->msn));
830 continue;
831 }
832
833 /* May receive FLAGS updates in a separate untagged response (#2935) */
834 if (idata->msn_index[h.data->msn - 1])
835 {
836 dprint (2, (debugfile, "imap_read_headers: skipping FETCH response for "
837 "duplicate message %d\n", h.data->msn));
838 continue;
839 }
840
841 ctx->hdrs[idx] = mutt_new_header ();
842
843 idata->max_msn = MAX (idata->max_msn, h.data->msn);
844 idata->msn_index[h.data->msn - 1] = ctx->hdrs[idx];
845 int_hash_insert (idata->uid_hash, h.data->uid, ctx->hdrs[idx]);
846
847 ctx->hdrs[idx]->index = idx;
848 /* messages which have not been expunged are ACTIVE (borrowed from mh
849 * folders) */
850 ctx->hdrs[idx]->active = 1;
851 ctx->hdrs[idx]->changed = 0;
852 ctx->hdrs[idx]->read = h.data->read;
853 ctx->hdrs[idx]->old = h.data->old;
854 ctx->hdrs[idx]->deleted = h.data->deleted;
855 ctx->hdrs[idx]->flagged = h.data->flagged;
856 ctx->hdrs[idx]->replied = h.data->replied;
857 ctx->hdrs[idx]->received = h.received;
858 ctx->hdrs[idx]->data = (void *) (h.data);
859
860 if (*maxuid < h.data->uid)
861 *maxuid = h.data->uid;
862
863 rewind (fp);
864 /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends
865 * on h.received being set */
866 ctx->hdrs[idx]->env = mutt_read_rfc822_header (fp, ctx->hdrs[idx],
867 0, 0);
868 /* content built as a side-effect of mutt_read_rfc822_header */
869 ctx->hdrs[idx]->content->length = h.content_length;
870 ctx->size += h.content_length;
871
872#if USE_HCACHE
873 imap_hcache_put (idata, ctx->hdrs[idx]);
874#endif /* USE_HCACHE */
875
876 ctx->msgcount++;
877
878 h.data = NULL;
879 idx++;
880 }
881 while (mfhrc == -1);
882
883 imap_free_header_data (&h.data);
884
885 if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK)))
886 goto bail;
887 }
888
889 /* In case we get new mail while fetching the headers. */
890 if (idata->reopen & IMAP_NEWMAIL_PENDING)
891 {
892 msn_end = idata->newMailCount;
893 while (msn_end > ctx->hdrmax)
894 mx_alloc_memory (ctx);
895 imap_alloc_msn_index (idata, msn_end);
896 idata->reopen &= ~IMAP_NEWMAIL_PENDING;
897 idata->newMailCount = 0;
898 }
899
900 /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
901 * must not get any EXPUNGE/VANISHED responses in the middle of a
902 * FETCH, nor when no command is in progress (e.g. between the
903 * chunked FETCH commands). We previously tried to be robust by
904 * setting:
905 * msn_begin = idata->max_msn + 1;
906 * but with chunking (and the mythical header cache holes) this
907 * may not be correct. So here we must assume the msn values have
908 * not been altered during or after the fetch.
909 */
910 msn_begin = fetch_msn_end + 1;
911 }
912
913 retval = 0;
914
915bail:
916 mutt_buffer_pool_release (&hdr_list);
917 mutt_buffer_pool_release (&b);
918 mutt_buffer_pool_release (&tempfile);
919 safe_fclose (&fp);
920 FREE (&hdrreq);
921
922 return retval;
923}
924
925int imap_fetch_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
926{
927 IMAP_DATA* idata;
928 HEADER* h;
929 ENVELOPE* newenv;
930 char buf[LONG_STRING];
931 char *pc;
932 unsigned int bytes;
933 progress_t progressbar;
934 unsigned int uid;
935 int cacheno;
936 IMAP_CACHE *cache;
937 int read;
938 int rc;
939 /* Sam's weird courier server returns an OK response even when FETCH
940 * fails. Thanks Sam. */
941 short fetched = 0;
942 int output_progress;
943
944 idata = (IMAP_DATA*) ctx->data;
945 h = ctx->hdrs[msgno];
946
947 if ((msg->fp = msg_cache_get (idata, h)))
948 {
949 if (HEADER_DATA(h)->parsed)
950 return 0;
951 else
952 goto parsemsg;
953 }
954
955 /* we still do some caching even if imap_cachedir is unset */
956 /* see if we already have the message in our cache */
957 cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
958 cache = &idata->cache[cacheno];
959
960 if (cache->path)
961 {
962 /* don't treat cache errors as fatal, just fall back. */
963 if (cache->uid == HEADER_DATA(h)->uid &&
964 (msg->fp = fopen (cache->path, "r")))
965 return 0;
966 else
967 {
968 unlink (cache->path);
969 FREE (&cache->path);
970 }
971 }
972
973 /* This function is called in a few places after endwin()
974 * e.g. _mutt_pipe_message(). */
975 output_progress = !isendwin ();
976 if (output_progress)
977 mutt_message _("Fetching message...");
978
979 if (!(msg->fp = msg_cache_put (idata, h)))
980 {
981 BUFFER *path;
982
983 cache->uid = HEADER_DATA(h)->uid;
984
985 path = mutt_buffer_pool_get ();
986 mutt_buffer_mktemp (path);
987 cache->path = safe_strdup (mutt_b2s (path));
988 mutt_buffer_pool_release (&path);
989
990 if (!(msg->fp = safe_fopen (cache->path, "w+")))
991 {
992 FREE (&cache->path);
993 return -1;
994 }
995 }
996
997 /* mark this header as currently inactive so the command handler won't
998 * also try to update it. HACK until all this code can be moved into the
999 * command handler */
1000 h->active = 0;
1001
1002 snprintf (buf, sizeof (buf), "UID FETCH %u %s", HEADER_DATA(h)->uid,
1003 (mutt_bit_isset (idata->capabilities, IMAP4REV1) ?
1004 (option (OPTIMAPPEEK) ? "BODY.PEEK[]" : "BODY[]") :
1005 "RFC822"));
1006
1007 imap_cmd_start (idata, buf);
1008 do
1009 {
1010 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1011 break;
1012
1013 pc = idata->buf;
1014 pc = imap_next_word (pc);
1015 pc = imap_next_word (pc);
1016
1017 if (!ascii_strncasecmp ("FETCH", pc, 5))
1018 {
1019 while (*pc)
1020 {
1021 pc = imap_next_word (pc);
1022 if (pc[0] == '(')
1023 pc++;
1024 if (ascii_strncasecmp ("UID", pc, 3) == 0)
1025 {
1026 pc = imap_next_word (pc);
1027 if (mutt_atoui (pc, &uid) < 0)
1028 goto bail;
1029 if (uid != HEADER_DATA(h)->uid)
1030 mutt_error (_("The message index is incorrect. Try reopening the mailbox."));
1031 }
1032 else if ((ascii_strncasecmp ("RFC822", pc, 6) == 0) ||
1033 (ascii_strncasecmp ("BODY[]", pc, 6) == 0))
1034 {
1035 pc = imap_next_word (pc);
1036 if (imap_get_literal_count(pc, &bytes) < 0)
1037 {
1038 imap_error ("imap_fetch_message()", buf);
1039 goto bail;
1040 }
1041 if (output_progress)
1042 {
1043 mutt_progress_init (&progressbar, _("Fetching message..."),
1044 MUTT_PROGRESS_SIZE, NetInc, bytes);
1045 }
1046 if (imap_read_literal (msg->fp, idata, bytes,
1047 output_progress ? &progressbar : NULL) < 0)
1048 goto bail;
1049 /* pick up trailing line */
1050 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1051 goto bail;
1052 pc = idata->buf;
1053
1054 fetched = 1;
1055 }
1056 /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
1057 * change (eg from \Unseen to \Seen).
1058 * Uncommitted changes in mutt take precedence. If we decide to
1059 * incrementally update flags later, this won't stop us syncing */
1060 else if ((ascii_strncasecmp ("FLAGS", pc, 5) == 0) && !h->changed)
1061 {
1062 if ((pc = imap_set_flags (idata, h, pc, NULL)) == NULL)
1063 goto bail;
1064 }
1065 }
1066 }
1067 }
1068 while (rc == IMAP_CMD_CONTINUE);
1069
1070 /* see comment before command start. */
1071 h->active = 1;
1072
1073 fflush (msg->fp);
1074 if (ferror (msg->fp))
1075 {
1076 mutt_perror (cache->path);
1077 goto bail;
1078 }
1079
1080 if (rc != IMAP_CMD_OK)
1081 goto bail;
1082
1083 if (!fetched || !imap_code (idata->buf))
1084 goto bail;
1085
1086 msg_cache_commit (idata, h);
1087
1088parsemsg:
1089 /* Update the header information. Previously, we only downloaded a
1090 * portion of the headers, those required for the main display.
1091 */
1092 rewind (msg->fp);
1093 /* It may be that the Status header indicates a message is read, but the
1094 * IMAP server doesn't know the message has been \Seen. So we capture
1095 * the server's notion of 'read' and if it differs from the message info
1096 * picked up in mutt_read_rfc822_header, we mark the message (and context
1097 * changed). Another possibility: ignore Status on IMAP?*/
1098 read = h->read;
1099 newenv = mutt_read_rfc822_header (msg->fp, h, 0, 0);
1100 mutt_merge_envelopes(h->env, &newenv);
1101
1102 /* see above. We want the new status in h->read, so we unset it manually
1103 * and let mutt_set_flag set it correctly, updating context. */
1104 if (read != h->read)
1105 {
1106 h->read = read;
1107 mutt_set_flag (ctx, h, MUTT_NEW, read);
1108 }
1109
1110 h->lines = 0;
1111 fgets (buf, sizeof (buf), msg->fp);
1112 while (!feof (msg->fp))
1113 {
1114 h->lines++;
1115 fgets (buf, sizeof (buf), msg->fp);
1116 }
1117
1118 h->content->length = ftell (msg->fp) - h->content->offset;
1119
1120 /* This needs to be done in case this is a multipart message */
1121#if defined(HAVE_PGP) || defined(HAVE_SMIME)
1122 h->security = crypt_query (h->content);
1123#endif
1124
1125 mutt_clear_error();
1126 rewind (msg->fp);
1127 HEADER_DATA(h)->parsed = 1;
1128
1129 return 0;
1130
1131bail:
1132 h->active = 1;
1133 safe_fclose (&msg->fp);
1134 imap_cache_del (idata, h);
1135 if (cache->path)
1136 {
1137 unlink (cache->path);
1138 FREE (&cache->path);
1139 }
1140
1141 return -1;
1142}
1143
1144int imap_close_message (CONTEXT *ctx, MESSAGE *msg)
1145{
1146 return safe_fclose (&msg->fp);
1147}
1148
1149int imap_commit_message (CONTEXT *ctx, MESSAGE *msg)
1150{
1151 int r = safe_fclose (&msg->fp);
1152
1153 if (r)
1154 return r;
1155
1156 return imap_append_message (ctx, msg);
1157}
1158
1159int imap_append_message (CONTEXT *ctx, MESSAGE *msg)
1160{
1161 IMAP_DATA* idata;
1162 FILE *fp = NULL;
1163 char buf[LONG_STRING*2];
1164 char mbox[LONG_STRING];
1165 char mailbox[LONG_STRING];
1166 char internaldate[IMAP_DATELEN];
1167 char imap_flags[SHORT_STRING];
1168 size_t len;
1169 progress_t progressbar;
1170 size_t sent;
1171 int c, last;
1172 IMAP_MBOX mx;
1173 int rc;
1174
1175 idata = (IMAP_DATA*) ctx->data;
1176
1177 if (imap_parse_path (ctx->path, &mx))
1178 return -1;
1179
1180 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
1181 if (!*mailbox)
1182 strfcpy (mailbox, "INBOX", sizeof (mailbox));
1183
1184 if ((fp = fopen (msg->path, "r")) == NULL)
1185 {
1186 mutt_perror (msg->path);
1187 goto fail;
1188 }
1189
1190 /* currently we set the \Seen flag on all messages, but probably we
1191 * should scan the message Status header for flag info. Since we're
1192 * already rereading the whole file for length it isn't any more
1193 * expensive (it'd be nice if we had the file size passed in already
1194 * by the code that writes the file, but that's a lot of changes.
1195 * Ideally we'd have a HEADER structure with flag info here... */
1196 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1197 {
1198 if (c == '\n' && last != '\r')
1199 len++;
1200
1201 len++;
1202 }
1203 rewind (fp);
1204
1205 mutt_progress_init (&progressbar, _("Uploading message..."),
1206 MUTT_PROGRESS_SIZE, NetInc, len);
1207
1208 imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox);
1209 imap_make_date (internaldate, msg->received);
1210
1211 imap_flags[0] = imap_flags[1] = 0;
1212 if (msg->flags.read)
1213 safe_strcat (imap_flags, sizeof (imap_flags), " \\Seen");
1214 if (msg->flags.replied)
1215 safe_strcat (imap_flags, sizeof (imap_flags), " \\Answered");
1216 if (msg->flags.flagged)
1217 safe_strcat (imap_flags, sizeof (imap_flags), " \\Flagged");
1218 if (msg->flags.draft)
1219 safe_strcat (imap_flags, sizeof (imap_flags), " \\Draft");
1220
1221 snprintf (buf, sizeof (buf), "APPEND %s (%s) \"%s\" {%lu}", mbox,
1222 imap_flags + 1,
1223 internaldate,
1224 (unsigned long) len);
1225
1226 imap_cmd_start (idata, buf);
1227
1228 do
1229 rc = imap_cmd_step (idata);
1230 while (rc == IMAP_CMD_CONTINUE);
1231
1232 if (rc != IMAP_CMD_RESPOND)
1233 goto cmd_step_fail;
1234
1235 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1236 {
1237 if (c == '\n' && last != '\r')
1238 buf[len++] = '\r';
1239
1240 buf[len++] = c;
1241
1242 if (len > sizeof(buf) - 3)
1243 {
1244 sent += len;
1245 if (flush_buffer (buf, &len, idata->conn) < 0)
1246 goto fail;
1247 mutt_progress_update (&progressbar, sent, -1);
1248 }
1249 }
1250
1251 if (len)
1252 if (flush_buffer (buf, &len, idata->conn) < 0)
1253 goto fail;
1254
1255 if (mutt_socket_write (idata->conn, "\r\n") < 0)
1256 goto fail;
1257 safe_fclose (&fp);
1258
1259 do
1260 rc = imap_cmd_step (idata);
1261 while (rc == IMAP_CMD_CONTINUE);
1262
1263 if (rc != IMAP_CMD_OK)
1264 goto cmd_step_fail;
1265
1266 FREE (&mx.mbox);
1267 return 0;
1268
1269cmd_step_fail:
1270 dprint (1, (debugfile, "imap_append_message(): command failed: %s\n",
1271 idata->buf));
1272 if (rc != IMAP_CMD_BAD)
1273 {
1274 char *pc;
1275
1276 pc = imap_next_word (idata->buf); /* skip sequence number or token */
1277 pc = imap_next_word (pc); /* skip response code */
1278 if (*pc)
1279 {
1280 mutt_error ("%s", pc);
1281 mutt_sleep (1);
1282 }
1283 }
1284
1285fail:
1286 safe_fclose (&fp);
1287 FREE (&mx.mbox);
1288 return -1;
1289}
1290
1291/* imap_copy_messages: use server COPY command to copy messages to another
1292 * folder.
1293 * Return codes:
1294 * -1: error
1295 * 0: success
1296 * 1: non-fatal error - try fetch/append */
1297int imap_copy_messages (CONTEXT* ctx, HEADER* h, const char* dest, int delete)
1298{
1299 IMAP_DATA* idata;
1300 BUFFER cmd, sync_cmd;
1301 char mbox[LONG_STRING];
1302 char mmbox[LONG_STRING];
1303 char prompt[LONG_STRING];
1304 int rc;
1305 int n;
1306 IMAP_MBOX mx;
1307 int err_continue = MUTT_NO;
1308 int triedcreate = 0;
1309
1310 idata = (IMAP_DATA*) ctx->data;
1311
1312 if (imap_parse_path (dest, &mx))
1313 {
1314 dprint (1, (debugfile, "imap_copy_messages: bad destination %s\n", dest));
1315 return -1;
1316 }
1317
1318 /* check that the save-to folder is in the same account */
1319 if (!mutt_account_match (&(CTX_DATA->conn->account), &(mx.account)))
1320 {
1321 dprint (3, (debugfile, "imap_copy_messages: %s not same server as %s\n",
1322 dest, ctx->path));
1323 return 1;
1324 }
1325
1326 if (h && h->attach_del)
1327 {
1328 dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n"));
1329 return 1;
1330 }
1331
1332 imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox));
1333 if (!*mbox)
1334 strfcpy (mbox, "INBOX", sizeof (mbox));
1335 imap_munge_mbox_name (idata, mmbox, sizeof (mmbox), mbox);
1336
1337 /* loop in case of TRYCREATE */
1338 do
1339 {
1340 mutt_buffer_init (&sync_cmd);
1341 mutt_buffer_init (&cmd);
1342
1343 /* Null HEADER* means copy tagged messages */
1344 if (!h)
1345 {
1346 /* if any messages have attachments to delete, fall through to FETCH
1347 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1348 * remainder. */
1349 for (n = 0; n < ctx->msgcount; n++)
1350 {
1351 if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del)
1352 {
1353 dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n"));
1354 return 1;
1355 }
1356
1357 if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active &&
1358 ctx->hdrs[n]->changed)
1359 {
1360 rc = imap_sync_message_for_copy (idata, ctx->hdrs[n], &sync_cmd, &err_continue);
1361 if (rc < 0)
1362 {
1363 dprint (1, (debugfile, "imap_copy_messages: could not sync\n"));
1364 goto out;
1365 }
1366 }
1367 }
1368
1369 rc = imap_exec_msgset (idata, "UID COPY", mmbox, MUTT_TAG, 0, 0);
1370 if (!rc)
1371 {
1372 dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n"));
1373 rc = -1;
1374 goto out;
1375 }
1376 else if (rc < 0)
1377 {
1378 dprint (1, (debugfile, "could not queue copy\n"));
1379 goto out;
1380 }
1381 else
1382 mutt_message (_("Copying %d messages to %s..."), rc, mbox);
1383 }
1384 else
1385 {
1386 mutt_message (_("Copying message %d to %s..."), h->index+1, mbox);
1387 mutt_buffer_add_printf (&cmd, "UID COPY %u %s", HEADER_DATA (h)->uid, mmbox);
1388
1389 if (h->active && h->changed)
1390 {
1391 rc = imap_sync_message_for_copy (idata, h, &sync_cmd, &err_continue);
1392 if (rc < 0)
1393 {
1394 dprint (1, (debugfile, "imap_copy_messages: could not sync\n"));
1395 goto out;
1396 }
1397 }
1398 if ((rc = imap_exec (idata, cmd.data, IMAP_CMD_QUEUE)) < 0)
1399 {
1400 dprint (1, (debugfile, "could not queue copy\n"));
1401 goto out;
1402 }
1403 }
1404
1405 /* let's get it on */
1406 rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
1407 if (rc == -2)
1408 {
1409 if (triedcreate)
1410 {
1411 dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox));
1412 break;
1413 }
1414 /* bail out if command failed for reasons other than nonexistent target */
1415 if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11))
1416 break;
1417 dprint (3, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n"));
1418 snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox);
1419 if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1)
1420 {
1421 mutt_clear_error ();
1422 goto out;
1423 }
1424 if (imap_create_mailbox (idata, mbox) < 0)
1425 break;
1426 triedcreate = 1;
1427 }
1428 }
1429 while (rc == -2);
1430
1431 if (rc != 0)
1432 {
1433 imap_error ("imap_copy_messages", idata->buf);
1434 goto out;
1435 }
1436
1437 /* cleanup */
1438 if (delete)
1439 {
1440 if (!h)
1441 for (n = 0; n < ctx->msgcount; n++)
1442 {
1443 if (ctx->hdrs[n]->tagged)
1444 {
1445 mutt_set_flag (ctx, ctx->hdrs[n], MUTT_DELETE, 1);
1446 mutt_set_flag (ctx, ctx->hdrs[n], MUTT_PURGE, 1);
1447 if (option (OPTDELETEUNTAG))
1448 mutt_set_flag (ctx, ctx->hdrs[n], MUTT_TAG, 0);
1449 }
1450 }
1451 else
1452 {
1453 mutt_set_flag (ctx, h, MUTT_DELETE, 1);
1454 mutt_set_flag (ctx, h, MUTT_PURGE, 1);
1455 if (option (OPTDELETEUNTAG))
1456 mutt_set_flag (ctx, h, MUTT_TAG, 0);
1457 }
1458 }
1459
1460 rc = 0;
1461
1462out:
1463 if (cmd.data)
1464 FREE (&cmd.data);
1465 if (sync_cmd.data)
1466 FREE (&sync_cmd.data);
1467 FREE (&mx.mbox);
1468
1469 return rc < 0 ? -1 : rc;
1470}
1471
1472static body_cache_t *msg_cache_open (IMAP_DATA *idata)
1473{
1474 BUFFER *mailbox;
1475 body_cache_t *rv;
1476
1477 if (idata->bcache)
1478 return idata->bcache;
1479
1480 mailbox = mutt_buffer_pool_get ();
1481 imap_cachepath (idata, idata->mailbox, mailbox);
1482 rv = mutt_bcache_open (&idata->conn->account, mutt_b2s (mailbox));
1483 mutt_buffer_pool_release (&mailbox);
1484
1485 return rv;
1486}
1487
1488static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h)
1489{
1490 char id[SHORT_STRING];
1491
1492 if (!idata || !h)
1493 return NULL;
1494
1495 idata->bcache = msg_cache_open (idata);
1496 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
1497 return mutt_bcache_get (idata->bcache, id);
1498}
1499
1500static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h)
1501{
1502 char id[SHORT_STRING];
1503
1504 if (!idata || !h)
1505 return NULL;
1506
1507 idata->bcache = msg_cache_open (idata);
1508 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
1509 return mutt_bcache_put (idata->bcache, id, 1);
1510}
1511
1512static int msg_cache_commit (IMAP_DATA* idata, HEADER* h)
1513{
1514 char id[SHORT_STRING];
1515
1516 if (!idata || !h)
1517 return -1;
1518
1519 idata->bcache = msg_cache_open (idata);
1520 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
1521
1522 return mutt_bcache_commit (idata->bcache, id);
1523}
1524
1525int imap_cache_del (IMAP_DATA* idata, HEADER* h)
1526{
1527 char id[SHORT_STRING];
1528
1529 if (!idata || !h)
1530 return -1;
1531
1532 idata->bcache = msg_cache_open (idata);
1533 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
1534 return mutt_bcache_del (idata->bcache, id);
1535}
1536
1537static int msg_cache_clean_cb (const char* id, body_cache_t* bcache, void* data)
1538{
1539 unsigned int uv, uid;
1540 IMAP_DATA* idata = (IMAP_DATA*)data;
1541
1542 if (sscanf (id, "%u-%u", &uv, &uid) != 2)
1543 return 0;
1544
1545 /* bad UID */
1546 if (uv != idata->uid_validity ||
1547 !int_hash_find (idata->uid_hash, uid))
1548 mutt_bcache_del (bcache, id);
1549
1550 return 0;
1551}
1552
1553int imap_cache_clean (IMAP_DATA* idata)
1554{
1555 idata->bcache = msg_cache_open (idata);
1556 mutt_bcache_list (idata->bcache, msg_cache_clean_cb, idata);
1557
1558 return 0;
1559}
1560
1561/* imap_add_keywords: concatenate custom IMAP tags to list, if they
1562 * appear in the folder flags list. Why wouldn't they? */
1563void imap_add_keywords (char* s, HEADER* h, LIST* mailbox_flags, size_t slen)
1564{
1565 LIST *keywords;
1566
1567 if (!mailbox_flags || !HEADER_DATA(h) || !HEADER_DATA(h)->keywords)
1568 return;
1569
1570 keywords = HEADER_DATA(h)->keywords->next;
1571
1572 while (keywords)
1573 {
1574 if (imap_has_flag (mailbox_flags, keywords->data))
1575 {
1576 safe_strcat (s, slen, keywords->data);
1577 safe_strcat (s, slen, " ");
1578 }
1579 keywords = keywords->next;
1580 }
1581}
1582
1583/* imap_free_header_data: free IMAP_HEADER structure */
1584void imap_free_header_data (IMAP_HEADER_DATA** data)
1585{
1586 if (*data)
1587 {
1588 /* this should be safe even if the list wasn't used */
1589 mutt_free_list (&((*data)->keywords));
1590 FREE (data); /* __FREE_CHECKED__ */
1591 }
1592}
1593
1594/* Sets server_changes to 1 if a change to a flag is made, or in the
1595 * case of local_changes, if a change to a flag _would_ have been
1596 * made. */
1597static void imap_set_changed_flag (CONTEXT *ctx, HEADER *h, int local_changes,
1598 int *server_changes, int flag_name, int old_hd_flag,
1599 int new_hd_flag, int h_flag)
1600{
1601 /* If there are local_changes, we only want to note if the server
1602 * flags have changed, so we can set a reopen flag in
1603 * cmd_parse_fetch(). We don't want to count a local modification
1604 * to the header flag as a "change".
1605 */
1606 if ((old_hd_flag != new_hd_flag) || (!local_changes))
1607 {
1608 if (new_hd_flag != h_flag)
1609 {
1610 if (server_changes)
1611 *server_changes = 1;
1612
1613 /* Local changes have priority */
1614 if (!local_changes)
1615 mutt_set_flag (ctx, h, flag_name, new_hd_flag);
1616 }
1617 }
1618}
1619
1620/* imap_set_flags: fill out the message header according to the flags from
1621 * the server. Expects a flags line of the form "FLAGS (flag flag ...)"
1622 *
1623 * Sets server_changes to 1 if a change to a flag is made, or in the
1624 * case of h->changed, if a change to a flag _would_ have been
1625 * made. */
1626char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s, int *server_changes)
1627{
1628 CONTEXT* ctx = idata->ctx;
1629 IMAP_HEADER newh;
1630 IMAP_HEADER_DATA old_hd;
1631 IMAP_HEADER_DATA* hd;
1632 unsigned char readonly;
1633 int local_changes;
1634
1635 local_changes = h->changed;
1636
1637 memset (&newh, 0, sizeof (newh));
1638 hd = h->data;
1639 newh.data = hd;
1640
1641 memcpy (&old_hd, hd, sizeof(old_hd));
1642
1643 dprint (2, (debugfile, "imap_set_flags: parsing FLAGS\n"));
1644 if ((s = msg_parse_flags (&newh, s)) == NULL)
1645 return NULL;
1646
1647 /* YAUH (yet another ugly hack): temporarily set context to
1648 * read-write even if it's read-only, so *server* updates of
1649 * flags can be processed by mutt_set_flag. ctx->changed must
1650 * be restored afterwards */
1651 readonly = ctx->readonly;
1652 ctx->readonly = 0;
1653
1654 /* This is redundant with the following two checks. Removing:
1655 * mutt_set_flag (ctx, h, MUTT_NEW, !(hd->read || hd->old));
1656 */
1657 imap_set_changed_flag (ctx, h, local_changes, server_changes,
1658 MUTT_OLD, old_hd.old, hd->old, h->old);
1659 imap_set_changed_flag (ctx, h, local_changes, server_changes,
1660 MUTT_READ, old_hd.read, hd->read, h->read);
1661 imap_set_changed_flag (ctx, h, local_changes, server_changes,
1662 MUTT_DELETE, old_hd.deleted, hd->deleted, h->deleted);
1663 imap_set_changed_flag (ctx, h, local_changes, server_changes,
1664 MUTT_FLAG, old_hd.flagged, hd->flagged, h->flagged);
1665 imap_set_changed_flag (ctx, h, local_changes, server_changes,
1666 MUTT_REPLIED, old_hd.replied, hd->replied, h->replied);
1667
1668 /* this message is now definitively *not* changed (mutt_set_flag
1669 * marks things changed as a side-effect) */
1670 if (!local_changes)
1671 h->changed = 0;
1672 ctx->changed &= ~readonly;
1673 ctx->readonly = readonly;
1674
1675 return s;
1676}
1677
1678
1679/* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER.
1680 * Expects string beginning with * n FETCH.
1681 * Returns:
1682 * 0 on success
1683 * -1 if the string is not a fetch response
1684 * -2 if the string is a corrupt fetch response */
1685static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
1686{
1687 IMAP_DATA* idata;
1688 unsigned int bytes;
1689 int rc = -1; /* default now is that string isn't FETCH response*/
1690 int parse_rc;
1691
1692 idata = (IMAP_DATA*) ctx->data;
1693
1694 if (buf[0] != '*')
1695 return rc;
1696
1697 /* skip to message number */
1698 buf = imap_next_word (buf);
1699 if (mutt_atoui (buf, &h->data->msn) < 0)
1700 return rc;
1701
1702 /* find FETCH tag */
1703 buf = imap_next_word (buf);
1704 if (ascii_strncasecmp ("FETCH", buf, 5))
1705 return rc;
1706
1707 rc = -2; /* we've got a FETCH response, for better or worse */
1708 if (!(buf = strchr (buf, '(')))
1709 return rc;
1710 buf++;
1711
1712 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
1713 * read header lines and call it again. Silly. */
1714 parse_rc = msg_parse_fetch (h, buf);
1715 if (!parse_rc)
1716 return 0;
1717 if (parse_rc != -2 || !fp)
1718 return rc;
1719
1720 if (imap_get_literal_count (buf, &bytes) == 0)
1721 {
1722 imap_read_literal (fp, idata, bytes, NULL);
1723
1724 /* we may have other fields of the FETCH _after_ the literal
1725 * (eg Domino puts FLAGS here). Nothing wrong with that, either.
1726 * This all has to go - we should accept literals and nonliterals
1727 * interchangeably at any time. */
1728 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1729 return rc;
1730
1731 if (msg_parse_fetch (h, idata->buf) == -1)
1732 return rc;
1733 }
1734
1735 rc = 0; /* success */
1736
1737 /* subtract headers from message size - unfortunately only the subset of
1738 * headers we've requested. */
1739 h->content_length -= bytes;
1740
1741 return rc;
1742}
1743
1744/* msg_parse_fetch: handle headers returned from header fetch.
1745 * Returns:
1746 * 0 on success
1747 * -1 if the string is corrupted
1748 * -2 if the fetch contains a body or header lines
1749 * that still need to be parsed.
1750 */
1751static int msg_parse_fetch (IMAP_HEADER *h, char *s)
1752{
1753 char tmp[SHORT_STRING];
1754 char *ptmp;
1755 size_t dlen;
1756
1757 if (!s)
1758 return -1;
1759
1760 while (*s)
1761 {
1762 SKIPWS (s);
1763
1764 if (ascii_strncasecmp ("FLAGS", s, 5) == 0)
1765 {
1766 if ((s = msg_parse_flags (h, s)) == NULL)
1767 return -1;
1768 }
1769 else if (ascii_strncasecmp ("UID", s, 3) == 0)
1770 {
1771 s += 3;
1772 SKIPWS (s);
1773 if (mutt_atoui (s, &h->data->uid) < 0)
1774 return -1;
1775
1776 s = imap_next_word (s);
1777 }
1778 else if (ascii_strncasecmp ("INTERNALDATE", s, 12) == 0)
1779 {
1780 s += 12;
1781 SKIPWS (s);
1782 if (*s != '\"')
1783 {
1784 dprint (1, (debugfile, "msg_parse_fetch(): bogus INTERNALDATE entry: %s\n", s));
1785 return -1;
1786 }
1787 s++;
1788 ptmp = tmp;
1789 dlen = sizeof(tmp) - 1;
1790 while (*s && *s != '\"' && dlen)
1791 {
1792 *ptmp++ = *s++;
1793 dlen--;
1794 }
1795 if (*s != '\"')
1796 return -1;
1797 s++; /* skip past the trailing " */
1798 *ptmp = 0;
1799 h->received = imap_parse_date (tmp);
1800 }
1801 else if (ascii_strncasecmp ("RFC822.SIZE", s, 11) == 0)
1802 {
1803 s += 11;
1804 SKIPWS (s);
1805 ptmp = tmp;
1806 dlen = sizeof(tmp) - 1;
1807 while (isdigit ((unsigned char) *s) && dlen)
1808 {
1809 *ptmp++ = *s++;
1810 dlen--;
1811 }
1812 *ptmp = 0;
1813 if (mutt_atol (tmp, &h->content_length) < 0)
1814 return -1;
1815 }
1816 else if (!ascii_strncasecmp ("BODY", s, 4) ||
1817 !ascii_strncasecmp ("RFC822.HEADER", s, 13))
1818 {
1819 /* handle above, in msg_fetch_header */
1820 return -2;
1821 }
1822 else if (ascii_strncasecmp ("MODSEQ", s, 6) == 0)
1823 {
1824 s += 6;
1825 SKIPWS(s);
1826 if (*s != '(')
1827 {
1828 dprint (1, (debugfile, "msg_parse_flags: bogus MODSEQ response: %s\n",
1829 s));
1830 return -1;
1831 }
1832 s++;
1833 while (*s && *s != ')')
1834 s++;
1835 if (*s == ')')
1836 s++;
1837 else
1838 {
1839 dprint (1, (debugfile,
1840 "msg_parse_flags: Unterminated MODSEQ response: %s\n", s));
1841 return -1;
1842 }
1843 }
1844 else if (*s == ')')
1845 s++; /* end of request */
1846 else if (*s)
1847 {
1848 /* got something i don't understand */
1849 imap_error ("msg_parse_fetch", s);
1850 return -1;
1851 }
1852 }
1853
1854 return 0;
1855}
1856
1857/* msg_parse_flags: read a FLAGS token into an IMAP_HEADER */
1858static char* msg_parse_flags (IMAP_HEADER* h, char* s)
1859{
1860 IMAP_HEADER_DATA* hd = h->data;
1861
1862 /* sanity-check string */
1863 if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
1864 {
1865 dprint (1, (debugfile, "msg_parse_flags: not a FLAGS response: %s\n",
1866 s));
1867 return NULL;
1868 }
1869 s += 5;
1870 SKIPWS(s);
1871 if (*s != '(')
1872 {
1873 dprint (1, (debugfile, "msg_parse_flags: bogus FLAGS response: %s\n",
1874 s));
1875 return NULL;
1876 }
1877 s++;
1878
1879 mutt_free_list (&hd->keywords);
1880 hd->deleted = hd->flagged = hd->replied = hd->read = hd->old = 0;
1881
1882 /* start parsing */
1883 while (*s && *s != ')')
1884 {
1885 if (ascii_strncasecmp ("\\deleted", s, 8) == 0)
1886 {
1887 s += 8;
1888 hd->deleted = 1;
1889 }
1890 else if (ascii_strncasecmp ("\\flagged", s, 8) == 0)
1891 {
1892 s += 8;
1893 hd->flagged = 1;
1894 }
1895 else if (ascii_strncasecmp ("\\answered", s, 9) == 0)
1896 {
1897 s += 9;
1898 hd->replied = 1;
1899 }
1900 else if (ascii_strncasecmp ("\\seen", s, 5) == 0)
1901 {
1902 s += 5;
1903 hd->read = 1;
1904 }
1905 else if (ascii_strncasecmp ("\\recent", s, 7) == 0)
1906 s += 7;
1907 else if (ascii_strncasecmp ("old", s, 3) == 0)
1908 {
1909 s += 3;
1910 hd->old = 1;
1911 }
1912 else
1913 {
1914 /* store custom flags as well */
1915 char ctmp;
1916 char* flag_word = s;
1917
1918 if (!hd->keywords)
1919 hd->keywords = mutt_new_list ();
1920
1921 while (*s && !ISSPACE (*s) && *s != ')')
1922 s++;
1923 ctmp = *s;
1924 *s = '\0';
1925 mutt_add_list (hd->keywords, flag_word);
1926 *s = ctmp;
1927 }
1928 SKIPWS(s);
1929 }
1930
1931 /* wrap up, or note bad flags response */
1932 if (*s == ')')
1933 s++;
1934 else
1935 {
1936 dprint (1, (debugfile,
1937 "msg_parse_flags: Unterminated FLAGS response: %s\n", s));
1938 return NULL;
1939 }
1940
1941 return s;
1942}
1943
1944static int flush_buffer (char *buf, size_t *len, CONNECTION *conn)
1945{
1946 int rc;
1947
1948 buf[*len] = '\0';
1949 rc = mutt_socket_write_n(conn, buf, *len);
1950 *len = 0;
1951
1952 return rc;
1953}