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