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