Andreas Krennmair:
[apps/madmutt.git] / imap / imap.c
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-2003 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., 59 Temple Place - Suite 330, Boston, MA  02111, 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 "mutt_curses.h"
29 #include "mx.h"
30 #include "mailbox.h"
31 #include "globals.h"
32 #include "sort.h"
33 #include "browser.h"
34 #include "message.h"
35 #include "imap_private.h"
36 #if defined(USE_SSL) || defined(USE_GNUTLS)
37 # include "mutt_ssl.h"
38 #endif
39
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46
47 /* imap forward declarations */
48 static int imap_get_delim (IMAP_DATA * idata);
49 static char *imap_get_flags (LIST ** hflags, char *s);
50 static int imap_check_acl (IMAP_DATA * idata);
51 static int imap_check_capabilities (IMAP_DATA * idata);
52 static 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 int imap_access (const char *path, int flags)
57 {
58   IMAP_DATA *idata;
59   IMAP_MBOX mx;
60   char buf[LONG_STRING];
61   char mailbox[LONG_STRING];
62   char mbox[LONG_STRING];
63
64   if (imap_parse_path (path, &mx))
65     return -1;
66
67   if (!(idata = imap_conn_find (&mx.account,
68                                 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW :
69                                 0))) {
70     FREE (&mx.mbox);
71     return -1;
72   }
73
74   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
75   FREE (&mx.mbox);
76   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
77
78   /* TODO: ACL checks. Right now we assume if it exists we can mess with it. */
79   if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
80     snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
81   else if (mutt_bit_isset (idata->capabilities, STATUS))
82     snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
83   else {
84     dprint (2, (debugfile, "imap_access: STATUS not supported?\n"));
85     return -1;
86   }
87
88   if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0) {
89     dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox));
90     return -1;
91   }
92
93   return 0;
94 }
95
96 int imap_create_mailbox (IMAP_DATA * idata, char *mailbox)
97 {
98   char buf[LONG_STRING], mbox[LONG_STRING];
99
100   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
101   snprintf (buf, sizeof (buf), "CREATE %s", mbox);
102
103   if (imap_exec (idata, buf, 0) != 0)
104     return -1;
105
106   return 0;
107 }
108
109 int imap_rename_mailbox (IMAP_DATA * idata, IMAP_MBOX * mx,
110                          const char *newname)
111 {
112   char oldmbox[LONG_STRING];
113   char newmbox[LONG_STRING];
114   char buf[LONG_STRING];
115
116   imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox);
117   imap_munge_mbox_name (newmbox, sizeof (newmbox), newname);
118
119   snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox);
120
121   if (imap_exec (idata, buf, 0) != 0)
122     return -1;
123
124   return 0;
125 }
126
127 int imap_delete_mailbox (CONTEXT * ctx, IMAP_MBOX mx)
128 {
129   char buf[LONG_STRING], mbox[LONG_STRING];
130   IMAP_DATA *idata;
131
132   if (!ctx || !ctx->data) {
133     if (!(idata = imap_conn_find (&mx.account,
134                                   option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW
135                                   : 0))) {
136       FREE (&mx.mbox);
137       return -1;
138     }
139   }
140   else {
141     idata = ctx->data;
142   }
143
144   imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
145   snprintf (buf, sizeof (buf), "DELETE %s", mbox);
146
147   if (imap_exec ((IMAP_DATA *) idata, buf, 0) != 0)
148     return -1;
149
150   return 0;
151 }
152
153 /* imap_logout_all: close all open connections. Quick and dirty until we can
154  *   make sure we've got all the context we need. */
155 void imap_logout_all (void)
156 {
157   CONNECTION *conn;
158   CONNECTION *tmp;
159
160   conn = mutt_socket_head ();
161
162   while (conn) {
163     tmp = conn->next;
164
165     if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0) {
166       mutt_message (_("Closing connection to %s..."), conn->account.host);
167       imap_logout ((IMAP_DATA *) conn->data);
168       mutt_clear_error ();
169       mutt_socket_close (conn);
170       mutt_socket_free (conn);
171     }
172
173     conn = tmp;
174   }
175 }
176
177 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
178  *   buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
179  *   Apparently even literals use \r\n-terminated strings ?! */
180 int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes)
181 {
182   long pos;
183   char c;
184
185   int r = 0;
186
187   dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes));
188
189   for (pos = 0; pos < bytes; pos++) {
190     if (mutt_socket_readchar (idata->conn, &c) != 1) {
191       dprint (1,
192               (debugfile,
193                "imap_read_literal: error during read, %ld bytes read\n",
194                pos));
195       idata->status = IMAP_FATAL;
196
197       return -1;
198     }
199
200 #if 1
201     if (r == 1 && c != '\n')
202       fputc ('\r', fp);
203
204     if (c == '\r') {
205       r = 1;
206       continue;
207     }
208     else
209       r = 0;
210 #endif
211     fputc (c, fp);
212 #ifdef DEBUG
213     if (debuglevel >= IMAP_LOG_LTRL)
214       fputc (c, debugfile);
215 #endif
216   }
217
218   return 0;
219 }
220
221 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
222  *   context. Must not be done while something has a handle on any headers
223  *   (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
224 void imap_expunge_mailbox (IMAP_DATA * idata)
225 {
226   HEADER *h;
227   int i, cacheno;
228
229   for (i = 0; i < idata->ctx->msgcount; i++) {
230     h = idata->ctx->hdrs[i];
231
232     if (h->index == -1) {
233       dprint (2,
234               (debugfile, "Expunging message UID %d.\n",
235                HEADER_DATA (h)->uid));
236
237       h->active = 0;
238
239       /* free cached body from disk, if neccessary */
240       cacheno = HEADER_DATA (h)->uid % IMAP_CACHE_LEN;
241       if (idata->cache[cacheno].uid == HEADER_DATA (h)->uid &&
242           idata->cache[cacheno].path) {
243         unlink (idata->cache[cacheno].path);
244         FREE (&idata->cache[cacheno].path);
245       }
246
247       imap_free_header_data (&h->data);
248     }
249   }
250
251   /* We may be called on to expunge at any time. We can't rely on the caller
252    * to always know to rethread */
253   mx_update_tables (idata->ctx, 0);
254   mutt_sort_headers (idata->ctx, 1);
255 }
256
257 static int imap_get_delim (IMAP_DATA * idata)
258 {
259   char *s;
260   int rc;
261
262   /* assume that the delim is /.  If this fails, we're in bigger trouble
263    * than getting the delim wrong */
264   idata->delim = '/';
265
266   imap_cmd_start (idata, "LIST \"\" \"\"");
267
268   do {
269     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
270       break;
271
272     s = imap_next_word (idata->cmd.buf);
273     if (ascii_strncasecmp ("LIST", s, 4) == 0) {
274       s = imap_next_word (s);
275       s = imap_next_word (s);
276       if (s && s[0] == '\"' && s[1] && s[2] == '\"')
277         idata->delim = s[1];
278       else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
279                && s[3] == '\"')
280         idata->delim = s[2];
281     }
282   }
283   while (rc == IMAP_CMD_CONTINUE);
284
285   if (rc != IMAP_CMD_OK) {
286     dprint (1, (debugfile, "imap_get_delim: failed.\n"));
287     return -1;
288   }
289
290   dprint (2, (debugfile, "Delimiter: %c\n", idata->delim));
291
292   return -1;
293 }
294
295 /* get rights for folder, let imap_handle_untagged do the rest */
296 static int imap_check_acl (IMAP_DATA * idata)
297 {
298   char buf[LONG_STRING];
299   char mbox[LONG_STRING];
300
301   imap_munge_mbox_name (mbox, sizeof (mbox), idata->mailbox);
302   snprintf (buf, sizeof (buf), "MYRIGHTS %s", mbox);
303   if (imap_exec (idata, buf, 0) != 0) {
304     imap_error ("imap_check_acl", buf);
305     return -1;
306   }
307   return 0;
308 }
309
310 /* imap_check_capabilities: make sure we can log in to this server. */
311 static int imap_check_capabilities (IMAP_DATA * idata)
312 {
313   if (imap_exec (idata, "CAPABILITY", 0) != 0) {
314     imap_error ("imap_check_capabilities", idata->cmd.buf);
315     return -1;
316   }
317
318   if (!(mutt_bit_isset (idata->capabilities, IMAP4)
319         || mutt_bit_isset (idata->capabilities, IMAP4REV1))) {
320     mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
321
322     mutt_sleep (2);             /* pause a moment to let the user see the error */
323
324     return -1;
325   }
326
327   return 0;
328 }
329
330 /* imap_conn_find: Find an open IMAP connection matching account, or open
331  *   a new one if none can be found. */
332 IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
333 {
334   CONNECTION *conn;
335   IMAP_DATA *idata;
336   ACCOUNT *creds;
337
338   if (!(conn = mutt_conn_find (NULL, account)))
339     return NULL;
340
341   /* if opening a new UNSELECTED connection, preserve existing creds */
342   creds = &(conn->account);
343
344   /* make sure this connection is not in SELECTED state, if neccessary */
345   if (flags & M_IMAP_CONN_NOSELECT)
346     while (conn->data && ((IMAP_DATA *) conn->data)->state == IMAP_SELECTED) {
347       if (!(conn = mutt_conn_find (conn, account)))
348         return NULL;
349       memcpy (&(conn->account), creds, sizeof (ACCOUNT));
350     }
351
352   idata = (IMAP_DATA *) conn->data;
353
354   /* don't open a new connection if one isn't wanted */
355   if (flags & M_IMAP_CONN_NONEW) {
356     if (!idata) {
357       mutt_socket_free (conn);
358       return NULL;
359     }
360     if (idata->state < IMAP_AUTHENTICATED)
361       return NULL;
362   }
363
364   if (!idata) {
365     /* The current connection is a new connection */
366     if (!(idata = imap_new_idata ())) {
367       mutt_socket_free (conn);
368       return NULL;
369     }
370
371     conn->data = idata;
372     idata->conn = conn;
373   }
374
375   if (idata->state == IMAP_DISCONNECTED)
376     imap_open_connection (idata);
377   if (idata->state == IMAP_CONNECTED) {
378     if (!imap_authenticate (idata)) {
379       idata->state = IMAP_AUTHENTICATED;
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     FREE (&idata->capstr);
388   }
389   if (idata->state == IMAP_AUTHENTICATED)
390     imap_get_delim (idata);
391
392   return idata;
393 }
394
395 int imap_open_connection (IMAP_DATA * idata)
396 {
397   char buf[LONG_STRING];
398
399   if (mutt_socket_open (idata->conn) < 0)
400     return -1;
401
402   idata->state = IMAP_CONNECTED;
403
404   if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) {
405     mutt_error (_("Unexpected response received from server: %s"),
406                 idata->cmd.buf);
407     mutt_sleep (1);
408
409     mutt_socket_close (idata->conn);
410     idata->state = IMAP_DISCONNECTED;
411     return -1;
412   }
413
414   if (ascii_strncasecmp ("* OK", idata->cmd.buf, 4) == 0) {
415     /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
416     if (imap_check_capabilities (idata))
417       goto bail;
418 #if defined(USE_SSL) || defined(USE_GNUTLS)
419     /* Attempt STARTTLS if available and desired. */
420     if (mutt_bit_isset (idata->capabilities, STARTTLS) && !idata->conn->ssf) {
421       int rc;
422
423       if ((rc = query_quadoption (OPT_SSLSTARTTLS,
424                                   _("Secure connection with TLS?"))) == -1)
425         goto err_close_conn;
426       if (rc == M_YES) {
427         if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
428           goto bail;
429         if (rc != -2) {
430 #ifdef USE_SSL
431           if (mutt_ssl_starttls (idata->conn))
432 #elif USE_GNUTLS
433           if (mutt_gnutls_starttls (idata->conn))
434 #endif
435           {
436             mutt_error (_("Could not negotiate TLS connection"));
437             mutt_sleep (1);
438             goto err_close_conn;
439           }
440           else {
441             /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
442             if (imap_exec (idata, "CAPABILITY", 0))
443               goto bail;
444           }
445         }
446       }
447     }
448 #endif
449   }
450   else if (ascii_strncasecmp ("* PREAUTH", idata->cmd.buf, 9) == 0) {
451     idata->state = IMAP_AUTHENTICATED;
452     if (imap_check_capabilities (idata) != 0)
453       goto bail;
454     FREE (&idata->capstr);
455   }
456   else {
457     imap_error ("imap_open_connection()", buf);
458     goto bail;
459   }
460
461   return 0;
462
463 err_close_conn:
464   mutt_socket_close (idata->conn);
465   idata->state = IMAP_DISCONNECTED;
466 bail:
467   FREE (&idata->capstr);
468   return -1;
469 }
470
471 /* imap_get_flags: Make a simple list out of a FLAGS response.
472  *   return stream following FLAGS response */
473 static char *imap_get_flags (LIST ** hflags, char *s)
474 {
475   LIST *flags;
476   char *flag_word;
477   char ctmp;
478
479   /* sanity-check string */
480   if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
481     dprint (1, (debugfile, "imap_get_flags: not a FLAGS response: %s\n", s));
482     return NULL;
483   }
484   s += 5;
485   SKIPWS (s);
486   if (*s != '(') {
487     dprint (1, (debugfile, "imap_get_flags: bogus FLAGS response: %s\n", s));
488     return NULL;
489   }
490
491   /* create list, update caller's flags handle */
492   flags = mutt_new_list ();
493   *hflags = flags;
494
495   while (*s && *s != ')') {
496     s++;
497     SKIPWS (s);
498     flag_word = s;
499     while (*s && (*s != ')') && !ISSPACE (*s))
500       s++;
501     ctmp = *s;
502     *s = '\0';
503     if (*flag_word)
504       mutt_add_list (flags, flag_word);
505     *s = ctmp;
506   }
507
508   /* note bad flags response */
509   if (*s != ')') {
510     dprint (1, (debugfile,
511                 "imap_get_flags: Unterminated FLAGS response: %s\n", s));
512     mutt_free_list (hflags);
513
514     return NULL;
515   }
516
517   s++;
518
519   return s;
520 }
521
522 int imap_open_mailbox (CONTEXT * ctx)
523 {
524   CONNECTION *conn;
525   IMAP_DATA *idata;
526   char buf[LONG_STRING];
527   char bufout[LONG_STRING];
528   int count = 0;
529   IMAP_MBOX mx;
530   int rc;
531
532   if (imap_parse_path (ctx->path, &mx)) {
533     mutt_error (_("%s is an invalid IMAP path"), ctx->path);
534     return -1;
535   }
536
537   /* we require a connection which isn't currently in IMAP_SELECTED state */
538   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
539     goto fail_noidata;
540   if (idata->state < IMAP_AUTHENTICATED)
541     goto fail;
542
543   conn = idata->conn;
544
545   /* once again the context is new */
546   ctx->data = idata;
547
548   /* Clean up path and replace the one in the ctx */
549   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
550   FREE (&(idata->mailbox));
551   idata->mailbox = safe_strdup (buf);
552   imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
553
554   FREE (&(ctx->path));
555   ctx->path = safe_strdup (buf);
556
557   idata->ctx = ctx;
558
559   /* clear mailbox status */
560   idata->status = 0;
561   memset (idata->rights, 0, (RIGHTSMAX + 7) / 8);
562   idata->newMailCount = 0;
563
564   mutt_message (_("Selecting %s..."), idata->mailbox);
565   imap_munge_mbox_name (buf, sizeof (buf), idata->mailbox);
566   snprintf (bufout, sizeof (bufout), "%s %s",
567             ctx->readonly ? "EXAMINE" : "SELECT", buf);
568
569   idata->state = IMAP_SELECTED;
570
571   imap_cmd_start (idata, bufout);
572
573   do {
574     char *pc;
575
576     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
577       break;
578
579     pc = idata->cmd.buf + 2;
580
581     /* Obtain list of available flags here, may be overridden by a
582      * PERMANENTFLAGS tag in the OK response */
583     if (ascii_strncasecmp ("FLAGS", pc, 5) == 0) {
584       /* don't override PERMANENTFLAGS */
585       if (!idata->flags) {
586         dprint (2, (debugfile, "Getting mailbox FLAGS\n"));
587         if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
588           goto fail;
589       }
590     }
591     /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
592     else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0) {
593       dprint (2, (debugfile, "Getting mailbox PERMANENTFLAGS\n"));
594       /* safe to call on NULL */
595       mutt_free_list (&(idata->flags));
596       /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
597       pc += 13;
598       if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
599         goto fail;
600     }
601 #ifdef USE_HCACHE
602     /* save UIDVALIDITY for the header cache */
603     else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0) {
604       dprint (2, (debugfile, "Getting mailbox UIDVALIDITY\n"));
605       pc += 3;
606       pc = imap_next_word (pc);
607
608       sscanf (pc, "%u", &(idata->uid_validity));
609     }
610 #endif
611     else {
612       pc = imap_next_word (pc);
613       if (!ascii_strncasecmp ("EXISTS", pc, 6)) {
614         count = idata->newMailCount;
615         idata->newMailCount = 0;
616       }
617     }
618   }
619   while (rc == IMAP_CMD_CONTINUE);
620
621   if (rc == IMAP_CMD_NO) {
622     char *s;
623
624     s = imap_next_word (idata->cmd.buf);        /* skip seq */
625     s = imap_next_word (s);     /* Skip response */
626     mutt_error ("%s", s);
627     mutt_sleep (2);
628     goto fail;
629   }
630
631   if (rc != IMAP_CMD_OK)
632     goto fail;
633
634   /* check for READ-ONLY notification */
635   if (!ascii_strncasecmp
636       (imap_get_qualifier (idata->cmd.buf), "[READ-ONLY]", 11)
637       && !mutt_bit_isset (idata->capabilities, ACL)) {
638     dprint (2, (debugfile, "Mailbox is read-only.\n"));
639     ctx->readonly = 1;
640   }
641
642 #ifdef DEBUG
643   /* dump the mailbox flags we've found */
644   if (debuglevel > 2) {
645     if (!idata->flags)
646       dprint (3, (debugfile, "No folder flags found\n"));
647     else {
648       LIST *t = idata->flags;
649
650       dprint (3, (debugfile, "Mailbox flags: "));
651
652       t = t->next;
653       while (t) {
654         dprint (3, (debugfile, "[%s] ", t->data));
655         t = t->next;
656       }
657       dprint (3, (debugfile, "\n"));
658     }
659   }
660 #endif
661
662   if (mutt_bit_isset (idata->capabilities, ACL)) {
663     if (imap_check_acl (idata))
664       goto fail;
665     if (!(mutt_bit_isset (idata->rights, IMAP_ACL_DELETE) ||
666           mutt_bit_isset (idata->rights, IMAP_ACL_SEEN) ||
667           mutt_bit_isset (idata->rights, IMAP_ACL_WRITE) ||
668           mutt_bit_isset (idata->rights, IMAP_ACL_INSERT)))
669       ctx->readonly = 1;
670   }
671   /* assume we have all rights if ACL is unavailable */
672   else {
673     mutt_bit_set (idata->rights, IMAP_ACL_LOOKUP);
674     mutt_bit_set (idata->rights, IMAP_ACL_READ);
675     mutt_bit_set (idata->rights, IMAP_ACL_SEEN);
676     mutt_bit_set (idata->rights, IMAP_ACL_WRITE);
677     mutt_bit_set (idata->rights, IMAP_ACL_INSERT);
678     mutt_bit_set (idata->rights, IMAP_ACL_POST);
679     mutt_bit_set (idata->rights, IMAP_ACL_CREATE);
680     mutt_bit_set (idata->rights, IMAP_ACL_DELETE);
681   }
682
683   ctx->hdrmax = count;
684   ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
685   ctx->v2r = safe_calloc (count, sizeof (int));
686   ctx->msgcount = 0;
687   if (count && (imap_read_headers (idata, 0, count - 1) < 0)) {
688     mutt_error _("Error opening mailbox");
689
690     mutt_sleep (1);
691     goto fail;
692   }
693
694   dprint (2,
695           (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount));
696   FREE (&mx.mbox);
697   return 0;
698
699 fail:
700   if (idata->state == IMAP_SELECTED)
701     idata->state = IMAP_AUTHENTICATED;
702 fail_noidata:
703   FREE (&mx.mbox);
704   return -1;
705 }
706
707 int imap_open_mailbox_append (CONTEXT * ctx)
708 {
709   CONNECTION *conn;
710   IMAP_DATA *idata;
711   char buf[LONG_STRING], mbox[LONG_STRING];
712   char mailbox[LONG_STRING];
713   int r;
714   IMAP_MBOX mx;
715
716   if (imap_parse_path (ctx->path, &mx))
717     return -1;
718
719   /* in APPEND mode, we appear to hijack an existing IMAP connection -
720    * ctx is brand new and mostly empty */
721
722   if (!(idata = imap_conn_find (&(mx.account), 0)))
723     goto fail;
724   conn = idata->conn;
725
726   ctx->magic = M_IMAP;
727   ctx->data = idata;
728
729   /* check mailbox existance */
730
731   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
732
733   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
734
735   if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
736     snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
737   else if (mutt_bit_isset (idata->capabilities, STATUS))
738     /* We have no idea what the other guy wants. UW imapd 8.3 wants this
739      * (but it does not work if another mailbox is selected) */
740     snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
741   else {
742     /* STATUS not supported */
743     mutt_message _("Unable to append to IMAP mailboxes at this server");
744
745     goto fail;
746   }
747
748   r = imap_exec (idata, buf, IMAP_CMD_FAIL_OK);
749   if (r == -2) {
750     /* command failed cause folder doesn't exist */
751     snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
752     if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
753       goto fail;
754
755     if (imap_create_mailbox (idata, mailbox) < 0)
756       goto fail;
757   }
758   else if (r == -1)
759     /* Hmm, some other failure */
760     goto fail;
761
762   FREE (&mx.mbox);
763   return 0;
764
765 fail:
766   FREE (&mx.mbox);
767   return -1;
768 }
769
770 /* imap_logout: Gracefully log out of server. */
771 void imap_logout (IMAP_DATA * idata)
772 {
773   /* we set status here to let imap_handle_untagged know we _expect_ to
774    * receive a bye response (so it doesn't freak out and close the conn) */
775   idata->status = IMAP_BYE;
776   imap_cmd_start (idata, "LOGOUT");
777   while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE);
778   FREE (&idata->cmd.buf);
779   FREE (&idata);
780 }
781
782 /*
783 int imap_close_connection (CONTEXT *ctx)
784 {
785   dprint (1, (debugfile, "imap_close_connection(): closing connection\n"));
786   if (CTX_DATA->status != IMAP_BYE)
787   {
788     mutt_message _("Closing connection to IMAP server...");
789     imap_logout (CTX_DATA);
790     mutt_clear_error ();
791   }
792   mutt_socket_close (CTX_DATA->conn);
793   CTX_DATA->state = IMAP_DISCONNECTED;
794   CTX_DATA->conn->data = NULL;
795   return 0;
796 }
797 */
798
799 /* imap_set_flag: append str to flags if we currently have permission
800  *   according to aclbit */
801 static void imap_set_flag (IMAP_DATA * idata, int aclbit, int flag,
802                            const char *str, char *flags, size_t flsize)
803 {
804   if (mutt_bit_isset (idata->rights, aclbit))
805     if (flag)
806       safe_strcat (flags, flsize, str);
807 }
808
809 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of
810  *   headers, given a flag enum to filter on.
811  * Params: idata: IMAP_DATA containing context containing header set
812  *         buf: to write message set into
813  *         buflen: length of buffer
814  *         flag: enum of flag type on which to filter
815  *         changed: include only changed messages in message set
816  * Returns: number of messages in message set (0 if no matches) */
817 int imap_make_msg_set (IMAP_DATA * idata, BUFFER * buf, int flag, int changed)
818 {
819   HEADER **hdrs;                /* sorted local copy */
820   int count = 0;                /* number of messages in message set */
821   int match = 0;                /* whether current message matches flag condition */
822   unsigned int setstart = 0;    /* start of current message range */
823   int n;
824   short oldsort;                /* we clobber reverse, must restore it */
825
826   /* assuming 32-bit UIDs */
827   char uid[12];
828   int started = 0;
829
830   /* make copy of header pointers to sort in natural order */
831   hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER *));
832   memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER *));
833
834   if (Sort != SORT_ORDER) {
835     oldsort = Sort;
836     Sort = SORT_ORDER;
837     qsort ((void *) hdrs, idata->ctx->msgcount, sizeof (HEADER *),
838            mutt_get_sort_func (SORT_ORDER));
839     Sort = oldsort;
840   }
841
842   for (n = 0; n < idata->ctx->msgcount; n++) {
843     match = 0;
844     /* don't include pending expunged messages */
845     if (hdrs[n]->active)
846       switch (flag) {
847       case M_DELETE:
848         if (hdrs[n]->deleted)
849           match = 1;
850         break;
851       case M_TAG:
852         if (hdrs[n]->tagged)
853           match = 1;
854         break;
855       }
856
857     if (match && (!changed || hdrs[n]->changed)) {
858       count++;
859       if (setstart == 0) {
860         setstart = HEADER_DATA (hdrs[n])->uid;
861         if (started == 0) {
862           snprintf (uid, sizeof (uid), "%u", HEADER_DATA (hdrs[n])->uid);
863           mutt_buffer_addstr (buf, uid);
864           started = 1;
865         }
866         else {
867           snprintf (uid, sizeof (uid), ",%u", HEADER_DATA (hdrs[n])->uid);
868           mutt_buffer_addstr (buf, uid);
869         }
870       }
871       /* tie up if the last message also matches */
872       else if (n == idata->ctx->msgcount - 1) {
873         snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n])->uid);
874         mutt_buffer_addstr (buf, uid);
875       }
876     }
877     /* this message is not expunged and doesn't match. End current set. */
878     else if (setstart && hdrs[n]->active) {
879       if (HEADER_DATA (hdrs[n - 1])->uid > setstart) {
880         snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n - 1])->uid);
881         mutt_buffer_addstr (buf, uid);
882       }
883       setstart = 0;
884     }
885   }
886
887   FREE (&hdrs);
888
889   return count;
890 }
891
892 /* update the IMAP server to reflect message changes done within mutt.
893  * Arguments
894  *   ctx: the current context
895  *   expunge: 0 or 1 - do expunge? 
896  */
897
898 int imap_sync_mailbox (CONTEXT * ctx, int expunge, int *index_hint)
899 {
900   IMAP_DATA *idata;
901   CONTEXT *appendctx = NULL;
902   BUFFER cmd;
903   char flags[LONG_STRING];
904   char uid[11];
905   int deleted;
906   int n;
907   int err_continue = M_NO;      /* continue on error? */
908   int rc;
909
910   idata = (IMAP_DATA *) ctx->data;
911
912   if (idata->state != IMAP_SELECTED) {
913     dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n"));
914     return -1;
915   }
916
917   /* CLOSE purges deleted messages. If we don't want to purge them, we must
918    * tell imap_close_mailbox not to issue the CLOSE command */
919   if (expunge)
920     idata->noclose = 0;
921   else
922     idata->noclose = 1;
923
924   /* This function is only called when the calling code expects the context
925    * to be changed. */
926   imap_allow_reopen (ctx);
927
928   if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
929     return rc;
930
931   memset (&cmd, 0, sizeof (cmd));
932
933   /* if we are expunging anyway, we can do deleted messages very quickly... */
934   if (expunge && mutt_bit_isset (idata->rights, IMAP_ACL_DELETE)) {
935     mutt_buffer_addstr (&cmd, "UID STORE ");
936     deleted = imap_make_msg_set (idata, &cmd, M_DELETE, 1);
937
938     /* if we have a message set, then let's delete */
939     if (deleted) {
940       mutt_message (_("Marking %d messages deleted..."), deleted);
941       mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)");
942       /* mark these messages as unchanged so second pass ignores them. Done
943        * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
944       for (n = 0; n < ctx->msgcount; n++)
945         if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
946           ctx->hdrs[n]->active = 0;
947       if (imap_exec (idata, cmd.data, 0) != 0) {
948         mutt_error (_("Expunge failed"));
949         mutt_sleep (1);
950         rc = -1;
951         goto out;
952       }
953     }
954   }
955
956   /* save status changes */
957   for (n = 0; n < ctx->msgcount; n++) {
958     if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed) {
959       ctx->hdrs[n]->changed = 0;
960
961       mutt_message (_("Saving message status flags... [%d/%d]"), n + 1,
962                     ctx->msgcount);
963
964       snprintf (uid, sizeof (uid), "%u", HEADER_DATA (ctx->hdrs[n])->uid);
965       cmd.dptr = cmd.data;
966       mutt_buffer_addstr (&cmd, "UID STORE ");
967       mutt_buffer_addstr (&cmd, uid);
968
969       /* if the message has been rethreaded or attachments have been deleted
970        * we delete the message and reupload it.
971        * This works better if we're expunging, of course. */
972       if (ctx->hdrs[n]->refs_changed || ctx->hdrs[n]->irt_changed ||
973           ctx->hdrs[n]->attach_del) {
974         dprint (3,
975                 (debugfile,
976                  "imap_sync_mailbox: Attachments to be deleted, falling back to _mutt_save_message\n"));
977         if (!appendctx)
978           appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
979         if (!appendctx) {
980           dprint (1,
981                   (debugfile,
982                    "imap_sync_mailbox: Error opening mailbox in append mode\n"));
983         }
984         else
985           _mutt_save_message (ctx->hdrs[n], appendctx, 1, 0, 0);
986       }
987       flags[0] = '\0';
988
989       imap_set_flag (idata, IMAP_ACL_SEEN, ctx->hdrs[n]->read, "\\Seen ",
990                      flags, sizeof (flags));
991       imap_set_flag (idata, IMAP_ACL_WRITE, ctx->hdrs[n]->flagged,
992                      "\\Flagged ", flags, sizeof (flags));
993       imap_set_flag (idata, IMAP_ACL_WRITE, ctx->hdrs[n]->replied,
994                      "\\Answered ", flags, sizeof (flags));
995       imap_set_flag (idata, IMAP_ACL_DELETE, ctx->hdrs[n]->deleted,
996                      "\\Deleted ", flags, sizeof (flags));
997
998       /* now make sure we don't lose custom tags */
999       if (mutt_bit_isset (idata->rights, IMAP_ACL_WRITE))
1000         imap_add_keywords (flags, ctx->hdrs[n], idata->flags, sizeof (flags));
1001
1002       mutt_remove_trailing_ws (flags);
1003
1004       /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1005        * explicitly revoke all system flags (if we have permission) */
1006       if (!*flags) {
1007         imap_set_flag (idata, IMAP_ACL_SEEN, 1, "\\Seen ", flags,
1008                        sizeof (flags));
1009         imap_set_flag (idata, IMAP_ACL_WRITE, 1, "\\Flagged ", flags,
1010                        sizeof (flags));
1011         imap_set_flag (idata, IMAP_ACL_WRITE, 1, "\\Answered ", flags,
1012                        sizeof (flags));
1013         imap_set_flag (idata, IMAP_ACL_DELETE, 1, "\\Deleted ", flags,
1014                        sizeof (flags));
1015
1016         mutt_remove_trailing_ws (flags);
1017
1018         mutt_buffer_addstr (&cmd, " -FLAGS.SILENT (");
1019       }
1020       else
1021         mutt_buffer_addstr (&cmd, " FLAGS.SILENT (");
1022
1023       mutt_buffer_addstr (&cmd, flags);
1024       mutt_buffer_addstr (&cmd, ")");
1025
1026       /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
1027       ctx->hdrs[n]->active = 0;
1028
1029       /* after all this it's still possible to have no flags, if you
1030        * have no ACL rights */
1031       if (*flags && (imap_exec (idata, cmd.data, 0) != 0) &&
1032           (err_continue != M_YES)) {
1033         err_continue = imap_continue ("imap_sync_mailbox: STORE failed",
1034                                       idata->cmd.buf);
1035         if (err_continue != M_YES) {
1036           rc = -1;
1037           goto out;
1038         }
1039       }
1040
1041       ctx->hdrs[n]->active = 1;
1042     }
1043   }
1044   ctx->changed = 0;
1045
1046   /* We must send an EXPUNGE command if we're not closing. */
1047   if (expunge && !(ctx->closing) &&
1048       mutt_bit_isset (idata->rights, IMAP_ACL_DELETE)) {
1049     mutt_message _("Expunging messages from server...");
1050
1051     /* Set expunge bit so we don't get spurious reopened messages */
1052     idata->reopen |= IMAP_EXPUNGE_EXPECTED;
1053     if (imap_exec (idata, "EXPUNGE", 0) != 0) {
1054       imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->cmd.buf);
1055       rc = -1;
1056       goto out;
1057     }
1058   }
1059
1060   rc = 0;
1061 out:
1062   if (cmd.data)
1063     FREE (&cmd.data);
1064   if (appendctx) {
1065     mx_fastclose_mailbox (appendctx);
1066     FREE (&appendctx);
1067   }
1068   return rc;
1069 }
1070
1071 /* imap_close_mailbox: issue close command if neccessary, reset IMAP_DATA */
1072 void imap_close_mailbox (CONTEXT * ctx)
1073 {
1074   IMAP_DATA *idata;
1075   int i;
1076
1077   idata = (IMAP_DATA *) ctx->data;
1078   /* Check to see if the mailbox is actually open */
1079   if (!idata)
1080     return;
1081
1082   if ((idata->status != IMAP_FATAL) &&
1083       (idata->state == IMAP_SELECTED) && (ctx == idata->ctx)) {
1084     if (!(idata->noclose) && imap_exec (idata, "CLOSE", 0))
1085       mutt_error (_("CLOSE failed"));
1086
1087     idata->reopen &= IMAP_REOPEN_ALLOW;
1088     idata->state = IMAP_AUTHENTICATED;
1089     FREE (&(idata->mailbox));
1090     mutt_free_list (&idata->flags);
1091     idata->ctx = NULL;
1092   }
1093
1094   /* free IMAP part of headers */
1095   for (i = 0; i < ctx->msgcount; i++)
1096     imap_free_header_data (&(ctx->hdrs[i]->data));
1097
1098   for (i = 0; i < IMAP_CACHE_LEN; i++) {
1099     if (idata->cache[i].path) {
1100       unlink (idata->cache[i].path);
1101       FREE (&idata->cache[i].path);
1102     }
1103   }
1104 }
1105
1106 /* use the NOOP command to poll for new mail
1107  *
1108  * return values:
1109  *      M_REOPENED      mailbox has been externally modified
1110  *      M_NEW_MAIL      new mail has arrived!
1111  *      0               no change
1112  *      -1              error
1113  */
1114 int imap_check_mailbox (CONTEXT * ctx, int *index_hint, int force)
1115 {
1116   /* overload keyboard timeout to avoid many mailbox checks in a row.
1117    * Most users don't like having to wait exactly when they press a key. */
1118   IMAP_DATA *idata;
1119   int result = 0;
1120
1121   idata = (IMAP_DATA *) ctx->data;
1122
1123   if ((force || time (NULL) > idata->lastread + Timeout)
1124       && imap_exec (idata, "NOOP", 0) != 0)
1125     return -1;
1126
1127   /* We call this even when we haven't run NOOP in case we have pending
1128    * changes to process, since we can reopen here. */
1129   imap_cmd_finish (idata);
1130
1131   if (idata->check_status & IMAP_EXPUNGE_PENDING)
1132     result = M_REOPENED;
1133   else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1134     result = M_NEW_MAIL;
1135   else if (idata->check_status & IMAP_FLAGS_PENDING)
1136     result = M_FLAGS;
1137
1138   idata->check_status = 0;
1139
1140   return result;
1141 }
1142
1143 /* returns count of recent messages if new = 1, else count of total messages.
1144  * (useful for at least postponed function)
1145  * Question of taste: use RECENT or UNSEEN for new?
1146  *   0+   number of messages in mailbox
1147  *  -1    error while polling mailboxes
1148  */
1149 int imap_mailbox_check (char *path, int new)
1150 {
1151   CONNECTION *conn;
1152   IMAP_DATA *idata;
1153   char buf[LONG_STRING];
1154   char mbox[LONG_STRING];
1155   char mbox_unquoted[LONG_STRING];
1156   char *s;
1157   int msgcount = 0;
1158   int connflags = 0;
1159   IMAP_MBOX mx;
1160   int rc;
1161
1162   if (imap_parse_path (path, &mx))
1163     return -1;
1164
1165   /* If imap_passive is set, don't open a connection to check for new mail */
1166   if (option (OPTIMAPPASSIVE))
1167     connflags = M_IMAP_CONN_NONEW;
1168
1169   if (!(idata = imap_conn_find (&(mx.account), connflags))) {
1170     FREE (&mx.mbox);
1171     return -1;
1172   }
1173   conn = idata->conn;
1174
1175   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1176   FREE (&mx.mbox);
1177
1178   imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1179   strfcpy (mbox_unquoted, buf, sizeof (mbox_unquoted));
1180
1181   /* The draft IMAP implementor's guide warns againts using the STATUS
1182    * command on a mailbox that you have selected 
1183    */
1184
1185   if (mutt_strcmp (mbox_unquoted, idata->mailbox) == 0
1186       || (ascii_strcasecmp (mbox_unquoted, "INBOX") == 0
1187           && mutt_strcasecmp (mbox_unquoted, idata->mailbox) == 0)) {
1188     strfcpy (buf, "NOOP", sizeof (buf));
1189   }
1190   else if (mutt_bit_isset (idata->capabilities, IMAP4REV1) ||
1191            mutt_bit_isset (idata->capabilities, STATUS)) {
1192     snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
1193               new ? "RECENT" : "MESSAGES");
1194   }
1195   else
1196     /* Server does not support STATUS, and this is not the current mailbox.
1197      * There is no lightweight way to check recent arrivals */
1198     return -1;
1199
1200   imap_cmd_start (idata, buf);
1201
1202   do {
1203     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1204       break;
1205
1206     s = imap_next_word (idata->cmd.buf);
1207     if (ascii_strncasecmp ("STATUS", s, 6) == 0) {
1208       s = imap_next_word (s);
1209       /* The mailbox name may or may not be quoted here. We could try to 
1210        * munge the server response and compare with quoted (or vise versa)
1211        * but it is probably more efficient to just strncmp against both. */
1212       if (mutt_strncmp (mbox_unquoted, s, mutt_strlen (mbox_unquoted)) == 0
1213           || mutt_strncmp (mbox, s, mutt_strlen (mbox)) == 0) {
1214         s = imap_next_word (s);
1215         s = imap_next_word (s);
1216         if (isdigit ((unsigned char) *s)) {
1217           if (*s != '0') {
1218             msgcount = atoi (s);
1219             dprint (2,
1220                     (debugfile, "%d new messages in %s\n", msgcount, path));
1221           }
1222         }
1223       }
1224       else
1225         dprint (1,
1226                 (debugfile,
1227                  "imap_mailbox_check: STATUS response doesn't match requested mailbox.\n"));
1228     }
1229   }
1230   while (rc == IMAP_CMD_CONTINUE);
1231
1232   return msgcount;
1233 }
1234
1235 /* all this listing/browsing is a mess. I don't like that name is a pointer
1236  *   into idata->buf (used to be a pointer into the passed in buffer, just
1237  *   as bad), nor do I like the fact that the fetch is done here. This
1238  *   code can't possibly handle non-LIST untagged responses properly.
1239  *   FIXME. ?! */
1240 int imap_parse_list_response (IMAP_DATA * idata, char **name, int *noselect,
1241                               int *noinferiors, char *delim)
1242 {
1243   char *s;
1244   long bytes;
1245   int rc;
1246
1247   *name = NULL;
1248
1249   rc = imap_cmd_step (idata);
1250   if (rc == IMAP_CMD_OK)
1251     return 0;
1252   if (rc != IMAP_CMD_CONTINUE)
1253     return -1;
1254
1255   s = imap_next_word (idata->cmd.buf);
1256   if ((ascii_strncasecmp ("LIST", s, 4) == 0) ||
1257       (ascii_strncasecmp ("LSUB", s, 4) == 0)) {
1258     *noselect = 0;
1259     *noinferiors = 0;
1260
1261     s = imap_next_word (s);     /* flags */
1262     if (*s == '(') {
1263       char *ep;
1264
1265       s++;
1266       ep = s;
1267       while (*ep && *ep != ')')
1268         ep++;
1269       do {
1270         if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1271           *noselect = 1;
1272         if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1273           *noinferiors = 1;
1274         /* See draft-gahrns-imap-child-mailbox-?? */
1275         if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1276           *noinferiors = 1;
1277         if (*s != ')')
1278           s++;
1279         while (*s && *s != '\\' && *s != ')')
1280           s++;
1281       } while (s != ep);
1282     }
1283     else
1284       return 0;
1285     s = imap_next_word (s);     /* delim */
1286     /* Reset the delimiter, this can change */
1287     if (ascii_strncasecmp (s, "NIL", 3)) {
1288       if (s && s[0] == '\"' && s[1] && s[2] == '\"')
1289         *delim = s[1];
1290       else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
1291                && s[3] == '\"')
1292         *delim = s[2];
1293     }
1294     s = imap_next_word (s);     /* name */
1295     if (s && *s == '{') {       /* Literal */
1296       if (imap_get_literal_count (idata->cmd.buf, &bytes) < 0)
1297         return -1;
1298       if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1299         return -1;
1300       *name = idata->cmd.buf;
1301     }
1302     else
1303       *name = s;
1304   }
1305
1306   return 0;
1307 }
1308
1309 int imap_subscribe (char *path, int subscribe)
1310 {
1311   CONNECTION *conn;
1312   IMAP_DATA *idata;
1313   char buf[LONG_STRING];
1314   char mbox[LONG_STRING];
1315   IMAP_MBOX mx;
1316
1317   if (!mx_is_imap (path) || imap_parse_path (path, &mx)) {
1318     mutt_error (_("Bad mailbox name"));
1319     return -1;
1320   }
1321
1322
1323   if (!(idata = imap_conn_find (&(mx.account), 0)))
1324     goto fail;
1325
1326   conn = idata->conn;
1327
1328   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1329   if (subscribe)
1330     mutt_message (_("Subscribing to %s..."), buf);
1331   else
1332     mutt_message (_("Unsubscribing to %s..."), buf);
1333   imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1334
1335   snprintf (buf, sizeof (buf), "%s %s", subscribe ? "SUBSCRIBE" :
1336             "UNSUBSCRIBE", mbox);
1337
1338   if (imap_exec (idata, buf, 0) < 0)
1339     goto fail;
1340
1341   FREE (&mx.mbox);
1342   return 0;
1343
1344 fail:
1345   FREE (&mx.mbox);
1346   return -1;
1347 }
1348
1349 /* imap_complete: given a partial IMAP folder path, return a string which
1350  *   adds as much to the path as is unique */
1351 int imap_complete (char *dest, size_t dlen, char *path)
1352 {
1353   CONNECTION *conn;
1354   IMAP_DATA *idata;
1355   char list[LONG_STRING];
1356   char buf[LONG_STRING];
1357   char *list_word = NULL;
1358   int noselect, noinferiors;
1359   char delim;
1360   char completion[LONG_STRING];
1361   int clen, matchlen = 0;
1362   int completions = 0;
1363   int pos = 0;
1364   IMAP_MBOX mx;
1365
1366   /* verify passed in path is an IMAP path */
1367   if (imap_parse_path (path, &mx)) {
1368     dprint (2, (debugfile, "imap_complete: bad path %s\n", path));
1369     return -1;
1370   }
1371
1372   /* don't open a new socket just for completion */
1373   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW)))
1374     goto fail;
1375   conn = idata->conn;
1376
1377   /* reformat path for IMAP list, and append wildcard */
1378   /* don't use INBOX in place of "" */
1379   if (mx.mbox && mx.mbox[0])
1380     imap_fix_path (idata, mx.mbox, list, sizeof (list));
1381   else
1382     list[0] = '\0';
1383
1384   /* fire off command */
1385   snprintf (buf, sizeof (buf), "%s \"\" \"%s%%\"",
1386             option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
1387
1388   imap_cmd_start (idata, buf);
1389
1390   /* and see what the results are */
1391   strfcpy (completion, NONULL (mx.mbox), sizeof (completion));
1392   do {
1393     if (imap_parse_list_response (idata, &list_word, &noselect, &noinferiors,
1394                                   &delim))
1395       break;
1396
1397     if (list_word) {
1398       /* store unquoted */
1399       imap_unmunge_mbox_name (list_word);
1400
1401       /* if the folder isn't selectable, append delimiter to force browse
1402        * to enter it on second tab. */
1403       if (noselect) {
1404         clen = strlen (list_word);
1405         list_word[clen++] = delim;
1406         list_word[clen] = '\0';
1407       }
1408       /* copy in first word */
1409       if (!completions) {
1410         strfcpy (completion, list_word, sizeof (completion));
1411         matchlen = strlen (completion);
1412         completions++;
1413         continue;
1414       }
1415
1416       pos = 0;
1417       while (pos < matchlen && list_word[pos] &&
1418              completion[pos] == list_word[pos])
1419         pos++;
1420       completion[pos] = '\0';
1421       matchlen = pos;
1422
1423       completions++;
1424     }
1425   }
1426   while (ascii_strncmp (idata->cmd.seq, idata->cmd.buf, SEQLEN));
1427
1428   if (completions) {
1429     /* reformat output */
1430     imap_qualify_path (dest, dlen, &mx, completion);
1431     mutt_pretty_mailbox (dest);
1432
1433     FREE (&mx.mbox);
1434     return 0;
1435   }
1436
1437 fail:
1438   FREE (&mx.mbox);
1439   return -1;
1440 }
1441
1442 /* reconnect if connection was lost */
1443 int imap_reconnect (CONTEXT * ctx)
1444 {
1445   IMAP_DATA *imap_data = (IMAP_DATA *) ctx->data;
1446
1447   if (imap_data) {
1448     if (imap_data->status == IMAP_CONNECTED)
1449       return -1;
1450   }
1451
1452   if (query_quadoption
1453       (OPT_IMAPRECONNECT,
1454        _("Connection lost. Reconnect to IMAP server?")) != M_YES)
1455     return -1;
1456
1457   mx_open_mailbox (ctx->path, 0, ctx);
1458   return 0;
1459 }