mutt stable branch with some hacks
at master 1341 lines 35 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 31#include "mutt.h" 32#include "imap_private.h" 33#include "mx.h" 34 35#ifdef HAVE_PGP 36#include "pgp.h" 37#endif 38 39#if USE_HCACHE 40#include "hcache.h" 41#endif 42 43#include "bcache.h" 44 45static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h); 46static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h); 47static int msg_cache_commit (IMAP_DATA* idata, HEADER* h); 48 49static void flush_buffer(char* buf, size_t* len, CONNECTION* conn); 50static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, 51 FILE* fp); 52static int msg_parse_fetch (IMAP_HEADER* h, char* s); 53static char* msg_parse_flags (IMAP_HEADER* h, char* s); 54 55static void imap_update_context (IMAP_DATA *idata, int oldmsgcount) 56{ 57 CONTEXT *ctx; 58 HEADER *h; 59 int msgno; 60 61 ctx = idata->ctx; 62 if (!idata->uid_hash) 63 idata->uid_hash = int_hash_create (MAX (6 * ctx->msgcount / 5, 30)); 64 65 for (msgno = oldmsgcount; msgno < ctx->msgcount; msgno++) 66 { 67 h = ctx->hdrs[msgno]; 68 int_hash_insert (idata->uid_hash, HEADER_DATA(h)->uid, h, 0); 69 } 70} 71 72/* imap_read_headers: 73 * Changed to read many headers instead of just one. It will return the 74 * msgno of the last message read. It will return a value other than 75 * msgend if mail comes in while downloading headers (in theory). 76 */ 77int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) 78{ 79 CONTEXT* ctx; 80 char *hdrreq = NULL; 81 FILE *fp; 82 char tempfile[_POSIX_PATH_MAX]; 83 int msgno, idx = msgbegin - 1; 84 IMAP_HEADER h; 85 IMAP_STATUS* status; 86 int rc, mfhrc, oldmsgcount; 87 int fetchlast = 0; 88 int maxuid = 0; 89 static const char * const want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL"; 90 progress_t progress; 91 int retval = -1; 92 93#if USE_HCACHE 94 char buf[LONG_STRING]; 95 unsigned int *uid_validity = NULL; 96 unsigned int *puidnext = NULL; 97 unsigned int uidnext = 0; 98 int evalhc = 0; 99#endif /* USE_HCACHE */ 100 101 ctx = idata->ctx; 102 103 if (mutt_bit_isset (idata->capabilities,IMAP4REV1)) 104 { 105 safe_asprintf (&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", 106 want_headers, ImapHeaders ? " " : "", NONULL (ImapHeaders)); 107 } 108 else if (mutt_bit_isset (idata->capabilities,IMAP4)) 109 { 110 safe_asprintf (&hdrreq, "RFC822.HEADER.LINES (%s%s%s)", 111 want_headers, ImapHeaders ? " " : "", NONULL (ImapHeaders)); 112 } 113 else 114 { /* Unable to fetch headers for lower versions */ 115 mutt_error _("Unable to fetch headers from this IMAP server version."); 116 mutt_sleep (2); /* pause a moment to let the user see the error */ 117 goto error_out_0; 118 } 119 120 /* instead of downloading all headers and then parsing them, we parse them 121 * as they come in. */ 122 mutt_mktemp (tempfile, sizeof (tempfile)); 123 if (!(fp = safe_fopen (tempfile, "w+"))) 124 { 125 mutt_error (_("Could not create temporary file %s"), tempfile); 126 mutt_sleep (2); 127 goto error_out_0; 128 } 129 unlink (tempfile); 130 131 /* make sure context has room to hold the mailbox */ 132 while ((msgend) >= idata->ctx->hdrmax) 133 mx_alloc_memory (idata->ctx); 134 135 oldmsgcount = ctx->msgcount; 136 idata->reopen &= ~(IMAP_REOPEN_ALLOW|IMAP_NEWMAIL_PENDING); 137 idata->newMailCount = 0; 138 139#if USE_HCACHE 140 idata->hcache = imap_hcache_open (idata, NULL); 141 142 if (idata->hcache && !msgbegin) 143 { 144 uid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", imap_hcache_keylen); 145 puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen); 146 if (puidnext) 147 { 148 uidnext = *puidnext; 149 FREE (&puidnext); 150 } 151 if (uid_validity && uidnext && *uid_validity == idata->uid_validity) 152 evalhc = 1; 153 FREE (&uid_validity); 154 } 155 if (evalhc) 156 { 157 /* L10N: 158 Comparing the cached data with the IMAP server's data */ 159 mutt_progress_init (&progress, _("Evaluating cache..."), 160 MUTT_PROGRESS_MSG, ReadInc, msgend + 1); 161 162 snprintf (buf, sizeof (buf), 163 "UID FETCH 1:%u (UID FLAGS)", uidnext - 1); 164 165 imap_cmd_start (idata, buf); 166 167 rc = IMAP_CMD_CONTINUE; 168 for (msgno = msgbegin; rc == IMAP_CMD_CONTINUE; msgno++) 169 { 170 mutt_progress_update (&progress, msgno + 1, -1); 171 172 memset (&h, 0, sizeof (h)); 173 h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); 174 do 175 { 176 mfhrc = 0; 177 178 rc = imap_cmd_step (idata); 179 if (rc != IMAP_CMD_CONTINUE) 180 { 181 imap_free_header_data (&h.data); 182 break; 183 } 184 185 /* hole in the header cache */ 186 if (!evalhc) 187 continue; 188 189 if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, NULL)) == -1) 190 continue; 191 else if (mfhrc < 0) 192 { 193 imap_free_header_data (&h.data); 194 break; 195 } 196 197 if (!h.data->uid) 198 { 199 dprint (2, (debugfile, "imap_read_headers: skipping hcache FETCH " 200 "response for unknown message number %d\n", h.sid)); 201 mfhrc = -1; 202 continue; 203 } 204 205 idx++; 206 ctx->hdrs[idx] = imap_hcache_get (idata, h.data->uid); 207 if (ctx->hdrs[idx]) 208 { 209 ctx->hdrs[idx]->index = idx; 210 /* messages which have not been expunged are ACTIVE (borrowed from mh 211 * folders) */ 212 ctx->hdrs[idx]->active = 1; 213 ctx->hdrs[idx]->read = h.data->read; 214 ctx->hdrs[idx]->old = h.data->old; 215 ctx->hdrs[idx]->deleted = h.data->deleted; 216 ctx->hdrs[idx]->flagged = h.data->flagged; 217 ctx->hdrs[idx]->replied = h.data->replied; 218 ctx->hdrs[idx]->changed = h.data->changed; 219 /* ctx->hdrs[msgno]->received is restored from mutt_hcache_restore */ 220 ctx->hdrs[idx]->data = (void *) (h.data); 221 222 ctx->msgcount++; 223 ctx->size += ctx->hdrs[idx]->content->length; 224 } 225 else 226 { 227 /* bad header in the cache, we'll have to refetch. */ 228 dprint (3, (debugfile, "bad cache entry at %d, giving up\n", h.sid - 1)); 229 imap_free_header_data(&h.data); 230 evalhc = 0; 231 idx--; 232 } 233 } 234 while (rc != IMAP_CMD_OK && mfhrc == -1); 235 if (rc == IMAP_CMD_OK) 236 break; 237 if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) 238 { 239 imap_free_header_data (&h.data); 240 imap_hcache_close (idata); 241 goto error_out_1; 242 } 243 } 244 /* could also look for first null header in case hcache is holey */ 245 msgbegin = ctx->msgcount; 246 } 247#endif /* USE_HCACHE */ 248 249 mutt_progress_init (&progress, _("Fetching message headers..."), 250 MUTT_PROGRESS_MSG, ReadInc, msgend + 1); 251 252 for (msgno = msgbegin; msgno <= msgend ; msgno++) 253 { 254 mutt_progress_update (&progress, msgno + 1, -1); 255 256 /* we may get notification of new mail while fetching headers */ 257 if (msgno + 1 > fetchlast) 258 { 259 char *cmd; 260 261 fetchlast = msgend + 1; 262 safe_asprintf (&cmd, "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", 263 msgno + 1, fetchlast, hdrreq); 264 imap_cmd_start (idata, cmd); 265 FREE (&cmd); 266 } 267 268 rewind (fp); 269 memset (&h, 0, sizeof (h)); 270 h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); 271 272 /* this DO loop does two things: 273 * 1. handles untagged messages, so we can try again on the same msg 274 * 2. fetches the tagged response at the end of the last message. 275 */ 276 do 277 { 278 mfhrc = 0; 279 280 rc = imap_cmd_step (idata); 281 if (rc != IMAP_CMD_CONTINUE) 282 break; 283 284 if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, fp)) == -1) 285 continue; 286 else if (mfhrc < 0) 287 break; 288 289 if (!ftello (fp)) 290 { 291 dprint (2, (debugfile, "msg_fetch_header: ignoring fetch response with no body\n")); 292 mfhrc = -1; 293 msgend--; 294 continue; 295 } 296 297 /* make sure we don't get remnants from older larger message headers */ 298 fputs ("\n\n", fp); 299 300 idx++; 301 if (idx > msgend) 302 { 303 dprint (1, (debugfile, "imap_read_headers: skipping FETCH response for " 304 "unknown message number %d\n", h.sid)); 305 mfhrc = -1; 306 idx--; 307 continue; 308 } 309 /* May receive FLAGS updates in a separate untagged response (#2935) */ 310 if (idx < ctx->msgcount) 311 { 312 dprint (2, (debugfile, "imap_read_headers: message %d is not new\n", 313 h.sid)); 314 idx--; 315 continue; 316 } 317 318 ctx->hdrs[idx] = mutt_new_header (); 319 320 ctx->hdrs[idx]->index = h.sid - 1; 321 /* messages which have not been expunged are ACTIVE (borrowed from mh 322 * folders) */ 323 ctx->hdrs[idx]->active = 1; 324 ctx->hdrs[idx]->read = h.data->read; 325 ctx->hdrs[idx]->old = h.data->old; 326 ctx->hdrs[idx]->deleted = h.data->deleted; 327 ctx->hdrs[idx]->flagged = h.data->flagged; 328 ctx->hdrs[idx]->replied = h.data->replied; 329 ctx->hdrs[idx]->changed = h.data->changed; 330 ctx->hdrs[idx]->received = h.received; 331 ctx->hdrs[idx]->data = (void *) (h.data); 332 333 if (maxuid < h.data->uid) 334 maxuid = h.data->uid; 335 336 rewind (fp); 337 /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends 338 * on h.received being set */ 339 ctx->hdrs[idx]->env = mutt_read_rfc822_header (fp, ctx->hdrs[idx], 340 0, 0); 341 /* content built as a side-effect of mutt_read_rfc822_header */ 342 ctx->hdrs[idx]->content->length = h.content_length; 343 ctx->size += h.content_length; 344 345#if USE_HCACHE 346 imap_hcache_put (idata, ctx->hdrs[idx]); 347#endif /* USE_HCACHE */ 348 349 ctx->msgcount++; 350 } 351 while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) || 352 ((msgno + 1) >= fetchlast))); 353 354 if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) 355 { 356 imap_free_header_data (&h.data); 357#if USE_HCACHE 358 imap_hcache_close (idata); 359#endif 360 goto error_out_1; 361 } 362 363 /* in case we get new mail while fetching the headers */ 364 if (idata->reopen & IMAP_NEWMAIL_PENDING) 365 { 366 msgend = idata->newMailCount - 1; 367 while ((msgend) >= ctx->hdrmax) 368 mx_alloc_memory (ctx); 369 idata->reopen &= ~IMAP_NEWMAIL_PENDING; 370 idata->newMailCount = 0; 371 } 372 } 373 374 if (maxuid && (status = imap_mboxcache_get (idata, idata->mailbox, 0)) && 375 (status->uidnext < maxuid + 1)) 376 status->uidnext = maxuid + 1; 377 378#if USE_HCACHE 379 mutt_hcache_store_raw (idata->hcache, "/UIDVALIDITY", &idata->uid_validity, 380 sizeof (idata->uid_validity), imap_hcache_keylen); 381 if (maxuid && idata->uidnext < maxuid + 1) 382 { 383 dprint (2, (debugfile, "Overriding UIDNEXT: %u -> %u\n", idata->uidnext, maxuid + 1)); 384 idata->uidnext = maxuid + 1; 385 } 386 if (idata->uidnext > 1) 387 mutt_hcache_store_raw (idata->hcache, "/UIDNEXT", &idata->uidnext, 388 sizeof (idata->uidnext), imap_hcache_keylen); 389 390 imap_hcache_close (idata); 391#endif /* USE_HCACHE */ 392 393 if (ctx->msgcount > oldmsgcount) 394 { 395 mx_alloc_memory(ctx); 396 mx_update_context (ctx, ctx->msgcount - oldmsgcount); 397 imap_update_context (idata, oldmsgcount); 398 } 399 400 idata->reopen |= IMAP_REOPEN_ALLOW; 401 402 retval = msgend; 403 404error_out_1: 405 safe_fclose (&fp); 406 407error_out_0: 408 FREE (&hdrreq); 409 410 return retval; 411} 412 413int imap_fetch_message (CONTEXT *ctx, MESSAGE *msg, int msgno) 414{ 415 IMAP_DATA* idata; 416 HEADER* h; 417 ENVELOPE* newenv; 418 char buf[LONG_STRING]; 419 char path[_POSIX_PATH_MAX]; 420 char *pc; 421 long bytes; 422 progress_t progressbar; 423 int uid; 424 int cacheno; 425 IMAP_CACHE *cache; 426 int read; 427 int rc; 428 /* Sam's weird courier server returns an OK response even when FETCH 429 * fails. Thanks Sam. */ 430 short fetched = 0; 431 432 idata = (IMAP_DATA*) ctx->data; 433 h = ctx->hdrs[msgno]; 434 435 if ((msg->fp = msg_cache_get (idata, h))) 436 { 437 if (HEADER_DATA(h)->parsed) 438 return 0; 439 else 440 goto parsemsg; 441 } 442 443 /* we still do some caching even if imap_cachedir is unset */ 444 /* see if we already have the message in our cache */ 445 cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN; 446 cache = &idata->cache[cacheno]; 447 448 if (cache->path) 449 { 450 /* don't treat cache errors as fatal, just fall back. */ 451 if (cache->uid == HEADER_DATA(h)->uid && 452 (msg->fp = fopen (cache->path, "r"))) 453 return 0; 454 else 455 { 456 unlink (cache->path); 457 FREE (&cache->path); 458 } 459 } 460 461 if (!isendwin()) 462 mutt_message _("Fetching message..."); 463 464 if (!(msg->fp = msg_cache_put (idata, h))) 465 { 466 cache->uid = HEADER_DATA(h)->uid; 467 mutt_mktemp (path, sizeof (path)); 468 cache->path = safe_strdup (path); 469 if (!(msg->fp = safe_fopen (path, "w+"))) 470 { 471 FREE (&cache->path); 472 return -1; 473 } 474 } 475 476 /* mark this header as currently inactive so the command handler won't 477 * also try to update it. HACK until all this code can be moved into the 478 * command handler */ 479 h->active = 0; 480 481 snprintf (buf, sizeof (buf), "UID FETCH %u %s", HEADER_DATA(h)->uid, 482 (mutt_bit_isset (idata->capabilities, IMAP4REV1) ? 483 (option (OPTIMAPPEEK) ? "BODY.PEEK[]" : "BODY[]") : 484 "RFC822")); 485 486 imap_cmd_start (idata, buf); 487 do 488 { 489 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) 490 break; 491 492 pc = idata->buf; 493 pc = imap_next_word (pc); 494 pc = imap_next_word (pc); 495 496 if (!ascii_strncasecmp ("FETCH", pc, 5)) 497 { 498 while (*pc) 499 { 500 pc = imap_next_word (pc); 501 if (pc[0] == '(') 502 pc++; 503 if (ascii_strncasecmp ("UID", pc, 3) == 0) 504 { 505 pc = imap_next_word (pc); 506 uid = atoi (pc); 507 if (uid != HEADER_DATA(h)->uid) 508 mutt_error (_("The message index is incorrect. Try reopening the mailbox.")); 509 } 510 else if ((ascii_strncasecmp ("RFC822", pc, 6) == 0) || 511 (ascii_strncasecmp ("BODY[]", pc, 6) == 0)) 512 { 513 pc = imap_next_word (pc); 514 if (imap_get_literal_count(pc, &bytes) < 0) 515 { 516 imap_error ("imap_fetch_message()", buf); 517 goto bail; 518 } 519 mutt_progress_init (&progressbar, _("Fetching message..."), 520 MUTT_PROGRESS_SIZE, NetInc, bytes); 521 if (imap_read_literal (msg->fp, idata, bytes, &progressbar) < 0) 522 goto bail; 523 /* pick up trailing line */ 524 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) 525 goto bail; 526 pc = idata->buf; 527 528 fetched = 1; 529 } 530 /* UW-IMAP will provide a FLAGS update here if the FETCH causes a 531 * change (eg from \Unseen to \Seen). 532 * Uncommitted changes in mutt take precedence. If we decide to 533 * incrementally update flags later, this won't stop us syncing */ 534 else if ((ascii_strncasecmp ("FLAGS", pc, 5) == 0) && !h->changed) 535 { 536 if ((pc = imap_set_flags (idata, h, pc)) == NULL) 537 goto bail; 538 } 539 } 540 } 541 } 542 while (rc == IMAP_CMD_CONTINUE); 543 544 /* see comment before command start. */ 545 h->active = 1; 546 547 fflush (msg->fp); 548 if (ferror (msg->fp)) 549 { 550 mutt_perror (cache->path); 551 goto bail; 552 } 553 554 if (rc != IMAP_CMD_OK) 555 goto bail; 556 557 if (!fetched || !imap_code (idata->buf)) 558 goto bail; 559 560 msg_cache_commit (idata, h); 561 562 parsemsg: 563 /* Update the header information. Previously, we only downloaded a 564 * portion of the headers, those required for the main display. 565 */ 566 rewind (msg->fp); 567 /* It may be that the Status header indicates a message is read, but the 568 * IMAP server doesn't know the message has been \Seen. So we capture 569 * the server's notion of 'read' and if it differs from the message info 570 * picked up in mutt_read_rfc822_header, we mark the message (and context 571 * changed). Another possibility: ignore Status on IMAP?*/ 572 read = h->read; 573 newenv = mutt_read_rfc822_header (msg->fp, h, 0, 0); 574 mutt_merge_envelopes(h->env, &newenv); 575 576 /* see above. We want the new status in h->read, so we unset it manually 577 * and let mutt_set_flag set it correctly, updating context. */ 578 if (read != h->read) 579 { 580 h->read = read; 581 mutt_set_flag (ctx, h, MUTT_NEW, read); 582 } 583 584 h->lines = 0; 585 fgets (buf, sizeof (buf), msg->fp); 586 while (!feof (msg->fp)) 587 { 588 h->lines++; 589 fgets (buf, sizeof (buf), msg->fp); 590 } 591 592 h->content->length = ftell (msg->fp) - h->content->offset; 593 594 /* This needs to be done in case this is a multipart message */ 595#if defined(HAVE_PGP) || defined(HAVE_SMIME) 596 h->security = crypt_query (h->content); 597#endif 598 599 mutt_clear_error(); 600 rewind (msg->fp); 601 HEADER_DATA(h)->parsed = 1; 602 603 return 0; 604 605bail: 606 safe_fclose (&msg->fp); 607 imap_cache_del (idata, h); 608 if (cache->path) 609 { 610 unlink (cache->path); 611 FREE (&cache->path); 612 } 613 614 return -1; 615} 616 617int imap_close_message (CONTEXT *ctx, MESSAGE *msg) 618{ 619 return safe_fclose (&msg->fp); 620} 621 622int imap_commit_message (CONTEXT *ctx, MESSAGE *msg) 623{ 624 int r = safe_fclose (&msg->fp); 625 626 if (r) 627 return r; 628 629 return imap_append_message (ctx, msg); 630} 631 632int imap_append_message (CONTEXT *ctx, MESSAGE *msg) 633{ 634 IMAP_DATA* idata; 635 FILE *fp; 636 char buf[LONG_STRING]; 637 char mbox[LONG_STRING]; 638 char mailbox[LONG_STRING]; 639 char internaldate[IMAP_DATELEN]; 640 char imap_flags[SHORT_STRING]; 641 size_t len; 642 progress_t progressbar; 643 size_t sent; 644 int c, last; 645 IMAP_MBOX mx; 646 int rc; 647 648 idata = (IMAP_DATA*) ctx->data; 649 650 if (imap_parse_path (ctx->path, &mx)) 651 return -1; 652 653 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); 654 if (!*mailbox) 655 strfcpy (mailbox, "INBOX", sizeof (mailbox)); 656 657 if ((fp = fopen (msg->path, "r")) == NULL) 658 { 659 mutt_perror (msg->path); 660 goto fail; 661 } 662 663 /* currently we set the \Seen flag on all messages, but probably we 664 * should scan the message Status header for flag info. Since we're 665 * already rereading the whole file for length it isn't any more 666 * expensive (it'd be nice if we had the file size passed in already 667 * by the code that writes the file, but that's a lot of changes. 668 * Ideally we'd have a HEADER structure with flag info here... */ 669 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c) 670 { 671 if(c == '\n' && last != '\r') 672 len++; 673 674 len++; 675 } 676 rewind (fp); 677 678 mutt_progress_init (&progressbar, _("Uploading message..."), 679 MUTT_PROGRESS_SIZE, NetInc, len); 680 681 imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); 682 imap_make_date (internaldate, msg->received); 683 684 imap_flags[0] = imap_flags[1] = 0; 685 if (msg->flags.read) 686 safe_strcat (imap_flags, sizeof (imap_flags), " \\Seen"); 687 if (msg->flags.replied) 688 safe_strcat (imap_flags, sizeof (imap_flags), " \\Answered"); 689 if (msg->flags.flagged) 690 safe_strcat (imap_flags, sizeof (imap_flags), " \\Flagged"); 691 if (msg->flags.draft) 692 safe_strcat (imap_flags, sizeof (imap_flags), " \\Draft"); 693 694 snprintf (buf, sizeof (buf), "APPEND %s (%s) \"%s\" {%lu}", mbox, 695 imap_flags + 1, 696 internaldate, 697 (unsigned long) len); 698 699 imap_cmd_start (idata, buf); 700 701 do 702 rc = imap_cmd_step (idata); 703 while (rc == IMAP_CMD_CONTINUE); 704 705 if (rc != IMAP_CMD_RESPOND) 706 { 707 char *pc; 708 709 dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", 710 idata->buf)); 711 712 pc = idata->buf + SEQLEN; 713 SKIPWS (pc); 714 pc = imap_next_word (pc); 715 mutt_error ("%s", pc); 716 mutt_sleep (1); 717 safe_fclose (&fp); 718 goto fail; 719 } 720 721 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c) 722 { 723 if (c == '\n' && last != '\r') 724 buf[len++] = '\r'; 725 726 buf[len++] = c; 727 728 if (len > sizeof(buf) - 3) 729 { 730 sent += len; 731 flush_buffer(buf, &len, idata->conn); 732 mutt_progress_update (&progressbar, sent, -1); 733 } 734 } 735 736 if (len) 737 flush_buffer(buf, &len, idata->conn); 738 739 mutt_socket_write (idata->conn, "\r\n"); 740 safe_fclose (&fp); 741 742 do 743 rc = imap_cmd_step (idata); 744 while (rc == IMAP_CMD_CONTINUE); 745 746 if (!imap_code (idata->buf)) 747 { 748 char *pc; 749 750 dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", 751 idata->buf)); 752 pc = idata->buf + SEQLEN; 753 SKIPWS (pc); 754 pc = imap_next_word (pc); 755 mutt_error ("%s", pc); 756 mutt_sleep (1); 757 goto fail; 758 } 759 760 FREE (&mx.mbox); 761 return 0; 762 763 fail: 764 FREE (&mx.mbox); 765 return -1; 766} 767 768/* imap_copy_messages: use server COPY command to copy messages to another 769 * folder. 770 * Return codes: 771 * -1: error 772 * 0: success 773 * 1: non-fatal error - try fetch/append */ 774int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) 775{ 776 IMAP_DATA* idata; 777 BUFFER cmd, sync_cmd; 778 char mbox[LONG_STRING]; 779 char mmbox[LONG_STRING]; 780 char prompt[LONG_STRING]; 781 int rc; 782 int n; 783 IMAP_MBOX mx; 784 int err_continue = MUTT_NO; 785 int triedcreate = 0; 786 787 idata = (IMAP_DATA*) ctx->data; 788 789 if (imap_parse_path (dest, &mx)) 790 { 791 dprint (1, (debugfile, "imap_copy_messages: bad destination %s\n", dest)); 792 return -1; 793 } 794 795 /* check that the save-to folder is in the same account */ 796 if (!mutt_account_match (&(CTX_DATA->conn->account), &(mx.account))) 797 { 798 dprint (3, (debugfile, "imap_copy_messages: %s not same server as %s\n", 799 dest, ctx->path)); 800 return 1; 801 } 802 803 if (h && h->attach_del) 804 { 805 dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); 806 return 1; 807 } 808 809 imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); 810 if (!*mbox) 811 strfcpy (mbox, "INBOX", sizeof (mbox)); 812 imap_munge_mbox_name (idata, mmbox, sizeof (mmbox), mbox); 813 814 /* loop in case of TRYCREATE */ 815 do 816 { 817 mutt_buffer_init (&sync_cmd); 818 mutt_buffer_init (&cmd); 819 820 /* Null HEADER* means copy tagged messages */ 821 if (!h) 822 { 823 /* if any messages have attachments to delete, fall through to FETCH 824 * and APPEND. TODO: Copy what we can with COPY, fall through for the 825 * remainder. */ 826 for (n = 0; n < ctx->msgcount; n++) 827 { 828 if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del) 829 { 830 dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); 831 return 1; 832 } 833 834 if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active && 835 ctx->hdrs[n]->changed) 836 { 837 rc = imap_sync_message (idata, ctx->hdrs[n], &sync_cmd, &err_continue); 838 if (rc < 0) 839 { 840 dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); 841 goto out; 842 } 843 } 844 } 845 846 rc = imap_exec_msgset (idata, "UID COPY", mmbox, MUTT_TAG, 0, 0); 847 if (!rc) 848 { 849 dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n")); 850 rc = -1; 851 goto out; 852 } 853 else if (rc < 0) 854 { 855 dprint (1, (debugfile, "could not queue copy\n")); 856 goto out; 857 } 858 else 859 mutt_message (_("Copying %d messages to %s..."), rc, mbox); 860 } 861 else 862 { 863 mutt_message (_("Copying message %d to %s..."), h->index+1, mbox); 864 mutt_buffer_printf (&cmd, "UID COPY %u %s", HEADER_DATA (h)->uid, mmbox); 865 866 if (h->active && h->changed) 867 { 868 rc = imap_sync_message (idata, h, &sync_cmd, &err_continue); 869 if (rc < 0) 870 { 871 dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); 872 goto out; 873 } 874 } 875 if ((rc = imap_exec (idata, cmd.data, IMAP_CMD_QUEUE)) < 0) 876 { 877 dprint (1, (debugfile, "could not queue copy\n")); 878 goto out; 879 } 880 } 881 882 /* let's get it on */ 883 rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); 884 if (rc == -2) 885 { 886 if (triedcreate) 887 { 888 dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox)); 889 break; 890 } 891 /* bail out if command failed for reasons other than nonexistent target */ 892 if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) 893 break; 894 dprint (3, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n")); 895 snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox); 896 if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1) 897 { 898 mutt_clear_error (); 899 goto out; 900 } 901 if (imap_create_mailbox (idata, mbox) < 0) 902 break; 903 triedcreate = 1; 904 } 905 } 906 while (rc == -2); 907 908 if (rc != 0) 909 { 910 imap_error ("imap_copy_messages", idata->buf); 911 goto out; 912 } 913 914 /* cleanup */ 915 if (delete) 916 { 917 if (!h) 918 for (n = 0; n < ctx->msgcount; n++) 919 { 920 if (ctx->hdrs[n]->tagged) 921 { 922 mutt_set_flag (ctx, ctx->hdrs[n], MUTT_DELETE, 1); 923 mutt_set_flag (ctx, ctx->hdrs[n], MUTT_PURGE, 1); 924 if (option (OPTDELETEUNTAG)) 925 mutt_set_flag (ctx, ctx->hdrs[n], MUTT_TAG, 0); 926 } 927 } 928 else 929 { 930 mutt_set_flag (ctx, h, MUTT_DELETE, 1); 931 mutt_set_flag (ctx, h, MUTT_PURGE, 1); 932 if (option (OPTDELETEUNTAG)) 933 mutt_set_flag (ctx, h, MUTT_TAG, 0); 934 } 935 } 936 937 rc = 0; 938 939 out: 940 if (cmd.data) 941 FREE (&cmd.data); 942 if (sync_cmd.data) 943 FREE (&sync_cmd.data); 944 FREE (&mx.mbox); 945 946 return rc < 0 ? -1 : rc; 947} 948 949static body_cache_t *msg_cache_open (IMAP_DATA *idata) 950{ 951 char mailbox[_POSIX_PATH_MAX]; 952 953 if (idata->bcache) 954 return idata->bcache; 955 956 imap_cachepath (idata, idata->mailbox, mailbox, sizeof (mailbox)); 957 958 return mutt_bcache_open (&idata->conn->account, mailbox); 959} 960 961static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h) 962{ 963 char id[_POSIX_PATH_MAX]; 964 965 if (!idata || !h) 966 return NULL; 967 968 idata->bcache = msg_cache_open (idata); 969 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid); 970 return mutt_bcache_get (idata->bcache, id); 971} 972 973static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h) 974{ 975 char id[_POSIX_PATH_MAX]; 976 977 if (!idata || !h) 978 return NULL; 979 980 idata->bcache = msg_cache_open (idata); 981 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid); 982 return mutt_bcache_put (idata->bcache, id, 1); 983} 984 985static int msg_cache_commit (IMAP_DATA* idata, HEADER* h) 986{ 987 char id[_POSIX_PATH_MAX]; 988 989 if (!idata || !h) 990 return -1; 991 992 idata->bcache = msg_cache_open (idata); 993 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid); 994 995 return mutt_bcache_commit (idata->bcache, id); 996} 997 998int imap_cache_del (IMAP_DATA* idata, HEADER* h) 999{ 1000 char id[_POSIX_PATH_MAX]; 1001 1002 if (!idata || !h) 1003 return -1; 1004 1005 idata->bcache = msg_cache_open (idata); 1006 snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid); 1007 return mutt_bcache_del (idata->bcache, id); 1008} 1009 1010static int msg_cache_clean_cb (const char* id, body_cache_t* bcache, void* data) 1011{ 1012 unsigned int uv, uid, n; 1013 IMAP_DATA* idata = (IMAP_DATA*)data; 1014 1015 if (sscanf (id, "%u-%u", &uv, &uid) != 2) 1016 return 0; 1017 1018 /* bad UID */ 1019 if (uv != idata->uid_validity) 1020 mutt_bcache_del (bcache, id); 1021 1022 /* TODO: presort UIDs, walk in order */ 1023 for (n = 0; n < idata->ctx->msgcount; n++) 1024 { 1025 if (uid == HEADER_DATA(idata->ctx->hdrs[n])->uid) 1026 return 0; 1027 } 1028 mutt_bcache_del (bcache, id); 1029 1030 return 0; 1031} 1032 1033int imap_cache_clean (IMAP_DATA* idata) 1034{ 1035 idata->bcache = msg_cache_open (idata); 1036 mutt_bcache_list (idata->bcache, msg_cache_clean_cb, idata); 1037 1038 return 0; 1039} 1040 1041/* imap_add_keywords: concatenate custom IMAP tags to list, if they 1042 * appear in the folder flags list. Why wouldn't they? */ 1043void imap_add_keywords (char* s, HEADER* h, LIST* mailbox_flags, size_t slen) 1044{ 1045 LIST *keywords; 1046 1047 if (!mailbox_flags || !HEADER_DATA(h) || !HEADER_DATA(h)->keywords) 1048 return; 1049 1050 keywords = HEADER_DATA(h)->keywords->next; 1051 1052 while (keywords) 1053 { 1054 if (imap_has_flag (mailbox_flags, keywords->data)) 1055 { 1056 safe_strcat (s, slen, keywords->data); 1057 safe_strcat (s, slen, " "); 1058 } 1059 keywords = keywords->next; 1060 } 1061} 1062 1063/* imap_free_header_data: free IMAP_HEADER structure */ 1064void imap_free_header_data (IMAP_HEADER_DATA** data) 1065{ 1066 if (*data) 1067 { 1068 /* this should be safe even if the list wasn't used */ 1069 mutt_free_list (&((*data)->keywords)); 1070 FREE (data); /* __FREE_CHECKED__ */ 1071 } 1072} 1073 1074/* imap_set_flags: fill out the message header according to the flags from 1075 * the server. Expects a flags line of the form "FLAGS (flag flag ...)" */ 1076char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s) 1077{ 1078 CONTEXT* ctx = idata->ctx; 1079 IMAP_HEADER newh; 1080 IMAP_HEADER_DATA* hd; 1081 unsigned char readonly; 1082 1083 memset (&newh, 0, sizeof (newh)); 1084 hd = h->data; 1085 newh.data = hd; 1086 1087 dprint (2, (debugfile, "imap_fetch_message: parsing FLAGS\n")); 1088 if ((s = msg_parse_flags (&newh, s)) == NULL) 1089 return NULL; 1090 1091 /* YAUH (yet another ugly hack): temporarily set context to 1092 * read-write even if it's read-only, so *server* updates of 1093 * flags can be processed by mutt_set_flag. ctx->changed must 1094 * be restored afterwards */ 1095 readonly = ctx->readonly; 1096 ctx->readonly = 0; 1097 1098 mutt_set_flag (ctx, h, MUTT_NEW, !(hd->read || hd->old)); 1099 mutt_set_flag (ctx, h, MUTT_OLD, hd->old); 1100 mutt_set_flag (ctx, h, MUTT_READ, hd->read); 1101 mutt_set_flag (ctx, h, MUTT_DELETE, hd->deleted); 1102 mutt_set_flag (ctx, h, MUTT_FLAG, hd->flagged); 1103 mutt_set_flag (ctx, h, MUTT_REPLIED, hd->replied); 1104 1105 /* this message is now definitively *not* changed (mutt_set_flag 1106 * marks things changed as a side-effect) */ 1107 h->changed = 0; 1108 ctx->changed &= ~readonly; 1109 ctx->readonly = readonly; 1110 1111 return s; 1112} 1113 1114 1115/* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER. 1116 * Expects string beginning with * n FETCH. 1117 * Returns: 1118 * 0 on success 1119 * -1 if the string is not a fetch response 1120 * -2 if the string is a corrupt fetch response */ 1121static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp) 1122{ 1123 IMAP_DATA* idata; 1124 long bytes; 1125 int rc = -1; /* default now is that string isn't FETCH response*/ 1126 1127 idata = (IMAP_DATA*) ctx->data; 1128 1129 if (buf[0] != '*') 1130 return rc; 1131 1132 /* skip to message number */ 1133 buf = imap_next_word (buf); 1134 h->sid = atoi (buf); 1135 1136 /* find FETCH tag */ 1137 buf = imap_next_word (buf); 1138 if (ascii_strncasecmp ("FETCH", buf, 5)) 1139 return rc; 1140 1141 rc = -2; /* we've got a FETCH response, for better or worse */ 1142 if (!(buf = strchr (buf, '('))) 1143 return rc; 1144 buf++; 1145 1146 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2, 1147 * read header lines and call it again. Silly. */ 1148 if ((rc = msg_parse_fetch (h, buf)) != -2 || !fp) 1149 return rc; 1150 1151 if (imap_get_literal_count (buf, &bytes) == 0) 1152 { 1153 imap_read_literal (fp, idata, bytes, NULL); 1154 1155 /* we may have other fields of the FETCH _after_ the literal 1156 * (eg Domino puts FLAGS here). Nothing wrong with that, either. 1157 * This all has to go - we should accept literals and nonliterals 1158 * interchangeably at any time. */ 1159 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) 1160 return rc; 1161 1162 if (msg_parse_fetch (h, idata->buf) == -1) 1163 return rc; 1164 } 1165 1166 rc = 0; /* success */ 1167 1168 /* subtract headers from message size - unfortunately only the subset of 1169 * headers we've requested. */ 1170 h->content_length -= bytes; 1171 1172 return rc; 1173} 1174 1175/* msg_parse_fetch: handle headers returned from header fetch */ 1176static int msg_parse_fetch (IMAP_HEADER *h, char *s) 1177{ 1178 char tmp[SHORT_STRING]; 1179 char *ptmp; 1180 1181 if (!s) 1182 return -1; 1183 1184 while (*s) 1185 { 1186 SKIPWS (s); 1187 1188 if (ascii_strncasecmp ("FLAGS", s, 5) == 0) 1189 { 1190 if ((s = msg_parse_flags (h, s)) == NULL) 1191 return -1; 1192 } 1193 else if (ascii_strncasecmp ("UID", s, 3) == 0) 1194 { 1195 s += 3; 1196 SKIPWS (s); 1197 h->data->uid = (unsigned int) atoi (s); 1198 1199 s = imap_next_word (s); 1200 } 1201 else if (ascii_strncasecmp ("INTERNALDATE", s, 12) == 0) 1202 { 1203 s += 12; 1204 SKIPWS (s); 1205 if (*s != '\"') 1206 { 1207 dprint (1, (debugfile, "msg_parse_fetch(): bogus INTERNALDATE entry: %s\n", s)); 1208 return -1; 1209 } 1210 s++; 1211 ptmp = tmp; 1212 while (*s && *s != '\"') 1213 *ptmp++ = *s++; 1214 if (*s != '\"') 1215 return -1; 1216 s++; /* skip past the trailing " */ 1217 *ptmp = 0; 1218 h->received = imap_parse_date (tmp); 1219 } 1220 else if (ascii_strncasecmp ("RFC822.SIZE", s, 11) == 0) 1221 { 1222 s += 11; 1223 SKIPWS (s); 1224 ptmp = tmp; 1225 while (isdigit ((unsigned char) *s)) 1226 *ptmp++ = *s++; 1227 *ptmp = 0; 1228 h->content_length = atoi (tmp); 1229 } 1230 else if (!ascii_strncasecmp ("BODY", s, 4) || 1231 !ascii_strncasecmp ("RFC822.HEADER", s, 13)) 1232 { 1233 /* handle above, in msg_fetch_header */ 1234 return -2; 1235 } 1236 else if (*s == ')') 1237 s++; /* end of request */ 1238 else if (*s) 1239 { 1240 /* got something i don't understand */ 1241 imap_error ("msg_parse_fetch", s); 1242 return -1; 1243 } 1244 } 1245 1246 return 0; 1247} 1248 1249/* msg_parse_flags: read a FLAGS token into an IMAP_HEADER */ 1250static char* msg_parse_flags (IMAP_HEADER* h, char* s) 1251{ 1252 IMAP_HEADER_DATA* hd = h->data; 1253 1254 /* sanity-check string */ 1255 if (ascii_strncasecmp ("FLAGS", s, 5) != 0) 1256 { 1257 dprint (1, (debugfile, "msg_parse_flags: not a FLAGS response: %s\n", 1258 s)); 1259 return NULL; 1260 } 1261 s += 5; 1262 SKIPWS(s); 1263 if (*s != '(') 1264 { 1265 dprint (1, (debugfile, "msg_parse_flags: bogus FLAGS response: %s\n", 1266 s)); 1267 return NULL; 1268 } 1269 s++; 1270 1271 mutt_free_list (&hd->keywords); 1272 hd->deleted = hd->flagged = hd->replied = hd->read = hd->old = 0; 1273 1274 /* start parsing */ 1275 while (*s && *s != ')') 1276 { 1277 if (ascii_strncasecmp ("\\deleted", s, 8) == 0) 1278 { 1279 s += 8; 1280 hd->deleted = 1; 1281 } 1282 else if (ascii_strncasecmp ("\\flagged", s, 8) == 0) 1283 { 1284 s += 8; 1285 hd->flagged = 1; 1286 } 1287 else if (ascii_strncasecmp ("\\answered", s, 9) == 0) 1288 { 1289 s += 9; 1290 hd->replied = 1; 1291 } 1292 else if (ascii_strncasecmp ("\\seen", s, 5) == 0) 1293 { 1294 s += 5; 1295 hd->read = 1; 1296 } 1297 else if (ascii_strncasecmp ("\\recent", s, 7) == 0) 1298 s += 7; 1299 else if (ascii_strncasecmp ("old", s, 3) == 0) 1300 { 1301 s += 3; 1302 hd->old = 1; 1303 } 1304 else 1305 { 1306 /* store custom flags as well */ 1307 char ctmp; 1308 char* flag_word = s; 1309 1310 if (!hd->keywords) 1311 hd->keywords = mutt_new_list (); 1312 1313 while (*s && !ISSPACE (*s) && *s != ')') 1314 s++; 1315 ctmp = *s; 1316 *s = '\0'; 1317 mutt_add_list (hd->keywords, flag_word); 1318 *s = ctmp; 1319 } 1320 SKIPWS(s); 1321 } 1322 1323 /* wrap up, or note bad flags response */ 1324 if (*s == ')') 1325 s++; 1326 else 1327 { 1328 dprint (1, (debugfile, 1329 "msg_parse_flags: Unterminated FLAGS response: %s\n", s)); 1330 return NULL; 1331 } 1332 1333 return s; 1334} 1335 1336static void flush_buffer(char *buf, size_t *len, CONNECTION *conn) 1337{ 1338 buf[*len] = '\0'; 1339 mutt_socket_write_n(conn, buf, *len); 1340 *len = 0; 1341}