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