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