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