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