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