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