mutt stable branch with some hacks
at jcs 1953 lines 54 kB view raw
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}