mutt stable branch with some hacks
at master 2195 lines 57 kB view raw
1/* 2 * Copyright (C) 1996-1998,2012 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net> 4 * Copyright (C) 1999-2009,2012 Brendan Cully <brendan@kublai.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 */ 20 21/* Support for IMAP4rev1, with the occasional nod to IMAP 4. */ 22 23#if HAVE_CONFIG_H 24# include "config.h" 25#endif 26 27#include "mutt.h" 28#include "mx.h" 29#include "mailbox.h" 30#include "globals.h" 31#include "sort.h" 32#include "browser.h" 33#include "imap_private.h" 34#if defined(USE_SSL) 35# include "mutt_ssl.h" 36#endif 37#include "buffy.h" 38#if USE_HCACHE 39#include "hcache.h" 40#endif 41 42#include <unistd.h> 43#include <ctype.h> 44#include <string.h> 45#include <stdlib.h> 46#include <sys/types.h> 47#include <sys/stat.h> 48 49/* imap forward declarations */ 50static char* imap_get_flags (LIST** hflags, char* s); 51static int imap_check_capabilities (IMAP_DATA* idata); 52static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag, 53 const char* str, char* flags, size_t flsize); 54 55/* imap_access: Check permissions on an IMAP mailbox. 56 * TODO: ACL checks. Right now we assume if it exists we can 57 * mess with it. */ 58int imap_access (const char* path, int flags) 59{ 60 IMAP_DATA* idata; 61 IMAP_MBOX mx; 62 char buf[LONG_STRING]; 63 char mailbox[LONG_STRING]; 64 char mbox[LONG_STRING]; 65 int rc; 66 67 if (imap_parse_path (path, &mx)) 68 return -1; 69 70 if (!(idata = imap_conn_find (&mx.account, 71 option (OPTIMAPPASSIVE) ? MUTT_IMAP_CONN_NONEW : 0))) 72 { 73 FREE (&mx.mbox); 74 return -1; 75 } 76 77 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); 78 if (!*mailbox) 79 strfcpy (mailbox, "INBOX", sizeof (mailbox)); 80 81 /* we may already be in the folder we're checking */ 82 if (!ascii_strcmp(idata->mailbox, mx.mbox)) 83 { 84 FREE (&mx.mbox); 85 return 0; 86 } 87 FREE (&mx.mbox); 88 89 if (imap_mboxcache_get (idata, mailbox, 0)) 90 { 91 dprint (3, (debugfile, "imap_access: found %s in cache\n", mailbox)); 92 return 0; 93 } 94 95 imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); 96 97 if (mutt_bit_isset (idata->capabilities, IMAP4REV1)) 98 snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox); 99 else if (mutt_bit_isset (idata->capabilities, STATUS)) 100 snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox); 101 else 102 { 103 dprint (2, (debugfile, "imap_access: STATUS not supported?\n")); 104 return -1; 105 } 106 107 if ((rc = imap_exec (idata, buf, IMAP_CMD_FAIL_OK)) < 0) 108 { 109 dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox)); 110 return rc; 111 } 112 113 return 0; 114} 115 116int imap_create_mailbox (IMAP_DATA* idata, char* mailbox) 117{ 118 char buf[LONG_STRING], mbox[LONG_STRING]; 119 120 imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); 121 snprintf (buf, sizeof (buf), "CREATE %s", mbox); 122 123 if (imap_exec (idata, buf, 0) != 0) 124 { 125 mutt_error (_("CREATE failed: %s"), imap_cmd_trailer (idata)); 126 return -1; 127 } 128 129 return 0; 130} 131 132int imap_rename_mailbox (IMAP_DATA* idata, IMAP_MBOX* mx, const char* newname) 133{ 134 char oldmbox[LONG_STRING]; 135 char newmbox[LONG_STRING]; 136 char buf[LONG_STRING]; 137 138 imap_munge_mbox_name (idata, oldmbox, sizeof (oldmbox), mx->mbox); 139 imap_munge_mbox_name (idata, newmbox, sizeof (newmbox), newname); 140 141 snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox); 142 143 if (imap_exec (idata, buf, 0) != 0) 144 return -1; 145 146 return 0; 147} 148 149int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx) 150{ 151 char buf[LONG_STRING], mbox[LONG_STRING]; 152 IMAP_DATA *idata; 153 154 if (!ctx || !ctx->data) { 155 if (!(idata = imap_conn_find (&mx.account, 156 option (OPTIMAPPASSIVE) ? MUTT_IMAP_CONN_NONEW : 0))) 157 { 158 FREE (&mx.mbox); 159 return -1; 160 } 161 } else { 162 idata = ctx->data; 163 } 164 165 imap_munge_mbox_name (idata, mbox, sizeof (mbox), mx.mbox); 166 snprintf (buf, sizeof (buf), "DELETE %s", mbox); 167 168 if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0) 169 return -1; 170 171 return 0; 172} 173 174/* imap_logout_all: close all open connections. Quick and dirty until we can 175 * make sure we've got all the context we need. */ 176void imap_logout_all (void) 177{ 178 CONNECTION* conn; 179 CONNECTION* tmp; 180 181 conn = mutt_socket_head (); 182 183 while (conn) 184 { 185 tmp = conn->next; 186 187 if (conn->account.type == MUTT_ACCT_TYPE_IMAP && conn->fd >= 0) 188 { 189 mutt_message (_("Closing connection to %s..."), conn->account.host); 190 imap_logout ((IMAP_DATA**) (void*) &conn->data); 191 mutt_clear_error (); 192 mutt_socket_free (conn); 193 } 194 195 conn = tmp; 196 } 197} 198 199/* imap_read_literal: read bytes bytes from server into file. Not explicitly 200 * buffered, relies on FILE buffering. NOTE: strips \r from \r\n. 201 * Apparently even literals use \r\n-terminated strings ?! */ 202int imap_read_literal (FILE* fp, IMAP_DATA* idata, long bytes, progress_t* pbar) 203{ 204 long pos; 205 char c; 206 207 int r = 0; 208 209 dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes)); 210 211 for (pos = 0; pos < bytes; pos++) 212 { 213 if (mutt_socket_readchar (idata->conn, &c) != 1) 214 { 215 dprint (1, (debugfile, "imap_read_literal: error during read, %ld bytes read\n", pos)); 216 idata->status = IMAP_FATAL; 217 218 return -1; 219 } 220 221#if 1 222 if (r == 1 && c != '\n') 223 fputc ('\r', fp); 224 225 if (c == '\r') 226 { 227 r = 1; 228 continue; 229 } 230 else 231 r = 0; 232#endif 233 fputc (c, fp); 234 235 if (pbar && !(pos % 1024)) 236 mutt_progress_update (pbar, pos, -1); 237#ifdef DEBUG 238 if (debuglevel >= IMAP_LOG_LTRL) 239 fputc (c, debugfile); 240#endif 241 } 242 243 return 0; 244} 245 246/* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the 247 * context. Must not be done while something has a handle on any headers 248 * (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */ 249void imap_expunge_mailbox (IMAP_DATA* idata) 250{ 251 HEADER* h; 252 int i, cacheno; 253 254#ifdef USE_HCACHE 255 idata->hcache = imap_hcache_open (idata, NULL); 256#endif 257 258 for (i = 0; i < idata->ctx->msgcount; i++) 259 { 260 h = idata->ctx->hdrs[i]; 261 262 if (h->index == -1) 263 { 264 dprint (2, (debugfile, "Expunging message UID %d.\n", HEADER_DATA (h)->uid)); 265 266 h->active = 0; 267 idata->ctx->size -= h->content->length; 268 269 imap_cache_del (idata, h); 270#if USE_HCACHE 271 imap_hcache_del (idata, HEADER_DATA(h)->uid); 272#endif 273 274 /* free cached body from disk, if necessary */ 275 cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN; 276 if (idata->cache[cacheno].uid == HEADER_DATA(h)->uid && 277 idata->cache[cacheno].path) 278 { 279 unlink (idata->cache[cacheno].path); 280 FREE (&idata->cache[cacheno].path); 281 } 282 283 int_hash_delete (idata->uid_hash, HEADER_DATA(h)->uid, h, NULL); 284 285 imap_free_header_data ((IMAP_HEADER_DATA**)&h->data); 286 } 287 } 288 289#if USE_HCACHE 290 imap_hcache_close (idata); 291#endif 292 293 /* We may be called on to expunge at any time. We can't rely on the caller 294 * to always know to rethread */ 295 mx_update_tables (idata->ctx, 0); 296 mutt_sort_headers (idata->ctx, 1); 297} 298 299/* imap_check_capabilities: make sure we can log in to this server. */ 300static int imap_check_capabilities (IMAP_DATA* idata) 301{ 302 if (imap_exec (idata, "CAPABILITY", 0) != 0) 303 { 304 imap_error ("imap_check_capabilities", idata->buf); 305 return -1; 306 } 307 308 if (!(mutt_bit_isset(idata->capabilities,IMAP4) 309 ||mutt_bit_isset(idata->capabilities,IMAP4REV1))) 310 { 311 mutt_error _("This IMAP server is ancient. Mutt does not work with it."); 312 mutt_sleep (2); /* pause a moment to let the user see the error */ 313 314 return -1; 315 } 316 317 return 0; 318} 319 320/* imap_conn_find: Find an open IMAP connection matching account, or open 321 * a new one if none can be found. */ 322IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags) 323{ 324 CONNECTION* conn = NULL; 325 ACCOUNT* creds = NULL; 326 IMAP_DATA* idata = NULL; 327 int new = 0; 328 329 while ((conn = mutt_conn_find (conn, account))) 330 { 331 if (!creds) 332 creds = &conn->account; 333 else 334 memcpy (&conn->account, creds, sizeof (ACCOUNT)); 335 336 idata = (IMAP_DATA*)conn->data; 337 if (flags & MUTT_IMAP_CONN_NONEW) 338 { 339 if (!idata) 340 { 341 /* This should only happen if we've come to the end of the list */ 342 mutt_socket_free (conn); 343 return NULL; 344 } 345 else if (idata->state < IMAP_AUTHENTICATED) 346 continue; 347 } 348 if (flags & MUTT_IMAP_CONN_NOSELECT && idata && idata->state >= IMAP_SELECTED) 349 continue; 350 if (idata && idata->status == IMAP_FATAL) 351 continue; 352 break; 353 } 354 if (!conn) 355 return NULL; /* this happens when the initial connection fails */ 356 357 if (!idata) 358 { 359 /* The current connection is a new connection */ 360 if (! (idata = imap_new_idata ())) 361 { 362 mutt_socket_free (conn); 363 return NULL; 364 } 365 366 conn->data = idata; 367 idata->conn = conn; 368 new = 1; 369 } 370 371 if (idata->state == IMAP_DISCONNECTED) 372 imap_open_connection (idata); 373 if (idata->state == IMAP_CONNECTED) 374 { 375 if (!imap_authenticate (idata)) 376 { 377 idata->state = IMAP_AUTHENTICATED; 378 FREE (&idata->capstr); 379 new = 1; 380 if (idata->conn->ssf) 381 dprint (2, (debugfile, "Communication encrypted at %d bits\n", 382 idata->conn->ssf)); 383 } 384 else 385 mutt_account_unsetpass (&idata->conn->account); 386 } 387 if (new && idata->state == IMAP_AUTHENTICATED) 388 { 389 /* capabilities may have changed */ 390 imap_exec (idata, "CAPABILITY", IMAP_CMD_QUEUE); 391 /* enable RFC6855, if the server supports that */ 392 if (mutt_bit_isset (idata->capabilities, ENABLE)) 393 imap_exec (idata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE); 394 /* get root delimiter, '/' as default */ 395 idata->delim = '/'; 396 imap_exec (idata, "LIST \"\" \"\"", IMAP_CMD_QUEUE); 397 if (option (OPTIMAPCHECKSUBSCRIBED)) 398 imap_exec (idata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE); 399 /* we may need the root delimiter before we open a mailbox */ 400 imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); 401 } 402 403 return idata; 404} 405 406int imap_open_connection (IMAP_DATA* idata) 407{ 408 char buf[LONG_STRING]; 409 410 if (mutt_socket_open (idata->conn) < 0) 411 return -1; 412 413 idata->state = IMAP_CONNECTED; 414 415 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) 416 { 417 imap_close_connection (idata); 418 return -1; 419 } 420 421 if (ascii_strncasecmp ("* OK", idata->buf, 4) == 0) 422 { 423 if (ascii_strncasecmp ("* OK [CAPABILITY", idata->buf, 16) 424 && imap_check_capabilities (idata)) 425 goto bail; 426#if defined(USE_SSL) 427 /* Attempt STARTTLS if available and desired. */ 428 if (!idata->conn->ssf && (option(OPTSSLFORCETLS) || 429 mutt_bit_isset (idata->capabilities, STARTTLS))) 430 { 431 int rc; 432 433 if (option(OPTSSLFORCETLS)) 434 rc = MUTT_YES; 435 else if ((rc = query_quadoption (OPT_SSLSTARTTLS, 436 _("Secure connection with TLS?"))) == -1) 437 goto err_close_conn; 438 if (rc == MUTT_YES) { 439 if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1) 440 goto bail; 441 if (rc != -2) 442 { 443 if (mutt_ssl_starttls (idata->conn)) 444 { 445 mutt_error (_("Could not negotiate TLS connection")); 446 mutt_sleep (1); 447 goto err_close_conn; 448 } 449 else 450 { 451 /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */ 452 if (imap_exec (idata, "CAPABILITY", 0)) 453 goto bail; 454 } 455 } 456 } 457 } 458 459 if (option(OPTSSLFORCETLS) && ! idata->conn->ssf) 460 { 461 mutt_error _("Encrypted connection unavailable"); 462 mutt_sleep (1); 463 goto err_close_conn; 464 } 465#endif 466 } 467 else if (ascii_strncasecmp ("* PREAUTH", idata->buf, 9) == 0) 468 { 469 idata->state = IMAP_AUTHENTICATED; 470 if (imap_check_capabilities (idata) != 0) 471 goto bail; 472 FREE (&idata->capstr); 473 } 474 else 475 { 476 imap_error ("imap_open_connection()", buf); 477 goto bail; 478 } 479 480 return 0; 481 482#if defined(USE_SSL) 483 err_close_conn: 484 imap_close_connection (idata); 485#endif 486 bail: 487 FREE (&idata->capstr); 488 return -1; 489} 490 491void imap_close_connection(IMAP_DATA* idata) 492{ 493 if (idata->state != IMAP_DISCONNECTED) 494 { 495 mutt_socket_close (idata->conn); 496 idata->state = IMAP_DISCONNECTED; 497 } 498 idata->seqno = idata->nextcmd = idata->lastcmd = idata->status = 0; 499 memset (idata->cmds, 0, sizeof (IMAP_COMMAND) * idata->cmdslots); 500} 501 502/* imap_get_flags: Make a simple list out of a FLAGS response. 503 * return stream following FLAGS response */ 504static char* imap_get_flags (LIST** hflags, char* s) 505{ 506 LIST* flags; 507 char* flag_word; 508 char ctmp; 509 510 /* sanity-check string */ 511 if (ascii_strncasecmp ("FLAGS", s, 5) != 0) 512 { 513 dprint (1, (debugfile, "imap_get_flags: not a FLAGS response: %s\n", 514 s)); 515 return NULL; 516 } 517 s += 5; 518 SKIPWS(s); 519 if (*s != '(') 520 { 521 dprint (1, (debugfile, "imap_get_flags: bogus FLAGS response: %s\n", 522 s)); 523 return NULL; 524 } 525 526 /* create list, update caller's flags handle */ 527 flags = mutt_new_list(); 528 *hflags = flags; 529 530 while (*s && *s != ')') 531 { 532 s++; 533 SKIPWS(s); 534 flag_word = s; 535 while (*s && (*s != ')') && !ISSPACE (*s)) 536 s++; 537 ctmp = *s; 538 *s = '\0'; 539 if (*flag_word) 540 mutt_add_list (flags, flag_word); 541 *s = ctmp; 542 } 543 544 /* note bad flags response */ 545 if (*s != ')') 546 { 547 dprint (1, (debugfile, 548 "imap_get_flags: Unterminated FLAGS response: %s\n", s)); 549 mutt_free_list (hflags); 550 551 return NULL; 552 } 553 554 s++; 555 556 return s; 557} 558 559static int imap_open_mailbox (CONTEXT* ctx) 560{ 561 IMAP_DATA *idata; 562 IMAP_STATUS* status; 563 char buf[LONG_STRING]; 564 char bufout[LONG_STRING]; 565 int count = 0; 566 IMAP_MBOX mx, pmx; 567 int rc; 568 569 if (imap_parse_path (ctx->path, &mx)) 570 { 571 mutt_error (_("%s is an invalid IMAP path"), ctx->path); 572 return -1; 573 } 574 575 /* we require a connection which isn't currently in IMAP_SELECTED state */ 576 if (!(idata = imap_conn_find (&(mx.account), MUTT_IMAP_CONN_NOSELECT))) 577 goto fail_noidata; 578 if (idata->state < IMAP_AUTHENTICATED) 579 goto fail; 580 581 /* once again the context is new */ 582 ctx->data = idata; 583 584 /* Clean up path and replace the one in the ctx */ 585 imap_fix_path (idata, mx.mbox, buf, sizeof (buf)); 586 if (!*buf) 587 strfcpy (buf, "INBOX", sizeof (buf)); 588 FREE(&(idata->mailbox)); 589 idata->mailbox = safe_strdup (buf); 590 imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox); 591 592 FREE (&(ctx->path)); 593 FREE (&(ctx->realpath)); 594 ctx->path = safe_strdup (buf); 595 ctx->realpath = safe_strdup (ctx->path); 596 597 idata->ctx = ctx; 598 599 /* clear mailbox status */ 600 idata->status = 0; 601 memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights)); 602 idata->newMailCount = 0; 603 604 mutt_message (_("Selecting %s..."), idata->mailbox); 605 imap_munge_mbox_name (idata, buf, sizeof(buf), idata->mailbox); 606 607 /* pipeline ACL test */ 608 if (mutt_bit_isset (idata->capabilities, ACL)) 609 { 610 snprintf (bufout, sizeof (bufout), "MYRIGHTS %s", buf); 611 imap_exec (idata, bufout, IMAP_CMD_QUEUE); 612 } 613 /* assume we have all rights if ACL is unavailable */ 614 else 615 { 616 mutt_bit_set (idata->ctx->rights, MUTT_ACL_LOOKUP); 617 mutt_bit_set (idata->ctx->rights, MUTT_ACL_READ); 618 mutt_bit_set (idata->ctx->rights, MUTT_ACL_SEEN); 619 mutt_bit_set (idata->ctx->rights, MUTT_ACL_WRITE); 620 mutt_bit_set (idata->ctx->rights, MUTT_ACL_INSERT); 621 mutt_bit_set (idata->ctx->rights, MUTT_ACL_POST); 622 mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE); 623 mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE); 624 } 625 /* pipeline the postponed count if possible */ 626 pmx.mbox = NULL; 627 if (mx_is_imap (Postponed) && !imap_parse_path (Postponed, &pmx) 628 && mutt_account_match (&pmx.account, &mx.account)) 629 imap_status (Postponed, 1); 630 FREE (&pmx.mbox); 631 632 snprintf (bufout, sizeof (bufout), "%s %s", 633 ctx->readonly ? "EXAMINE" : "SELECT", buf); 634 635 idata->state = IMAP_SELECTED; 636 637 imap_cmd_start (idata, bufout); 638 639 status = imap_mboxcache_get (idata, idata->mailbox, 1); 640 641 do 642 { 643 char *pc; 644 645 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) 646 break; 647 648 pc = idata->buf + 2; 649 650 /* Obtain list of available flags here, may be overridden by a 651 * PERMANENTFLAGS tag in the OK response */ 652 if (ascii_strncasecmp ("FLAGS", pc, 5) == 0) 653 { 654 /* don't override PERMANENTFLAGS */ 655 if (!idata->flags) 656 { 657 dprint (3, (debugfile, "Getting mailbox FLAGS\n")); 658 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL) 659 goto fail; 660 } 661 } 662 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */ 663 else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0) 664 { 665 dprint (3, (debugfile, "Getting mailbox PERMANENTFLAGS\n")); 666 /* safe to call on NULL */ 667 mutt_free_list (&(idata->flags)); 668 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */ 669 pc += 13; 670 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL) 671 goto fail; 672 } 673 /* save UIDVALIDITY for the header cache */ 674 else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0) 675 { 676 dprint (3, (debugfile, "Getting mailbox UIDVALIDITY\n")); 677 pc += 3; 678 pc = imap_next_word (pc); 679 idata->uid_validity = strtol (pc, NULL, 10); 680 status->uidvalidity = idata->uid_validity; 681 } 682 else if (ascii_strncasecmp ("OK [UIDNEXT", pc, 11) == 0) 683 { 684 dprint (3, (debugfile, "Getting mailbox UIDNEXT\n")); 685 pc += 3; 686 pc = imap_next_word (pc); 687 idata->uidnext = strtol (pc, NULL, 10); 688 status->uidnext = idata->uidnext; 689 } 690 else 691 { 692 pc = imap_next_word (pc); 693 if (!ascii_strncasecmp ("EXISTS", pc, 6)) 694 { 695 count = idata->newMailCount; 696 idata->newMailCount = 0; 697 } 698 } 699 } 700 while (rc == IMAP_CMD_CONTINUE); 701 702 if (rc == IMAP_CMD_NO) 703 { 704 char *s; 705 s = imap_next_word (idata->buf); /* skip seq */ 706 s = imap_next_word (s); /* Skip response */ 707 mutt_error ("%s", s); 708 mutt_sleep (2); 709 goto fail; 710 } 711 712 if (rc != IMAP_CMD_OK) 713 goto fail; 714 715 /* check for READ-ONLY notification */ 716 if (!ascii_strncasecmp (imap_get_qualifier (idata->buf), "[READ-ONLY]", 11) \ 717 && !mutt_bit_isset (idata->capabilities, ACL)) 718 { 719 dprint (2, (debugfile, "Mailbox is read-only.\n")); 720 ctx->readonly = 1; 721 } 722 723#ifdef DEBUG 724 /* dump the mailbox flags we've found */ 725 if (debuglevel > 2) 726 { 727 if (!idata->flags) 728 dprint (3, (debugfile, "No folder flags found\n")); 729 else 730 { 731 LIST* t = idata->flags; 732 733 dprint (3, (debugfile, "Mailbox flags: ")); 734 735 t = t->next; 736 while (t) 737 { 738 dprint (3, (debugfile, "[%s] ", t->data)); 739 t = t->next; 740 } 741 dprint (3, (debugfile, "\n")); 742 } 743 } 744#endif 745 746 if (!(mutt_bit_isset(idata->ctx->rights, MUTT_ACL_DELETE) || 747 mutt_bit_isset(idata->ctx->rights, MUTT_ACL_SEEN) || 748 mutt_bit_isset(idata->ctx->rights, MUTT_ACL_WRITE) || 749 mutt_bit_isset(idata->ctx->rights, MUTT_ACL_INSERT))) 750 ctx->readonly = 1; 751 752 ctx->hdrmax = count; 753 ctx->hdrs = safe_calloc (count, sizeof (HEADER *)); 754 ctx->v2r = safe_calloc (count, sizeof (int)); 755 ctx->msgcount = 0; 756 757 if (count && (imap_read_headers (idata, 0, count-1) < 0)) 758 { 759 mutt_error _("Error opening mailbox"); 760 mutt_sleep (1); 761 goto fail; 762 } 763 764 dprint (2, (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount)); 765 FREE (&mx.mbox); 766 return 0; 767 768 fail: 769 if (idata->state == IMAP_SELECTED) 770 idata->state = IMAP_AUTHENTICATED; 771 fail_noidata: 772 FREE (&mx.mbox); 773 return -1; 774} 775 776static int imap_open_mailbox_append (CONTEXT *ctx, int flags) 777{ 778 IMAP_DATA *idata; 779 char buf[LONG_STRING]; 780 char mailbox[LONG_STRING]; 781 IMAP_MBOX mx; 782 int rc; 783 784 if (imap_parse_path (ctx->path, &mx)) 785 return -1; 786 787 /* in APPEND mode, we appear to hijack an existing IMAP connection - 788 * ctx is brand new and mostly empty */ 789 790 if (!(idata = imap_conn_find (&(mx.account), 0))) 791 { 792 FREE (&mx.mbox); 793 return -1; 794 } 795 796 ctx->data = idata; 797 798 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); 799 if (!*mailbox) 800 strfcpy (mailbox, "INBOX", sizeof (mailbox)); 801 FREE (&mx.mbox); 802 803 /* really we should also check for W_OK */ 804 if ((rc = imap_access (ctx->path, F_OK)) == 0) 805 return 0; 806 807 if (rc == -1) 808 return -1; 809 810 snprintf (buf, sizeof (buf), _("Create %s?"), mailbox); 811 if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1) 812 return -1; 813 814 if (imap_create_mailbox (idata, mailbox) < 0) 815 return -1; 816 817 return 0; 818} 819 820/* imap_logout: Gracefully log out of server. */ 821void imap_logout (IMAP_DATA** idata) 822{ 823 /* we set status here to let imap_handle_untagged know we _expect_ to 824 * receive a bye response (so it doesn't freak out and close the conn) */ 825 (*idata)->status = IMAP_BYE; 826 imap_cmd_start (*idata, "LOGOUT"); 827 while (imap_cmd_step (*idata) == IMAP_CMD_CONTINUE) 828 ; 829 830 mutt_socket_close ((*idata)->conn); 831 imap_free_idata (idata); 832} 833 834static int imap_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr) 835{ 836 char tmp[_POSIX_PATH_MAX]; 837 838 mutt_mktemp (tmp, sizeof (tmp)); 839 if ((msg->fp = safe_fopen (tmp, "w")) == NULL) 840 { 841 mutt_perror (tmp); 842 return (-1); 843 } 844 msg->path = safe_strdup(tmp); 845 return 0; 846} 847 848/* imap_set_flag: append str to flags if we currently have permission 849 * according to aclbit */ 850static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag, 851 const char *str, char *flags, size_t flsize) 852{ 853 if (mutt_bit_isset (idata->ctx->rights, aclbit)) 854 if (flag && imap_has_flag (idata->flags, str)) 855 safe_strcat (flags, flsize, str); 856} 857 858/* imap_has_flag: do a caseless comparison of the flag against a flag list, 859* return 1 if found or flag list has '\*', 0 otherwise */ 860int imap_has_flag (LIST* flag_list, const char* flag) 861{ 862 if (!flag_list) 863 return 0; 864 865 flag_list = flag_list->next; 866 while (flag_list) 867 { 868 if (!ascii_strncasecmp (flag_list->data, flag, strlen (flag_list->data))) 869 return 1; 870 871 if (!ascii_strncmp (flag_list->data, "\\*", strlen (flag_list->data))) 872 return 1; 873 874 flag_list = flag_list->next; 875 } 876 877 return 0; 878} 879 880/* Note: headers must be in SORT_ORDER. See imap_exec_msgset for args. 881 * Pos is an opaque pointer a la strtok. It should be 0 at first call. */ 882static int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, 883 int changed, int invert, int* pos) 884{ 885 HEADER** hdrs = idata->ctx->hdrs; 886 int count = 0; /* number of messages in message set */ 887 int match = 0; /* whether current message matches flag condition */ 888 unsigned int setstart = 0; /* start of current message range */ 889 int n; 890 int started = 0; 891 892 hdrs = idata->ctx->hdrs; 893 894 for (n = *pos; 895 n < idata->ctx->msgcount && buf->dptr - buf->data < IMAP_MAX_CMDLEN; 896 n++) 897 { 898 match = 0; 899 /* don't include pending expunged messages */ 900 if (hdrs[n]->active) 901 switch (flag) 902 { 903 case MUTT_DELETED: 904 if (hdrs[n]->deleted != HEADER_DATA(hdrs[n])->deleted) 905 match = invert ^ hdrs[n]->deleted; 906 break; 907 case MUTT_FLAG: 908 if (hdrs[n]->flagged != HEADER_DATA(hdrs[n])->flagged) 909 match = invert ^ hdrs[n]->flagged; 910 break; 911 case MUTT_OLD: 912 if (hdrs[n]->old != HEADER_DATA(hdrs[n])->old) 913 match = invert ^ hdrs[n]->old; 914 break; 915 case MUTT_READ: 916 if (hdrs[n]->read != HEADER_DATA(hdrs[n])->read) 917 match = invert ^ hdrs[n]->read; 918 break; 919 case MUTT_REPLIED: 920 if (hdrs[n]->replied != HEADER_DATA(hdrs[n])->replied) 921 match = invert ^ hdrs[n]->replied; 922 break; 923 924 case MUTT_TAG: 925 if (hdrs[n]->tagged) 926 match = 1; 927 break; 928 case MUTT_TRASH: 929 if (hdrs[n]->deleted && !hdrs[n]->purge) 930 match = 1; 931 break; 932 } 933 934 if (match && (!changed || hdrs[n]->changed)) 935 { 936 count++; 937 if (setstart == 0) 938 { 939 setstart = HEADER_DATA (hdrs[n])->uid; 940 if (started == 0) 941 { 942 mutt_buffer_printf (buf, "%u", HEADER_DATA (hdrs[n])->uid); 943 started = 1; 944 } 945 else 946 mutt_buffer_printf (buf, ",%u", HEADER_DATA (hdrs[n])->uid); 947 } 948 /* tie up if the last message also matches */ 949 else if (n == idata->ctx->msgcount-1) 950 mutt_buffer_printf (buf, ":%u", HEADER_DATA (hdrs[n])->uid); 951 } 952 /* End current set if message doesn't match or we've reached the end 953 * of the mailbox via inactive messages following the last match. */ 954 else if (setstart && (hdrs[n]->active || n == idata->ctx->msgcount-1)) 955 { 956 if (HEADER_DATA (hdrs[n-1])->uid > setstart) 957 mutt_buffer_printf (buf, ":%u", HEADER_DATA (hdrs[n-1])->uid); 958 setstart = 0; 959 } 960 } 961 962 *pos = n; 963 964 return count; 965} 966 967/* Prepares commands for all messages matching conditions (must be flushed 968 * with imap_exec) 969 * Params: 970 * idata: IMAP_DATA containing context containing header set 971 * pre, post: commands are of the form "%s %s %s %s", tag, 972 * pre, message set, post 973 * flag: enum of flag type on which to filter 974 * changed: include only changed messages in message set 975 * invert: invert sense of flag, eg MUTT_READ matches unread messages 976 * Returns: number of matched messages, or -1 on failure */ 977int imap_exec_msgset (IMAP_DATA* idata, const char* pre, const char* post, 978 int flag, int changed, int invert) 979{ 980 HEADER** hdrs = NULL; 981 short oldsort; 982 BUFFER* cmd; 983 int pos; 984 int rc; 985 int count = 0; 986 987 if (! (cmd = mutt_buffer_new ())) 988 { 989 dprint (1, (debugfile, "imap_exec_msgset: unable to allocate buffer\n")); 990 return -1; 991 } 992 993 /* We make a copy of the headers just in case resorting doesn't give 994 exactly the original order (duplicate messages?), because other parts of 995 the ctx are tied to the header order. This may be overkill. */ 996 oldsort = Sort; 997 if (Sort != SORT_ORDER) 998 { 999 hdrs = idata->ctx->hdrs; 1000 idata->ctx->hdrs = safe_malloc (idata->ctx->msgcount * sizeof (HEADER*)); 1001 memcpy (idata->ctx->hdrs, hdrs, idata->ctx->msgcount * sizeof (HEADER*)); 1002 1003 Sort = SORT_ORDER; 1004 qsort (idata->ctx->hdrs, idata->ctx->msgcount, sizeof (HEADER*), 1005 mutt_get_sort_func (SORT_ORDER)); 1006 } 1007 1008 pos = 0; 1009 1010 do 1011 { 1012 cmd->dptr = cmd->data; 1013 mutt_buffer_printf (cmd, "%s ", pre); 1014 rc = imap_make_msg_set (idata, cmd, flag, changed, invert, &pos); 1015 if (rc > 0) 1016 { 1017 mutt_buffer_printf (cmd, " %s", post); 1018 if (imap_exec (idata, cmd->data, IMAP_CMD_QUEUE)) 1019 { 1020 rc = -1; 1021 goto out; 1022 } 1023 count += rc; 1024 } 1025 } 1026 while (rc > 0); 1027 1028 rc = count; 1029 1030out: 1031 mutt_buffer_free (&cmd); 1032 if (oldsort != Sort) 1033 { 1034 Sort = oldsort; 1035 FREE (&idata->ctx->hdrs); 1036 idata->ctx->hdrs = hdrs; 1037 } 1038 1039 return rc; 1040} 1041 1042/* returns 0 if mutt's flags match cached server flags */ 1043static int compare_flags (HEADER* h) 1044{ 1045 IMAP_HEADER_DATA* hd = (IMAP_HEADER_DATA*)h->data; 1046 1047 if (h->read != hd->read) 1048 return 1; 1049 if (h->old != hd->old) 1050 return 1; 1051 if (h->flagged != hd->flagged) 1052 return 1; 1053 if (h->replied != hd->replied) 1054 return 1; 1055 if (h->deleted != hd->deleted) 1056 return 1; 1057 1058 return 0; 1059} 1060 1061/* Update the IMAP server to reflect the flags a single message. */ 1062int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd, 1063 int *err_continue) 1064{ 1065 char flags[LONG_STRING]; 1066 char uid[11]; 1067 1068 hdr->changed = 0; 1069 1070 if (!compare_flags (hdr)) 1071 { 1072 idata->ctx->changed--; 1073 return 0; 1074 } 1075 1076 snprintf (uid, sizeof (uid), "%u", HEADER_DATA(hdr)->uid); 1077 cmd->dptr = cmd->data; 1078 mutt_buffer_addstr (cmd, "UID STORE "); 1079 mutt_buffer_addstr (cmd, uid); 1080 1081 flags[0] = '\0'; 1082 1083 imap_set_flag (idata, MUTT_ACL_SEEN, hdr->read, "\\Seen ", 1084 flags, sizeof (flags)); 1085 imap_set_flag (idata, MUTT_ACL_WRITE, hdr->old, 1086 "Old ", flags, sizeof (flags)); 1087 imap_set_flag (idata, MUTT_ACL_WRITE, hdr->flagged, 1088 "\\Flagged ", flags, sizeof (flags)); 1089 imap_set_flag (idata, MUTT_ACL_WRITE, hdr->replied, 1090 "\\Answered ", flags, sizeof (flags)); 1091 imap_set_flag (idata, MUTT_ACL_DELETE, hdr->deleted, 1092 "\\Deleted ", flags, sizeof (flags)); 1093 1094 /* now make sure we don't lose custom tags */ 1095 if (mutt_bit_isset (idata->ctx->rights, MUTT_ACL_WRITE)) 1096 imap_add_keywords (flags, hdr, idata->flags, sizeof (flags)); 1097 1098 mutt_remove_trailing_ws (flags); 1099 1100 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to 1101 * explicitly revoke all system flags (if we have permission) */ 1102 if (!*flags) 1103 { 1104 imap_set_flag (idata, MUTT_ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags)); 1105 imap_set_flag (idata, MUTT_ACL_WRITE, 1, "Old ", flags, sizeof (flags)); 1106 imap_set_flag (idata, MUTT_ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags)); 1107 imap_set_flag (idata, MUTT_ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags)); 1108 imap_set_flag (idata, MUTT_ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags)); 1109 1110 mutt_remove_trailing_ws (flags); 1111 1112 mutt_buffer_addstr (cmd, " -FLAGS.SILENT ("); 1113 } else 1114 mutt_buffer_addstr (cmd, " FLAGS.SILENT ("); 1115 1116 mutt_buffer_addstr (cmd, flags); 1117 mutt_buffer_addstr (cmd, ")"); 1118 1119 /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */ 1120 hdr->active = 0; 1121 1122 /* after all this it's still possible to have no flags, if you 1123 * have no ACL rights */ 1124 if (*flags && (imap_exec (idata, cmd->data, 0) != 0) && 1125 err_continue && (*err_continue != MUTT_YES)) 1126 { 1127 *err_continue = imap_continue ("imap_sync_message: STORE failed", 1128 idata->buf); 1129 if (*err_continue != MUTT_YES) 1130 return -1; 1131 } 1132 1133 hdr->active = 1; 1134 idata->ctx->changed--; 1135 1136 return 0; 1137} 1138 1139static int sync_helper (IMAP_DATA* idata, int right, int flag, const char* name) 1140{ 1141 int count = 0; 1142 int rc; 1143 char buf[LONG_STRING]; 1144 1145 if (!idata->ctx) 1146 return -1; 1147 1148 if (!mutt_bit_isset (idata->ctx->rights, right)) 1149 return 0; 1150 1151 if (right == MUTT_ACL_WRITE && !imap_has_flag (idata->flags, name)) 1152 return 0; 1153 1154 snprintf (buf, sizeof(buf), "+FLAGS.SILENT (%s)", name); 1155 if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 0)) < 0) 1156 return rc; 1157 count += rc; 1158 1159 buf[0] = '-'; 1160 if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 1)) < 0) 1161 return rc; 1162 count += rc; 1163 1164 return count; 1165} 1166 1167/* update the IMAP server to reflect message changes done within mutt. 1168 * Arguments 1169 * ctx: the current context 1170 * expunge: 0 or 1 - do expunge? 1171 */ 1172int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint) 1173{ 1174 IMAP_DATA* idata; 1175 CONTEXT* appendctx = NULL; 1176 HEADER* h; 1177 HEADER** hdrs = NULL; 1178 int oldsort; 1179 int n; 1180 int rc; 1181 1182 idata = (IMAP_DATA*) ctx->data; 1183 1184 if (idata->state < IMAP_SELECTED) 1185 { 1186 dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n")); 1187 return -1; 1188 } 1189 1190 /* This function is only called when the calling code expects the context 1191 * to be changed. */ 1192 imap_allow_reopen (ctx); 1193 1194 if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0) 1195 return rc; 1196 1197 /* if we are expunging anyway, we can do deleted messages very quickly... */ 1198 if (expunge && mutt_bit_isset (ctx->rights, MUTT_ACL_DELETE)) 1199 { 1200 if ((rc = imap_exec_msgset (idata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", 1201 MUTT_DELETED, 1, 0)) < 0) 1202 { 1203 mutt_error (_("Expunge failed")); 1204 mutt_sleep (1); 1205 goto out; 1206 } 1207 1208 if (rc > 0) 1209 { 1210 /* mark these messages as unchanged so second pass ignores them. Done 1211 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */ 1212 for (n = 0; n < ctx->msgcount; n++) 1213 if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed) 1214 ctx->hdrs[n]->active = 0; 1215 mutt_message (_("Marking %d messages deleted..."), rc); 1216 } 1217 } 1218 1219#if USE_HCACHE 1220 idata->hcache = imap_hcache_open (idata, NULL); 1221#endif 1222 1223 /* save messages with real (non-flag) changes */ 1224 for (n = 0; n < ctx->msgcount; n++) 1225 { 1226 h = ctx->hdrs[n]; 1227 1228 if (h->deleted) 1229 { 1230 imap_cache_del (idata, h); 1231#if USE_HCACHE 1232 imap_hcache_del (idata, HEADER_DATA(h)->uid); 1233#endif 1234 } 1235 1236 if (h->active && h->changed) 1237 { 1238#if USE_HCACHE 1239 imap_hcache_put (idata, h); 1240#endif 1241 /* if the message has been rethreaded or attachments have been deleted 1242 * we delete the message and reupload it. 1243 * This works better if we're expunging, of course. */ 1244 if ((h->env && (h->env->refs_changed || h->env->irt_changed)) || 1245 h->attach_del) 1246 { 1247 mutt_message (_("Saving changed messages... [%d/%d]"), n+1, 1248 ctx->msgcount); 1249 if (!appendctx) 1250 appendctx = mx_open_mailbox (ctx->path, MUTT_APPEND | MUTT_QUIET, NULL); 1251 if (!appendctx) 1252 dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n")); 1253 else 1254 _mutt_save_message (h, appendctx, 1, 0, 0); 1255 } 1256 } 1257 } 1258 1259#if USE_HCACHE 1260 imap_hcache_close (idata); 1261#endif 1262 1263 /* presort here to avoid doing 10 resorts in imap_exec_msgset */ 1264 oldsort = Sort; 1265 if (Sort != SORT_ORDER) 1266 { 1267 hdrs = ctx->hdrs; 1268 ctx->hdrs = safe_malloc (ctx->msgcount * sizeof (HEADER*)); 1269 memcpy (ctx->hdrs, hdrs, ctx->msgcount * sizeof (HEADER*)); 1270 1271 Sort = SORT_ORDER; 1272 qsort (ctx->hdrs, ctx->msgcount, sizeof (HEADER*), 1273 mutt_get_sort_func (SORT_ORDER)); 1274 } 1275 1276 rc = sync_helper (idata, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted"); 1277 if (rc >= 0) 1278 rc |= sync_helper (idata, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged"); 1279 if (rc >= 0) 1280 rc |= sync_helper (idata, MUTT_ACL_WRITE, MUTT_OLD, "Old"); 1281 if (rc >= 0) 1282 rc |= sync_helper (idata, MUTT_ACL_SEEN, MUTT_READ, "\\Seen"); 1283 if (rc >= 0) 1284 rc |= sync_helper (idata, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered"); 1285 1286 if (oldsort != Sort) 1287 { 1288 Sort = oldsort; 1289 FREE (&ctx->hdrs); 1290 ctx->hdrs = hdrs; 1291 } 1292 1293 /* Flush the queued flags if any were changed in sync_helper. */ 1294 if (rc > 0) 1295 if (imap_exec (idata, NULL, 0) != IMAP_CMD_OK) 1296 rc = -1; 1297 1298 if (rc < 0) 1299 { 1300 if (ctx->closing) 1301 { 1302 if (mutt_yesorno (_("Error saving flags. Close anyway?"), 0) == MUTT_YES) 1303 { 1304 rc = 0; 1305 idata->state = IMAP_AUTHENTICATED; 1306 goto out; 1307 } 1308 } 1309 else 1310 mutt_error _("Error saving flags"); 1311 rc = -1; 1312 goto out; 1313 } 1314 1315 /* Update local record of server state to reflect the synchronization just 1316 * completed. imap_read_headers always overwrites hcache-origin flags, so 1317 * there is no need to mutate the hcache after flag-only changes. */ 1318 for (n = 0; n < ctx->msgcount; n++) 1319 { 1320 HEADER_DATA(ctx->hdrs[n])->deleted = ctx->hdrs[n]->deleted; 1321 HEADER_DATA(ctx->hdrs[n])->flagged = ctx->hdrs[n]->flagged; 1322 HEADER_DATA(ctx->hdrs[n])->old = ctx->hdrs[n]->old; 1323 HEADER_DATA(ctx->hdrs[n])->read = ctx->hdrs[n]->read; 1324 HEADER_DATA(ctx->hdrs[n])->replied = ctx->hdrs[n]->replied; 1325 ctx->hdrs[n]->changed = 0; 1326 } 1327 ctx->changed = 0; 1328 1329 /* We must send an EXPUNGE command if we're not closing. */ 1330 if (expunge && !(ctx->closing) && 1331 mutt_bit_isset(ctx->rights, MUTT_ACL_DELETE)) 1332 { 1333 mutt_message _("Expunging messages from server..."); 1334 /* Set expunge bit so we don't get spurious reopened messages */ 1335 idata->reopen |= IMAP_EXPUNGE_EXPECTED; 1336 if (imap_exec (idata, "EXPUNGE", 0) != 0) 1337 { 1338 imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->buf); 1339 rc = -1; 1340 goto out; 1341 } 1342 } 1343 1344 if (expunge && ctx->closing) 1345 { 1346 imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE); 1347 idata->state = IMAP_AUTHENTICATED; 1348 } 1349 1350 if (option (OPTMESSAGECACHECLEAN)) 1351 imap_cache_clean (idata); 1352 1353 rc = 0; 1354 1355 out: 1356 if (appendctx) 1357 { 1358 mx_fastclose_mailbox (appendctx); 1359 FREE (&appendctx); 1360 } 1361 return rc; 1362} 1363 1364/* imap_close_mailbox: clean up IMAP data in CONTEXT */ 1365int imap_close_mailbox (CONTEXT* ctx) 1366{ 1367 IMAP_DATA* idata; 1368 int i; 1369 1370 idata = (IMAP_DATA*) ctx->data; 1371 /* Check to see if the mailbox is actually open */ 1372 if (!idata) 1373 return 0; 1374 1375 if (ctx == idata->ctx) 1376 { 1377 if (idata->status != IMAP_FATAL && idata->state >= IMAP_SELECTED) 1378 { 1379 /* mx_close_mailbox won't sync if there are no deleted messages 1380 * and the mailbox is unchanged, so we may have to close here */ 1381 if (!ctx->deleted) 1382 imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE); 1383 idata->state = IMAP_AUTHENTICATED; 1384 } 1385 1386 idata->reopen &= IMAP_REOPEN_ALLOW; 1387 FREE (&(idata->mailbox)); 1388 mutt_free_list (&idata->flags); 1389 idata->ctx = NULL; 1390 } 1391 1392 /* free IMAP part of headers */ 1393 for (i = 0; i < ctx->msgcount; i++) 1394 /* mailbox may not have fully loaded */ 1395 if (ctx->hdrs[i] && ctx->hdrs[i]->data) 1396 imap_free_header_data ((IMAP_HEADER_DATA**)&(ctx->hdrs[i]->data)); 1397 hash_destroy (&idata->uid_hash, NULL); 1398 1399 for (i = 0; i < IMAP_CACHE_LEN; i++) 1400 { 1401 if (idata->cache[i].path) 1402 { 1403 unlink (idata->cache[i].path); 1404 FREE (&idata->cache[i].path); 1405 } 1406 } 1407 1408 mutt_bcache_close (&idata->bcache); 1409 1410 return 0; 1411} 1412 1413/* use the NOOP or IDLE command to poll for new mail 1414 * 1415 * return values: 1416 * MUTT_REOPENED mailbox has been externally modified 1417 * MUTT_NEW_MAIL new mail has arrived! 1418 * 0 no change 1419 * -1 error 1420 */ 1421int imap_check_mailbox (CONTEXT *ctx, int *index_hint, int force) 1422{ 1423 /* overload keyboard timeout to avoid many mailbox checks in a row. 1424 * Most users don't like having to wait exactly when they press a key. */ 1425 IMAP_DATA* idata; 1426 int result = 0; 1427 1428 idata = (IMAP_DATA*) ctx->data; 1429 1430 /* try IDLE first, unless force is set */ 1431 if (!force && option (OPTIMAPIDLE) && mutt_bit_isset (idata->capabilities, IDLE) 1432 && (idata->state != IMAP_IDLE || time(NULL) >= idata->lastread + ImapKeepalive)) 1433 { 1434 if (imap_cmd_idle (idata) < 0) 1435 return -1; 1436 } 1437 if (idata->state == IMAP_IDLE) 1438 { 1439 while ((result = mutt_socket_poll (idata->conn)) > 0) 1440 { 1441 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) 1442 { 1443 dprint (1, (debugfile, "Error reading IDLE response\n")); 1444 return -1; 1445 } 1446 } 1447 if (result < 0) 1448 { 1449 dprint (1, (debugfile, "Poll failed, disabling IDLE\n")); 1450 mutt_bit_unset (idata->capabilities, IDLE); 1451 } 1452 } 1453 1454 if ((force || 1455 (idata->state != IMAP_IDLE && time(NULL) >= idata->lastread + Timeout)) 1456 && imap_exec (idata, "NOOP", 0) != 0) 1457 return -1; 1458 1459 /* We call this even when we haven't run NOOP in case we have pending 1460 * changes to process, since we can reopen here. */ 1461 imap_cmd_finish (idata); 1462 1463 if (idata->check_status & IMAP_EXPUNGE_PENDING) 1464 result = MUTT_REOPENED; 1465 else if (idata->check_status & IMAP_NEWMAIL_PENDING) 1466 result = MUTT_NEW_MAIL; 1467 else if (idata->check_status & IMAP_FLAGS_PENDING) 1468 result = MUTT_FLAGS; 1469 1470 idata->check_status = 0; 1471 1472 return result; 1473} 1474 1475static int imap_check_mailbox_reopen (CONTEXT *ctx, int *index_hint) 1476{ 1477 int rc; 1478 1479 imap_allow_reopen (ctx); 1480 rc = imap_check_mailbox (ctx, index_hint, 0); 1481 imap_disallow_reopen (ctx); 1482 1483 return rc; 1484} 1485 1486/* split path into (idata,mailbox name) */ 1487static int imap_get_mailbox (const char* path, IMAP_DATA** hidata, char* buf, size_t blen) 1488{ 1489 IMAP_MBOX mx; 1490 1491 if (imap_parse_path (path, &mx)) 1492 { 1493 dprint (1, (debugfile, "imap_get_mailbox: Error parsing %s\n", path)); 1494 return -1; 1495 } 1496 if (!(*hidata = imap_conn_find (&(mx.account), option (OPTIMAPPASSIVE) ? MUTT_IMAP_CONN_NONEW : 0)) 1497 || (*hidata)->state < IMAP_AUTHENTICATED) 1498 { 1499 FREE (&mx.mbox); 1500 return -1; 1501 } 1502 1503 imap_fix_path (*hidata, mx.mbox, buf, blen); 1504 if (!*buf) 1505 strfcpy (buf, "INBOX", blen); 1506 FREE (&mx.mbox); 1507 1508 return 0; 1509} 1510 1511/* check for new mail in any subscribed mailboxes. Given a list of mailboxes 1512 * rather than called once for each so that it can batch the commands and 1513 * save on round trips. Returns number of mailboxes with new mail. */ 1514int imap_buffy_check (int force, int check_stats) 1515{ 1516 IMAP_DATA* idata; 1517 IMAP_DATA* lastdata = NULL; 1518 BUFFY* mailbox; 1519 char name[LONG_STRING]; 1520 char command[LONG_STRING]; 1521 char munged[LONG_STRING]; 1522 int buffies = 0; 1523 1524 for (mailbox = Incoming; mailbox; mailbox = mailbox->next) 1525 { 1526 /* Init newly-added mailboxes */ 1527 if (! mailbox->magic) 1528 { 1529 if (mx_is_imap (mailbox->path)) 1530 mailbox->magic = MUTT_IMAP; 1531 } 1532 1533 if (mailbox->magic != MUTT_IMAP) 1534 continue; 1535 1536 if (imap_get_mailbox (mailbox->path, &idata, name, sizeof (name)) < 0) 1537 { 1538 mailbox->new = 0; 1539 continue; 1540 } 1541 1542 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or 1543 * IDLEd elsewhere. 1544 * idata->mailbox may be NULL for connections other than the current 1545 * mailbox's, and shouldn't expand to INBOX in that case. #3216. */ 1546 if (idata->mailbox && !imap_mxcmp (name, idata->mailbox)) 1547 { 1548 mailbox->new = 0; 1549 continue; 1550 } 1551 1552 if (!mutt_bit_isset (idata->capabilities, IMAP4REV1) && 1553 !mutt_bit_isset (idata->capabilities, STATUS)) 1554 { 1555 dprint (2, (debugfile, "Server doesn't support STATUS\n")); 1556 continue; 1557 } 1558 1559 if (lastdata && idata != lastdata) 1560 { 1561 /* Send commands to previous server. Sorting the buffy list 1562 * may prevent some infelicitous interleavings */ 1563 if (imap_exec (lastdata, NULL, IMAP_CMD_FAIL_OK) == -1) 1564 dprint (1, (debugfile, "Error polling mailboxes\n")); 1565 1566 lastdata = NULL; 1567 } 1568 1569 if (!lastdata) 1570 lastdata = idata; 1571 1572 imap_munge_mbox_name (idata, munged, sizeof (munged), name); 1573 if (check_stats) 1574 snprintf (command, sizeof (command), 1575 "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT MESSAGES)", munged); 1576 else 1577 snprintf (command, sizeof (command), 1578 "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged); 1579 1580 if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0) 1581 { 1582 dprint (1, (debugfile, "Error queueing command\n")); 1583 return 0; 1584 } 1585 } 1586 1587 if (lastdata && (imap_exec (lastdata, NULL, IMAP_CMD_FAIL_OK) == -1)) 1588 { 1589 dprint (1, (debugfile, "Error polling mailboxes\n")); 1590 return 0; 1591 } 1592 1593 /* collect results */ 1594 for (mailbox = Incoming; mailbox; mailbox = mailbox->next) 1595 { 1596 if (mailbox->magic == MUTT_IMAP && mailbox->new) 1597 buffies++; 1598 } 1599 1600 return buffies; 1601} 1602 1603/* imap_status: returns count of messages in mailbox, or -1 on error. 1604 * if queue != 0, queue the command and expect it to have been run 1605 * on the next call (for pipelining the postponed count) */ 1606int imap_status (char* path, int queue) 1607{ 1608 static int queued = 0; 1609 1610 IMAP_DATA *idata; 1611 char buf[LONG_STRING]; 1612 char mbox[LONG_STRING]; 1613 IMAP_STATUS* status; 1614 1615 if (imap_get_mailbox (path, &idata, buf, sizeof (buf)) < 0) 1616 return -1; 1617 1618 if (!imap_mxcmp (buf, idata->mailbox)) 1619 /* We are in the folder we're polling - just return the mailbox count */ 1620 return idata->ctx->msgcount; 1621 else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) || 1622 mutt_bit_isset(idata->capabilities,STATUS)) 1623 { 1624 imap_munge_mbox_name (idata, mbox, sizeof(mbox), buf); 1625 snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox, "MESSAGES"); 1626 imap_unmunge_mbox_name (idata, mbox); 1627 } 1628 else 1629 /* Server does not support STATUS, and this is not the current mailbox. 1630 * There is no lightweight way to check recent arrivals */ 1631 return -1; 1632 1633 if (queue) 1634 { 1635 imap_exec (idata, buf, IMAP_CMD_QUEUE); 1636 queued = 1; 1637 return 0; 1638 } 1639 else if (!queued) 1640 imap_exec (idata, buf, 0); 1641 1642 queued = 0; 1643 if ((status = imap_mboxcache_get (idata, mbox, 0))) 1644 return status->messages; 1645 1646 return 0; 1647} 1648 1649/* return cached mailbox stats or NULL if create is 0 */ 1650IMAP_STATUS* imap_mboxcache_get (IMAP_DATA* idata, const char* mbox, int create) 1651{ 1652 LIST* cur; 1653 IMAP_STATUS* status; 1654 IMAP_STATUS scache; 1655#ifdef USE_HCACHE 1656 header_cache_t *hc = NULL; 1657 unsigned int *uidvalidity = NULL; 1658 unsigned int *uidnext = NULL; 1659#endif 1660 1661 for (cur = idata->mboxcache; cur; cur = cur->next) 1662 { 1663 status = (IMAP_STATUS*)cur->data; 1664 1665 if (!imap_mxcmp (mbox, status->name)) 1666 return status; 1667 } 1668 status = NULL; 1669 1670 /* lame */ 1671 if (create) 1672 { 1673 memset (&scache, 0, sizeof (scache)); 1674 scache.name = (char*)mbox; 1675 idata->mboxcache = mutt_add_list_n (idata->mboxcache, &scache, 1676 sizeof (scache)); 1677 status = imap_mboxcache_get (idata, mbox, 0); 1678 status->name = safe_strdup (mbox); 1679 } 1680 1681#ifdef USE_HCACHE 1682 hc = imap_hcache_open (idata, mbox); 1683 if (hc) 1684 { 1685 uidvalidity = mutt_hcache_fetch_raw (hc, "/UIDVALIDITY", imap_hcache_keylen); 1686 uidnext = mutt_hcache_fetch_raw (hc, "/UIDNEXT", imap_hcache_keylen); 1687 mutt_hcache_close (hc); 1688 if (uidvalidity) 1689 { 1690 if (!status) 1691 { 1692 FREE (&uidvalidity); 1693 FREE (&uidnext); 1694 return imap_mboxcache_get (idata, mbox, 1); 1695 } 1696 status->uidvalidity = *uidvalidity; 1697 status->uidnext = uidnext ? *uidnext: 0; 1698 dprint (3, (debugfile, "mboxcache: hcache uidvalidity %d, uidnext %d\n", 1699 status->uidvalidity, status->uidnext)); 1700 } 1701 FREE (&uidvalidity); 1702 FREE (&uidnext); 1703 } 1704#endif 1705 1706 return status; 1707} 1708 1709void imap_mboxcache_free (IMAP_DATA* idata) 1710{ 1711 LIST* cur; 1712 IMAP_STATUS* status; 1713 1714 for (cur = idata->mboxcache; cur; cur = cur->next) 1715 { 1716 status = (IMAP_STATUS*)cur->data; 1717 1718 FREE (&status->name); 1719 } 1720 1721 mutt_free_list (&idata->mboxcache); 1722} 1723 1724/* returns number of patterns in the search that should be done server-side 1725 * (eg are full-text) */ 1726static int do_search (const pattern_t* search, int allpats) 1727{ 1728 int rc = 0; 1729 const pattern_t* pat; 1730 1731 for (pat = search; pat; pat = pat->next) 1732 { 1733 switch (pat->op) 1734 { 1735 case MUTT_BODY: 1736 case MUTT_HEADER: 1737 case MUTT_WHOLE_MSG: 1738 if (pat->stringmatch) 1739 rc++; 1740 break; 1741 default: 1742 if (pat->child && do_search (pat->child, 1)) 1743 rc++; 1744 } 1745 1746 if (!allpats) 1747 break; 1748 } 1749 1750 return rc; 1751} 1752 1753/* convert mutt pattern_t to IMAP SEARCH command containing only elements 1754 * that require full-text search (mutt already has what it needs for most 1755 * match types, and does a better job (eg server doesn't support regexps). */ 1756static int imap_compile_search (const pattern_t* pat, BUFFER* buf) 1757{ 1758 if (! do_search (pat, 0)) 1759 return 0; 1760 1761 if (pat->not) 1762 mutt_buffer_addstr (buf, "NOT "); 1763 1764 if (pat->child) 1765 { 1766 int clauses; 1767 1768 if ((clauses = do_search (pat->child, 1)) > 0) 1769 { 1770 const pattern_t* clause = pat->child; 1771 1772 mutt_buffer_addch (buf, '('); 1773 1774 while (clauses) 1775 { 1776 if (do_search (clause, 0)) 1777 { 1778 if (pat->op == MUTT_OR && clauses > 1) 1779 mutt_buffer_addstr (buf, "OR "); 1780 clauses--; 1781 1782 if (imap_compile_search (clause, buf) < 0) 1783 return -1; 1784 1785 if (clauses) 1786 mutt_buffer_addch (buf, ' '); 1787 1788 } 1789 clause = clause->next; 1790 } 1791 1792 mutt_buffer_addch (buf, ')'); 1793 } 1794 } 1795 else 1796 { 1797 char term[STRING]; 1798 char *delim; 1799 1800 switch (pat->op) 1801 { 1802 case MUTT_HEADER: 1803 mutt_buffer_addstr (buf, "HEADER "); 1804 1805 /* extract header name */ 1806 if (! (delim = strchr (pat->p.str, ':'))) 1807 { 1808 mutt_error (_("Header search without header name: %s"), pat->p.str); 1809 return -1; 1810 } 1811 *delim = '\0'; 1812 imap_quote_string (term, sizeof (term), pat->p.str); 1813 mutt_buffer_addstr (buf, term); 1814 mutt_buffer_addch (buf, ' '); 1815 1816 /* and field */ 1817 *delim = ':'; 1818 delim++; 1819 SKIPWS(delim); 1820 imap_quote_string (term, sizeof (term), delim); 1821 mutt_buffer_addstr (buf, term); 1822 break; 1823 case MUTT_BODY: 1824 mutt_buffer_addstr (buf, "BODY "); 1825 imap_quote_string (term, sizeof (term), pat->p.str); 1826 mutt_buffer_addstr (buf, term); 1827 break; 1828 case MUTT_WHOLE_MSG: 1829 mutt_buffer_addstr (buf, "TEXT "); 1830 imap_quote_string (term, sizeof (term), pat->p.str); 1831 mutt_buffer_addstr (buf, term); 1832 break; 1833 } 1834 } 1835 1836 return 0; 1837} 1838 1839int imap_search (CONTEXT* ctx, const pattern_t* pat) 1840{ 1841 BUFFER buf; 1842 IMAP_DATA* idata = (IMAP_DATA*)ctx->data; 1843 int i; 1844 1845 for (i = 0; i < ctx->msgcount; i++) 1846 ctx->hdrs[i]->matched = 0; 1847 1848 if (!do_search (pat, 1)) 1849 return 0; 1850 1851 mutt_buffer_init (&buf); 1852 mutt_buffer_addstr (&buf, "UID SEARCH "); 1853 if (imap_compile_search (pat, &buf) < 0) 1854 { 1855 FREE (&buf.data); 1856 return -1; 1857 } 1858 if (imap_exec (idata, buf.data, 0) < 0) 1859 { 1860 FREE (&buf.data); 1861 return -1; 1862 } 1863 1864 FREE (&buf.data); 1865 return 0; 1866} 1867 1868int imap_subscribe (char *path, int subscribe) 1869{ 1870 IMAP_DATA *idata; 1871 char buf[LONG_STRING]; 1872 char mbox[LONG_STRING]; 1873 char errstr[STRING]; 1874 BUFFER err, token; 1875 IMAP_MBOX mx; 1876 1877 if (!mx_is_imap (path) || imap_parse_path (path, &mx) || !mx.mbox) 1878 { 1879 mutt_error (_("Bad mailbox name")); 1880 return -1; 1881 } 1882 if (!(idata = imap_conn_find (&(mx.account), 0))) 1883 goto fail; 1884 1885 imap_fix_path (idata, mx.mbox, buf, sizeof (buf)); 1886 if (!*buf) 1887 strfcpy (buf, "INBOX", sizeof (buf)); 1888 1889 if (option (OPTIMAPCHECKSUBSCRIBED)) 1890 { 1891 mutt_buffer_init (&token); 1892 mutt_buffer_init (&err); 1893 err.data = errstr; 1894 err.dsize = sizeof (errstr); 1895 snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"", 1896 subscribe ? "" : "un", path); 1897 if (mutt_parse_rc_line (mbox, &token, &err)) 1898 dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr)); 1899 FREE (&token.data); 1900 } 1901 1902 if (subscribe) 1903 mutt_message (_("Subscribing to %s..."), buf); 1904 else 1905 mutt_message (_("Unsubscribing from %s..."), buf); 1906 imap_munge_mbox_name (idata, mbox, sizeof(mbox), buf); 1907 1908 snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox); 1909 1910 if (imap_exec (idata, buf, 0) < 0) 1911 goto fail; 1912 1913 imap_unmunge_mbox_name(idata, mx.mbox); 1914 if (subscribe) 1915 mutt_message (_("Subscribed to %s"), mx.mbox); 1916 else 1917 mutt_message (_("Unsubscribed from %s"), mx.mbox); 1918 FREE (&mx.mbox); 1919 return 0; 1920 1921 fail: 1922 FREE (&mx.mbox); 1923 return -1; 1924} 1925 1926/* trim dest to the length of the longest prefix it shares with src, 1927 * returning the length of the trimmed string */ 1928static int 1929longest_common_prefix (char *dest, const char* src, int start, size_t dlen) 1930{ 1931 int pos = start; 1932 1933 while (pos < dlen && dest[pos] && dest[pos] == src[pos]) 1934 pos++; 1935 dest[pos] = '\0'; 1936 1937 return pos; 1938} 1939 1940/* look for IMAP URLs to complete from defined mailboxes. Could be extended 1941 * to complete over open connections and account/folder hooks too. */ 1942static int 1943imap_complete_hosts (char *dest, size_t len) 1944{ 1945 BUFFY* mailbox; 1946 CONNECTION* conn; 1947 int rc = -1; 1948 int matchlen; 1949 1950 matchlen = mutt_strlen (dest); 1951 for (mailbox = Incoming; mailbox; mailbox = mailbox->next) 1952 { 1953 if (!mutt_strncmp (dest, mailbox->path, matchlen)) 1954 { 1955 if (rc) 1956 { 1957 strfcpy (dest, mailbox->path, len); 1958 rc = 0; 1959 } 1960 else 1961 longest_common_prefix (dest, mailbox->path, matchlen, len); 1962 } 1963 } 1964 1965 for (conn = mutt_socket_head (); conn; conn = conn->next) 1966 { 1967 ciss_url_t url; 1968 char urlstr[LONG_STRING]; 1969 1970 if (conn->account.type != MUTT_ACCT_TYPE_IMAP) 1971 continue; 1972 1973 mutt_account_tourl (&conn->account, &url); 1974 /* FIXME: how to handle multiple users on the same host? */ 1975 url.user = NULL; 1976 url.path = NULL; 1977 url_ciss_tostring (&url, urlstr, sizeof (urlstr), 0); 1978 if (!mutt_strncmp (dest, urlstr, matchlen)) 1979 { 1980 if (rc) 1981 { 1982 strfcpy (dest, urlstr, len); 1983 rc = 0; 1984 } 1985 else 1986 longest_common_prefix (dest, urlstr, matchlen, len); 1987 } 1988 } 1989 1990 return rc; 1991} 1992 1993/* imap_complete: given a partial IMAP folder path, return a string which 1994 * adds as much to the path as is unique */ 1995int imap_complete(char* dest, size_t dlen, char* path) { 1996 IMAP_DATA* idata; 1997 char list[LONG_STRING]; 1998 char buf[LONG_STRING]; 1999 IMAP_LIST listresp; 2000 char completion[LONG_STRING]; 2001 int clen, matchlen = 0; 2002 int completions = 0; 2003 IMAP_MBOX mx; 2004 int rc; 2005 2006 if (imap_parse_path (path, &mx)) 2007 { 2008 strfcpy (dest, path, dlen); 2009 return imap_complete_hosts (dest, dlen); 2010 } 2011 2012 /* don't open a new socket just for completion. Instead complete over 2013 * known mailboxes/hooks/etc */ 2014 if (!(idata = imap_conn_find (&(mx.account), MUTT_IMAP_CONN_NONEW))) 2015 { 2016 FREE (&mx.mbox); 2017 strfcpy (dest, path, dlen); 2018 return imap_complete_hosts (dest, dlen); 2019 } 2020 2021 /* reformat path for IMAP list, and append wildcard */ 2022 /* don't use INBOX in place of "" */ 2023 if (mx.mbox && mx.mbox[0]) 2024 imap_fix_path (idata, mx.mbox, list, sizeof(list)); 2025 else 2026 list[0] = '\0'; 2027 2028 /* fire off command */ 2029 snprintf (buf, sizeof(buf), "%s \"\" \"%s%%\"", 2030 option (OPTIMAPLSUB) ? "LSUB" : "LIST", list); 2031 2032 imap_cmd_start (idata, buf); 2033 2034 /* and see what the results are */ 2035 strfcpy (completion, NONULL(mx.mbox), sizeof(completion)); 2036 idata->cmdtype = IMAP_CT_LIST; 2037 idata->cmddata = &listresp; 2038 do 2039 { 2040 listresp.name = NULL; 2041 rc = imap_cmd_step (idata); 2042 2043 if (rc == IMAP_CMD_CONTINUE && listresp.name) 2044 { 2045 /* if the folder isn't selectable, append delimiter to force browse 2046 * to enter it on second tab. */ 2047 if (listresp.noselect) 2048 { 2049 clen = strlen(listresp.name); 2050 listresp.name[clen++] = listresp.delim; 2051 listresp.name[clen] = '\0'; 2052 } 2053 /* copy in first word */ 2054 if (!completions) 2055 { 2056 strfcpy (completion, listresp.name, sizeof(completion)); 2057 matchlen = strlen (completion); 2058 completions++; 2059 continue; 2060 } 2061 2062 matchlen = longest_common_prefix (completion, listresp.name, 0, matchlen); 2063 completions++; 2064 } 2065 } 2066 while (rc == IMAP_CMD_CONTINUE); 2067 idata->cmddata = NULL; 2068 2069 if (completions) 2070 { 2071 /* reformat output */ 2072 imap_qualify_path (dest, dlen, &mx, completion); 2073 mutt_pretty_mailbox (dest, dlen); 2074 2075 FREE (&mx.mbox); 2076 return 0; 2077 } 2078 2079 return -1; 2080} 2081 2082/* imap_fast_trash: use server COPY command to copy deleted 2083 * messages to the trash folder. 2084 * Return codes: 2085 * -1: error 2086 * 0: success 2087 * 1: non-fatal error - try fetch/append */ 2088int imap_fast_trash (CONTEXT* ctx, char* dest) 2089{ 2090 IMAP_DATA* idata; 2091 char mbox[LONG_STRING]; 2092 char mmbox[LONG_STRING]; 2093 char prompt[LONG_STRING]; 2094 int rc; 2095 IMAP_MBOX mx; 2096 int triedcreate = 0; 2097 2098 idata = (IMAP_DATA*) ctx->data; 2099 2100 if (imap_parse_path (dest, &mx)) 2101 { 2102 dprint (1, (debugfile, "imap_fast_trash: bad destination %s\n", dest)); 2103 return -1; 2104 } 2105 2106 /* check that the save-to folder is in the same account */ 2107 if (!mutt_account_match (&(CTX_DATA->conn->account), &(mx.account))) 2108 { 2109 dprint (3, (debugfile, "imap_fast_trash: %s not same server as %s\n", 2110 dest, ctx->path)); 2111 return 1; 2112 } 2113 2114 imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); 2115 if (!*mbox) 2116 strfcpy (mbox, "INBOX", sizeof (mbox)); 2117 imap_munge_mbox_name (idata, mmbox, sizeof (mmbox), mbox); 2118 2119 /* loop in case of TRYCREATE */ 2120 do 2121 { 2122 rc = imap_exec_msgset (idata, "UID STORE", "+FLAGS.SILENT (\\Seen)", 2123 MUTT_TRASH, 0, 0); 2124 if (rc < 0) 2125 { 2126 dprint (1, (debugfile, "imap_fast_trash: Unable to mark messages as seen\n")); 2127 goto out; 2128 } 2129 2130 rc = imap_exec_msgset (idata, "UID COPY", mmbox, MUTT_TRASH, 0, 0); 2131 if (!rc) 2132 { 2133 dprint (1, (debugfile, "imap_fast_trash: No messages to trash\n")); 2134 rc = -1; 2135 goto out; 2136 } 2137 else if (rc < 0) 2138 { 2139 dprint (1, (debugfile, "could not queue copy\n")); 2140 goto out; 2141 } 2142 else 2143 mutt_message (_("Copying %d messages to %s..."), rc, mbox); 2144 2145 /* let's get it on */ 2146 rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); 2147 if (rc == -2) 2148 { 2149 if (triedcreate) 2150 { 2151 dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox)); 2152 break; 2153 } 2154 /* bail out if command failed for reasons other than nonexistent target */ 2155 if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) 2156 break; 2157 dprint (3, (debugfile, "imap_fast_trash: server suggests TRYCREATE\n")); 2158 snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox); 2159 if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1) 2160 { 2161 mutt_clear_error (); 2162 goto out; 2163 } 2164 if (imap_create_mailbox (idata, mbox) < 0) 2165 break; 2166 triedcreate = 1; 2167 } 2168 } 2169 while (rc == -2); 2170 2171 if (rc != 0) 2172 { 2173 imap_error ("imap_fast_trash", idata->buf); 2174 goto out; 2175 } 2176 2177 rc = 0; 2178 2179 out: 2180 FREE (&mx.mbox); 2181 2182 return rc < 0 ? -1 : rc; 2183} 2184 2185struct mx_ops mx_imap_ops = { 2186 .open = imap_open_mailbox, 2187 .open_append = imap_open_mailbox_append, 2188 .close = imap_close_mailbox, 2189 .open_msg = imap_fetch_message, 2190 .close_msg = imap_close_message, 2191 .commit_msg = imap_commit_message, 2192 .open_new_msg = imap_open_new_message, 2193 .check = imap_check_mailbox_reopen, 2194 .sync = NULL, /* imap syncing is handled by imap_sync_mailbox */ 2195};