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