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