]> git.llucax.com Git - software/mutt-debian.git/blob - imap/imap.c
removing patches that were applied usptream into 1.5.21
[software/mutt-debian.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-2009 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 "mx.h"
29 #include "mailbox.h"
30 #include "globals.h"
31 #include "sort.h"
32 #include "browser.h"
33 #include "message.h"
34 #include "imap_private.h"
35 #if defined(USE_SSL)
36 # include "mutt_ssl.h"
37 #endif
38 #include "buffy.h"
39 #if USE_HCACHE
40 #include "hcache.h"
41 #endif
42
43 #include <unistd.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49
50 /* imap forward declarations */
51 static char* imap_get_flags (LIST** hflags, char* s);
52 static int imap_check_capabilities (IMAP_DATA* idata);
53 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
54                            const char* str, char* flags, size_t flsize);
55
56 /* imap_access: Check permissions on an IMAP mailbox.
57  * TODO: ACL checks. Right now we assume if it exists we can
58  *       mess with it. */
59 int imap_access (const char* path, int flags)
60 {
61   IMAP_DATA* idata;
62   IMAP_MBOX mx;
63   char buf[LONG_STRING];
64   char mailbox[LONG_STRING];
65   char mbox[LONG_STRING];
66   int rc;
67
68   if (imap_parse_path (path, &mx))
69     return -1;
70
71   if (!(idata = imap_conn_find (&mx.account,
72     option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
73   {
74     FREE (&mx.mbox);
75     return -1;
76   }
77
78   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
79   if (!*mailbox)
80     strfcpy (mailbox, "INBOX", sizeof (mailbox));
81
82   /* we may already be in the folder we're checking */
83   if (!ascii_strcmp(idata->mailbox, mx.mbox))
84   {
85     FREE (&mx.mbox);
86     return 0;
87   }
88   FREE (&mx.mbox);
89
90   if (imap_mboxcache_get (idata, mailbox, 0))
91   {
92     dprint (3, (debugfile, "imap_access: found %s in cache\n", mailbox));
93     return 0;
94   }
95
96   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
97
98   if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
99     snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
100   else if (mutt_bit_isset (idata->capabilities, STATUS))
101     snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
102   else
103   {
104     dprint (2, (debugfile, "imap_access: STATUS not supported?\n"));
105     return -1;
106   }
107
108   if ((rc = imap_exec (idata, buf, IMAP_CMD_FAIL_OK)) < 0)
109   {
110     dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox));
111     return rc;
112   }
113
114   return 0;
115 }
116
117 int imap_create_mailbox (IMAP_DATA* idata, char* mailbox)
118 {
119   char buf[LONG_STRING], mbox[LONG_STRING];
120
121   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
122   snprintf (buf, sizeof (buf), "CREATE %s", mbox);
123
124   if (imap_exec (idata, buf, 0) != 0)
125   {
126     mutt_error (_("CREATE failed: %s"), imap_cmd_trailer (idata));
127     return -1;
128   }
129
130   return 0;
131 }
132
133 int imap_rename_mailbox (IMAP_DATA* idata, IMAP_MBOX* mx, const char* newname)
134 {
135   char oldmbox[LONG_STRING];
136   char newmbox[LONG_STRING];
137   char buf[LONG_STRING];
138
139   imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox);
140   imap_munge_mbox_name (newmbox, sizeof (newmbox), newname);
141
142   snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox);
143
144   if (imap_exec (idata, buf, 0) != 0)
145     return -1;
146
147   return 0;
148 }
149
150 int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx)
151 {
152   char buf[LONG_STRING], mbox[LONG_STRING];
153   IMAP_DATA *idata;
154
155   if (!ctx || !ctx->data) {
156     if (!(idata = imap_conn_find (&mx.account,
157           option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
158     {
159       FREE (&mx.mbox);
160       return -1;
161     }
162   } else {
163     idata = ctx->data;
164   }
165
166   imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
167   snprintf (buf, sizeof (buf), "DELETE %s", mbox);
168
169   if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0)
170     return -1;
171
172   return 0;
173 }
174
175 /* imap_logout_all: close all open connections. Quick and dirty until we can
176  *   make sure we've got all the context we need. */
177 void imap_logout_all (void)
178 {
179   CONNECTION* conn;
180   CONNECTION* tmp;
181
182   conn = mutt_socket_head ();
183
184   while (conn)
185   {
186     tmp = conn->next;
187
188     if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0)
189     {
190       mutt_message (_("Closing connection to %s..."), conn->account.host);
191       imap_logout ((IMAP_DATA**) (void*) &conn->data);
192       mutt_clear_error ();
193       mutt_socket_free (conn);
194     }
195
196     conn = tmp;
197   }
198 }
199
200 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
201  *   buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
202  *   Apparently even literals use \r\n-terminated strings ?! */
203 int imap_read_literal (FILE* fp, IMAP_DATA* idata, long bytes, progress_t* pbar)
204 {
205   long pos;
206   char c;
207
208   int r = 0;
209
210   dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes));
211
212   for (pos = 0; pos < bytes; pos++)
213   {
214     if (mutt_socket_readchar (idata->conn, &c) != 1)
215     {
216       dprint (1, (debugfile, "imap_read_literal: error during read, %ld bytes read\n", pos));
217       idata->status = IMAP_FATAL;
218
219       return -1;
220     }
221
222 #if 1
223     if (r == 1 && c != '\n')
224       fputc ('\r', fp);
225
226     if (c == '\r')
227     {
228       r = 1;
229       continue;
230     }
231     else
232       r = 0;
233 #endif
234     fputc (c, fp);
235
236     if (pbar && !(pos % 1024))
237       mutt_progress_update (pbar, pos, -1);
238 #ifdef DEBUG
239     if (debuglevel >= IMAP_LOG_LTRL)
240       fputc (c, debugfile);
241 #endif
242   }
243
244   return 0;
245 }
246
247 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
248  *   context. Must not be done while something has a handle on any headers
249  *   (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
250 void imap_expunge_mailbox (IMAP_DATA* idata)
251 {
252   HEADER* h;
253   int i, cacheno;
254
255 #ifdef USE_HCACHE
256   idata->hcache = imap_hcache_open (idata, NULL);
257 #endif
258
259   for (i = 0; i < idata->ctx->msgcount; i++)
260   {
261     h = idata->ctx->hdrs[i];
262
263     if (h->index == -1)
264     {
265       dprint (2, (debugfile, "Expunging message UID %d.\n", HEADER_DATA (h)->uid));
266
267       h->active = 0;
268       idata->ctx->size -= h->content->length;
269
270       imap_cache_del (idata, h);
271 #if USE_HCACHE
272       imap_hcache_del (idata, HEADER_DATA(h)->uid);
273 #endif
274
275       /* free cached body from disk, if necessary */
276       cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
277       if (idata->cache[cacheno].uid == HEADER_DATA(h)->uid &&
278           idata->cache[cacheno].path)
279       {
280         unlink (idata->cache[cacheno].path);
281         FREE (&idata->cache[cacheno].path);
282       }
283
284       imap_free_header_data (&h->data);
285     }
286   }
287
288 #if USE_HCACHE
289   imap_hcache_close (idata);
290 #endif
291
292   /* We may be called on to expunge at any time. We can't rely on the caller
293    * to always know to rethread */
294   mx_update_tables (idata->ctx, 0);
295   mutt_sort_headers (idata->ctx, 1);
296 }
297
298 /* imap_check_capabilities: make sure we can log in to this server. */
299 static int imap_check_capabilities (IMAP_DATA* idata)
300 {
301   if (imap_exec (idata, "CAPABILITY", 0) != 0)
302   {
303     imap_error ("imap_check_capabilities", idata->buf);
304     return -1;
305   }
306
307   if (!(mutt_bit_isset(idata->capabilities,IMAP4)
308       ||mutt_bit_isset(idata->capabilities,IMAP4REV1)))
309   {
310     mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
311     mutt_sleep (2);     /* pause a moment to let the user see the error */
312
313     return -1;
314   }
315
316   return 0;
317 }
318
319 /* imap_conn_find: Find an open IMAP connection matching account, or open
320  *   a new one if none can be found. */
321 IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags)
322 {
323   CONNECTION* conn = NULL;
324   ACCOUNT* creds = NULL;
325   IMAP_DATA* idata = NULL;
326   int new = 0;
327
328   while ((conn = mutt_conn_find (conn, account)))
329   {
330     if (!creds)
331       creds = &conn->account;
332     else
333       memcpy (&conn->account, creds, sizeof (ACCOUNT));
334
335     idata = (IMAP_DATA*)conn->data;
336     if (flags & M_IMAP_CONN_NONEW)
337     {
338       if (!idata)
339       {
340         /* This should only happen if we've come to the end of the list */
341         mutt_socket_free (conn);
342         return NULL;
343       }
344       else if (idata->state < IMAP_AUTHENTICATED)
345         continue;
346     }
347     if (flags & M_IMAP_CONN_NOSELECT && idata && idata->state >= IMAP_SELECTED)
348       continue;
349     if (idata && idata->status == IMAP_FATAL)
350       continue;
351     break;
352   }
353   if (!conn)
354     return NULL; /* this happens when the initial connection fails */
355
356   if (!idata)
357   {
358     /* The current connection is a new connection */
359     if (! (idata = imap_new_idata ()))
360     {
361       mutt_socket_free (conn);
362       return NULL;
363     }
364
365     conn->data = idata;
366     idata->conn = conn;
367     new = 1;
368   }
369
370   if (idata->state == IMAP_DISCONNECTED)
371     imap_open_connection (idata);
372   if (idata->state == IMAP_CONNECTED)
373   {
374     if (!imap_authenticate (idata))
375     {
376       idata->state = IMAP_AUTHENTICATED;
377       new = 1;
378       if (idata->conn->ssf)
379         dprint (2, (debugfile, "Communication encrypted at %d bits\n",
380                     idata->conn->ssf));
381     }
382     else
383       mutt_account_unsetpass (&idata->conn->account);
384
385     FREE (&idata->capstr);
386   }
387   if (new && idata->state == IMAP_AUTHENTICATED)
388   {
389     /* capabilities may have changed */
390     imap_exec (idata, "CAPABILITY", IMAP_CMD_QUEUE);
391     /* get root delimiter, '/' as default */
392     idata->delim = '/';
393     imap_exec (idata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
394     if (option (OPTIMAPCHECKSUBSCRIBED))
395       imap_exec (idata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
396     /* we may need the root delimiter before we open a mailbox */
397     imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
398   }
399
400   return idata;
401 }
402
403 int imap_open_connection (IMAP_DATA* idata)
404 {
405   char buf[LONG_STRING];
406
407   if (mutt_socket_open (idata->conn) < 0)
408     return -1;
409
410   idata->state = IMAP_CONNECTED;
411
412   if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
413   {
414     imap_close_connection (idata);
415     return -1;
416   }
417
418   if (ascii_strncasecmp ("* OK", idata->buf, 4) == 0)
419   {
420     if (ascii_strncasecmp ("* OK [CAPABILITY", idata->buf, 16)
421         && imap_check_capabilities (idata))
422       goto bail;
423 #if defined(USE_SSL)
424     /* Attempt STARTTLS if available and desired. */
425     if (!idata->conn->ssf && (option(OPTSSLFORCETLS) ||
426                               mutt_bit_isset (idata->capabilities, STARTTLS)))
427     {
428       int rc;
429
430       if (option(OPTSSLFORCETLS))
431         rc = M_YES;
432       else if ((rc = query_quadoption (OPT_SSLSTARTTLS,
433         _("Secure connection with TLS?"))) == -1)
434         goto err_close_conn;
435       if (rc == M_YES) {
436         if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
437           goto bail;
438         if (rc != -2)
439         {
440           if (mutt_ssl_starttls (idata->conn))
441           {
442             mutt_error (_("Could not negotiate TLS connection"));
443             mutt_sleep (1);
444             goto err_close_conn;
445           }
446           else
447           {
448             /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
449             if (imap_exec (idata, "CAPABILITY", 0))
450               goto bail;
451           }
452         }
453       }
454     }
455
456     if (option(OPTSSLFORCETLS) && ! idata->conn->ssf)
457     {
458       mutt_error _("Encrypted connection unavailable");
459       mutt_sleep (1);
460       goto err_close_conn;
461     }
462 #endif
463   }
464   else if (ascii_strncasecmp ("* PREAUTH", idata->buf, 9) == 0)
465   {
466     idata->state = IMAP_AUTHENTICATED;
467     if (imap_check_capabilities (idata) != 0)
468       goto bail;
469     FREE (&idata->capstr);
470   }
471   else
472   {
473     imap_error ("imap_open_connection()", buf);
474     goto bail;
475   }
476
477   return 0;
478
479 #if defined(USE_SSL)
480  err_close_conn:
481   imap_close_connection (idata);
482 #endif
483  bail:
484   FREE (&idata->capstr);
485   return -1;
486 }
487
488 void imap_close_connection(IMAP_DATA* idata)
489 {
490   if (idata->state != IMAP_DISCONNECTED)
491   {
492     mutt_socket_close (idata->conn);
493     idata->state = IMAP_DISCONNECTED;
494   }
495   idata->seqno = idata->nextcmd = idata->lastcmd = idata->status = 0;
496   memset (idata->cmds, 0, sizeof (IMAP_COMMAND) * idata->cmdslots);
497 }
498
499 /* imap_get_flags: Make a simple list out of a FLAGS response.
500  *   return stream following FLAGS response */
501 static char* imap_get_flags (LIST** hflags, char* s)
502 {
503   LIST* flags;
504   char* flag_word;
505   char ctmp;
506
507   /* sanity-check string */
508   if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
509   {
510     dprint (1, (debugfile, "imap_get_flags: not a FLAGS response: %s\n",
511       s));
512     return NULL;
513   }
514   s += 5;
515   SKIPWS(s);
516   if (*s != '(')
517   {
518     dprint (1, (debugfile, "imap_get_flags: bogus FLAGS response: %s\n",
519       s));
520     return NULL;
521   }
522
523   /* create list, update caller's flags handle */
524   flags = mutt_new_list();
525   *hflags = flags;
526
527   while (*s && *s != ')')
528   {
529     s++;
530     SKIPWS(s);
531     flag_word = s;
532     while (*s && (*s != ')') && !ISSPACE (*s))
533       s++;
534     ctmp = *s;
535     *s = '\0';
536     if (*flag_word)
537       mutt_add_list (flags, flag_word);
538     *s = ctmp;
539   }
540
541   /* note bad flags response */
542   if (*s != ')')
543   {
544     dprint (1, (debugfile,
545       "imap_get_flags: Unterminated FLAGS response: %s\n", s));
546     mutt_free_list (hflags);
547
548     return NULL;
549   }
550
551   s++;
552
553   return s;
554 }
555
556 int imap_open_mailbox (CONTEXT* ctx)
557 {
558   CONNECTION *conn;
559   IMAP_DATA *idata;
560   IMAP_STATUS* status;
561   char buf[LONG_STRING];
562   char bufout[LONG_STRING];
563   int count = 0;
564   IMAP_MBOX mx, pmx;
565   int rc;
566
567   if (imap_parse_path (ctx->path, &mx))
568   {
569     mutt_error (_("%s is an invalid IMAP path"), ctx->path);
570     return -1;
571   }
572
573   /* we require a connection which isn't currently in IMAP_SELECTED state */
574   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
575     goto fail_noidata;
576   if (idata->state < IMAP_AUTHENTICATED)
577     goto fail;
578
579   conn = idata->conn;
580
581   /* once again the context is new */
582   ctx->data = idata;
583   ctx->mx_close = imap_close_mailbox;
584
585   /* Clean up path and replace the one in the ctx */
586   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
587   if (!*buf)
588     strfcpy (buf, "INBOX", sizeof (buf));
589   FREE(&(idata->mailbox));
590   idata->mailbox = safe_strdup (buf);
591   imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
592
593   FREE (&(ctx->path));
594   ctx->path = safe_strdup (buf);
595
596   idata->ctx = ctx;
597
598   /* clear mailbox status */
599   idata->status = 0;
600   memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
601   idata->newMailCount = 0;
602
603   mutt_message (_("Selecting %s..."), idata->mailbox);
604   imap_munge_mbox_name (buf, sizeof(buf), idata->mailbox);
605
606   /* pipeline ACL test */
607   if (mutt_bit_isset (idata->capabilities, ACL))
608   {
609     snprintf (bufout, sizeof (bufout), "MYRIGHTS %s", buf);
610     imap_exec (idata, bufout, IMAP_CMD_QUEUE);
611   }
612   /* assume we have all rights if ACL is unavailable */
613   else
614   {
615     mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
616     mutt_bit_set (idata->ctx->rights, M_ACL_READ);
617     mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
618     mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
619     mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
620     mutt_bit_set (idata->ctx->rights, M_ACL_POST);
621     mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
622     mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
623   }
624   /* pipeline the postponed count if possible */
625   pmx.mbox = NULL;
626   if (mx_is_imap (Postponed) && !imap_parse_path (Postponed, &pmx)
627       && mutt_account_match (&pmx.account, &mx.account))
628     imap_status (Postponed, 1);
629   FREE (&pmx.mbox);
630
631   snprintf (bufout, sizeof (bufout), "%s %s",
632     ctx->readonly ? "EXAMINE" : "SELECT", buf);
633
634   idata->state = IMAP_SELECTED;
635
636   imap_cmd_start (idata, bufout);
637
638   status = imap_mboxcache_get (idata, idata->mailbox, 1);
639
640   do
641   {
642     char *pc;
643
644     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
645       break;
646
647     pc = idata->buf + 2;
648
649     /* Obtain list of available flags here, may be overridden by a
650      * PERMANENTFLAGS tag in the OK response */
651     if (ascii_strncasecmp ("FLAGS", pc, 5) == 0)
652     {
653       /* don't override PERMANENTFLAGS */
654       if (!idata->flags)
655       {
656         dprint (3, (debugfile, "Getting mailbox FLAGS\n"));
657         if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
658           goto fail;
659       }
660     }
661     /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
662     else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0)
663     {
664       dprint (3, (debugfile, "Getting mailbox PERMANENTFLAGS\n"));
665       /* safe to call on NULL */
666       mutt_free_list (&(idata->flags));
667       /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
668       pc += 13;
669       if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
670         goto fail;
671     }
672     /* save UIDVALIDITY for the header cache */
673     else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0)
674     {
675       dprint (3, (debugfile, "Getting mailbox UIDVALIDITY\n"));
676       pc += 3;
677       pc = imap_next_word (pc);
678       idata->uid_validity = strtol (pc, NULL, 10);
679       status->uidvalidity = idata->uid_validity;
680     }
681     else if (ascii_strncasecmp ("OK [UIDNEXT", pc, 11) == 0)
682     {
683       dprint (3, (debugfile, "Getting mailbox UIDNEXT\n"));
684       pc += 3;
685       pc = imap_next_word (pc);
686       idata->uidnext = strtol (pc, NULL, 10);
687       status->uidnext = idata->uidnext;
688     }
689     else
690     {
691       pc = imap_next_word (pc);
692       if (!ascii_strncasecmp ("EXISTS", pc, 6))
693       {
694         count = idata->newMailCount;
695         idata->newMailCount = 0;
696       }
697     }
698   }
699   while (rc == IMAP_CMD_CONTINUE);
700
701   if (rc == IMAP_CMD_NO)
702   {
703     char *s;
704     s = imap_next_word (idata->buf); /* skip seq */
705     s = imap_next_word (s); /* Skip response */
706     mutt_error ("%s", s);
707     mutt_sleep (2);
708     goto fail;
709   }
710
711   if (rc != IMAP_CMD_OK)
712     goto fail;
713
714   /* check for READ-ONLY notification */
715   if (!ascii_strncasecmp (imap_get_qualifier (idata->buf), "[READ-ONLY]", 11)  \
716   && !mutt_bit_isset (idata->capabilities, ACL))
717   {
718     dprint (2, (debugfile, "Mailbox is read-only.\n"));
719     ctx->readonly = 1;
720   }
721
722 #ifdef DEBUG
723   /* dump the mailbox flags we've found */
724   if (debuglevel > 2)
725   {
726     if (!idata->flags)
727       dprint (3, (debugfile, "No folder flags found\n"));
728     else
729     {
730       LIST* t = idata->flags;
731
732       dprint (3, (debugfile, "Mailbox flags: "));
733
734       t = t->next;
735       while (t)
736       {
737         dprint (3, (debugfile, "[%s] ", t->data));
738         t = t->next;
739       }
740       dprint (3, (debugfile, "\n"));
741     }
742   }
743 #endif
744
745   if (!(mutt_bit_isset(idata->ctx->rights, M_ACL_DELETE) ||
746         mutt_bit_isset(idata->ctx->rights, M_ACL_SEEN) ||
747         mutt_bit_isset(idata->ctx->rights, M_ACL_WRITE) ||
748         mutt_bit_isset(idata->ctx->rights, M_ACL_INSERT)))
749      ctx->readonly = 1;
750
751   ctx->hdrmax = count;
752   ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
753   ctx->v2r = safe_calloc (count, sizeof (int));
754   ctx->msgcount = 0;
755
756   if (count && (imap_read_headers (idata, 0, count-1) < 0))
757   {
758     mutt_error _("Error opening mailbox");
759     mutt_sleep (1);
760     goto fail;
761   }
762
763   dprint (2, (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount));
764   FREE (&mx.mbox);
765   return 0;
766
767  fail:
768   if (idata->state == IMAP_SELECTED)
769     idata->state = IMAP_AUTHENTICATED;
770  fail_noidata:
771   FREE (&mx.mbox);
772   return -1;
773 }
774
775 int imap_open_mailbox_append (CONTEXT *ctx)
776 {
777   CONNECTION *conn;
778   IMAP_DATA *idata;
779   char buf[LONG_STRING];
780   char mailbox[LONG_STRING];
781   IMAP_MBOX mx;
782   int rc;
783
784   if (imap_parse_path (ctx->path, &mx))
785     return -1;
786
787   /* in APPEND mode, we appear to hijack an existing IMAP connection -
788    * ctx is brand new and mostly empty */
789
790   if (!(idata = imap_conn_find (&(mx.account), 0)))
791   {
792     FREE (&mx.mbox);
793     return -1;
794   }
795
796   conn = idata->conn;
797
798   ctx->magic = M_IMAP;
799   ctx->data = idata;
800
801   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
802   if (!*mailbox)
803     strfcpy (mailbox, "INBOX", sizeof (mailbox));
804   FREE (&mx.mbox);
805
806   /* really we should also check for W_OK */
807   if ((rc = imap_access (ctx->path, F_OK)) == 0)
808     return 0;
809
810   if (rc == -1)
811     return -1;
812
813   snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
814   if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
815     return -1;
816
817   if (imap_create_mailbox (idata, mailbox) < 0)
818     return -1;
819
820   return 0;
821 }
822
823 /* imap_logout: Gracefully log out of server. */
824 void imap_logout (IMAP_DATA** idata)
825 {
826   /* we set status here to let imap_handle_untagged know we _expect_ to
827    * receive a bye response (so it doesn't freak out and close the conn) */
828   (*idata)->status = IMAP_BYE;
829   imap_cmd_start (*idata, "LOGOUT");
830   while (imap_cmd_step (*idata) == IMAP_CMD_CONTINUE)
831     ;
832
833   mutt_socket_close ((*idata)->conn);
834   imap_free_idata (idata);
835 }
836
837 /* imap_set_flag: append str to flags if we currently have permission
838  *   according to aclbit */
839 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
840   const char *str, char *flags, size_t flsize)
841 {
842   if (mutt_bit_isset (idata->ctx->rights, aclbit))
843     if (flag && imap_has_flag (idata->flags, str))
844       safe_strcat (flags, flsize, str);
845 }
846
847 /* imap_has_flag: do a caseless comparison of the flag against a flag list,
848 *   return 1 if found or flag list has '\*', 0 otherwise */
849 int imap_has_flag (LIST* flag_list, const char* flag)
850 {
851   if (!flag_list)
852     return 0;
853
854   flag_list = flag_list->next;
855   while (flag_list)
856   {
857     if (!ascii_strncasecmp (flag_list->data, flag, strlen (flag_list->data)))
858       return 1;
859
860     if (!ascii_strncmp (flag_list->data, "\\*", strlen (flag_list->data)))
861       return 1;
862
863     flag_list = flag_list->next;
864   }
865
866   return 0;
867 }
868
869 /* Note: headers must be in SORT_ORDER. See imap_exec_msgset for args.
870  * Pos is an opaque pointer a la strtok. It should be 0 at first call. */
871 static int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag,
872                               int changed, int invert, int* pos)
873 {
874   HEADER** hdrs = idata->ctx->hdrs;
875   int count = 0;        /* number of messages in message set */
876   int match = 0;        /* whether current message matches flag condition */
877   unsigned int setstart = 0;    /* start of current message range */
878   int n;
879   int started = 0;
880
881   hdrs = idata->ctx->hdrs;
882
883   for (n = *pos;
884        n < idata->ctx->msgcount && buf->dptr - buf->data < IMAP_MAX_CMDLEN;
885        n++)
886   {
887     match = 0;
888     /* don't include pending expunged messages */
889     if (hdrs[n]->active)
890       switch (flag)
891       {
892         case M_DELETED:
893           if (hdrs[n]->deleted != HEADER_DATA(hdrs[n])->deleted)
894             match = invert ^ hdrs[n]->deleted;
895           break;
896         case M_FLAG:
897           if (hdrs[n]->flagged != HEADER_DATA(hdrs[n])->flagged)
898             match = invert ^ hdrs[n]->flagged;
899           break;
900         case M_OLD:
901           if (hdrs[n]->old != HEADER_DATA(hdrs[n])->old)
902             match = invert ^ hdrs[n]->old;
903           break;
904         case M_READ:
905           if (hdrs[n]->read != HEADER_DATA(hdrs[n])->read)
906             match = invert ^ hdrs[n]->read;
907           break;
908         case M_REPLIED:
909           if (hdrs[n]->replied != HEADER_DATA(hdrs[n])->replied)
910             match = invert ^ hdrs[n]->replied;
911           break;
912
913         case M_TAG:
914           if (hdrs[n]->tagged)
915             match = 1;
916           break;
917       }
918
919     if (match && (!changed || hdrs[n]->changed))
920     {
921       count++;
922       if (setstart == 0)
923       {
924         setstart = HEADER_DATA (hdrs[n])->uid;
925         if (started == 0)
926         {
927           mutt_buffer_printf (buf, "%u", HEADER_DATA (hdrs[n])->uid);
928           started = 1;
929         }
930         else
931           mutt_buffer_printf (buf, ",%u", HEADER_DATA (hdrs[n])->uid);
932       }
933       /* tie up if the last message also matches */
934       else if (n == idata->ctx->msgcount-1)
935         mutt_buffer_printf (buf, ":%u", HEADER_DATA (hdrs[n])->uid);
936     }
937     /* End current set if message doesn't match or we've reached the end
938      * of the mailbox via inactive messages following the last match. */
939     else if (setstart && (hdrs[n]->active || n == idata->ctx->msgcount-1))
940     {
941       if (HEADER_DATA (hdrs[n-1])->uid > setstart)
942         mutt_buffer_printf (buf, ":%u", HEADER_DATA (hdrs[n-1])->uid);
943       setstart = 0;
944     }
945   }
946
947   *pos = n;
948
949   return count;
950 }
951
952 /* Prepares commands for all messages matching conditions (must be flushed
953  * with imap_exec)
954  * Params:
955  *   idata: IMAP_DATA containing context containing header set
956  *   pre, post: commands are of the form "%s %s %s %s", tag,
957  *     pre, message set, post
958  *   flag: enum of flag type on which to filter
959  *   changed: include only changed messages in message set
960  *   invert: invert sense of flag, eg M_READ matches unread messages
961  * Returns: number of matched messages, or -1 on failure */
962 int imap_exec_msgset (IMAP_DATA* idata, const char* pre, const char* post,
963                       int flag, int changed, int invert)
964 {
965   HEADER** hdrs = NULL;
966   short oldsort;
967   BUFFER* cmd;
968   int pos;
969   int rc;
970   int count = 0;
971
972   if (! (cmd = mutt_buffer_init (NULL)))
973   {
974     dprint (1, (debugfile, "imap_exec_msgset: unable to allocate buffer\n"));
975     return -1;
976   }
977
978   /* We make a copy of the headers just in case resorting doesn't give
979    exactly the original order (duplicate messages?), because other parts of
980    the ctx are tied to the header order. This may be overkill. */
981   oldsort = Sort;
982   if (Sort != SORT_ORDER)
983   {
984     hdrs = idata->ctx->hdrs;
985     idata->ctx->hdrs = safe_malloc (idata->ctx->msgcount * sizeof (HEADER*));
986     memcpy (idata->ctx->hdrs, hdrs, idata->ctx->msgcount * sizeof (HEADER*));
987
988     Sort = SORT_ORDER;
989     qsort (idata->ctx->hdrs, idata->ctx->msgcount, sizeof (HEADER*),
990            mutt_get_sort_func (SORT_ORDER));
991   }
992
993   pos = 0;
994
995   do
996   {
997     cmd->dptr = cmd->data;
998     mutt_buffer_printf (cmd, "%s ", pre);
999     rc = imap_make_msg_set (idata, cmd, flag, changed, invert, &pos);
1000     if (rc > 0)
1001     {
1002       mutt_buffer_printf (cmd, " %s", post);
1003       if (imap_exec (idata, cmd->data, IMAP_CMD_QUEUE))
1004       {
1005         rc = -1;
1006         goto out;
1007       }
1008       count += rc;
1009     }
1010   }
1011   while (rc > 0);
1012
1013   rc = count;
1014
1015 out:
1016   mutt_buffer_free (&cmd);
1017   if (oldsort != Sort)
1018   {
1019     Sort = oldsort;
1020     FREE (&idata->ctx->hdrs);
1021     idata->ctx->hdrs = hdrs;
1022   }
1023
1024   return rc;
1025 }
1026
1027 /* returns 0 if mutt's flags match cached server flags */
1028 static int compare_flags (HEADER* h)
1029 {
1030   IMAP_HEADER_DATA* hd = (IMAP_HEADER_DATA*)h->data;
1031
1032   if (h->read != hd->read)
1033     return 1;
1034   if (h->old != hd->old)
1035     return 1;
1036   if (h->flagged != hd->flagged)
1037     return 1;
1038   if (h->replied != hd->replied)
1039     return 1;
1040   if (h->deleted != hd->deleted)
1041     return 1;
1042
1043   return 0;
1044 }
1045
1046 /* Update the IMAP server to reflect the flags a single message.  */
1047 int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd,
1048                        int *err_continue)
1049 {
1050   char flags[LONG_STRING];
1051   char uid[11];
1052
1053   hdr->changed = 0;
1054
1055   if (!compare_flags (hdr))
1056   {
1057     idata->ctx->changed--;
1058     return 0;
1059   }
1060
1061   snprintf (uid, sizeof (uid), "%u", HEADER_DATA(hdr)->uid);
1062   cmd->dptr = cmd->data;
1063   mutt_buffer_addstr (cmd, "UID STORE ");
1064   mutt_buffer_addstr (cmd, uid);
1065
1066   flags[0] = '\0';
1067
1068   imap_set_flag (idata, M_ACL_SEEN, hdr->read, "\\Seen ",
1069                  flags, sizeof (flags));
1070   imap_set_flag (idata, M_ACL_WRITE, hdr->old,
1071                  "Old ", flags, sizeof (flags));
1072   imap_set_flag (idata, M_ACL_WRITE, hdr->flagged,
1073                  "\\Flagged ", flags, sizeof (flags));
1074   imap_set_flag (idata, M_ACL_WRITE, hdr->replied,
1075                  "\\Answered ", flags, sizeof (flags));
1076   imap_set_flag (idata, M_ACL_DELETE, hdr->deleted,
1077                  "\\Deleted ", flags, sizeof (flags));
1078
1079   /* now make sure we don't lose custom tags */
1080   if (mutt_bit_isset (idata->ctx->rights, M_ACL_WRITE))
1081     imap_add_keywords (flags, hdr, idata->flags, sizeof (flags));
1082
1083   mutt_remove_trailing_ws (flags);
1084
1085   /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1086    * explicitly revoke all system flags (if we have permission) */
1087   if (!*flags)
1088   {
1089     imap_set_flag (idata, M_ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
1090     imap_set_flag (idata, M_ACL_WRITE, 1, "Old ", flags, sizeof (flags));
1091     imap_set_flag (idata, M_ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
1092     imap_set_flag (idata, M_ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
1093     imap_set_flag (idata, M_ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
1094
1095     mutt_remove_trailing_ws (flags);
1096
1097     mutt_buffer_addstr (cmd, " -FLAGS.SILENT (");
1098   } else
1099     mutt_buffer_addstr (cmd, " FLAGS.SILENT (");
1100
1101   mutt_buffer_addstr (cmd, flags);
1102   mutt_buffer_addstr (cmd, ")");
1103
1104   /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
1105   hdr->active = 0;
1106
1107   /* after all this it's still possible to have no flags, if you
1108    * have no ACL rights */
1109   if (*flags && (imap_exec (idata, cmd->data, 0) != 0) &&
1110       err_continue && (*err_continue != M_YES))
1111   {
1112     *err_continue = imap_continue ("imap_sync_message: STORE failed",
1113                                    idata->buf);
1114     if (*err_continue != M_YES)
1115       return -1;
1116   }
1117
1118   hdr->active = 1;
1119   idata->ctx->changed--;
1120
1121   return 0;
1122 }
1123
1124 static int sync_helper (IMAP_DATA* idata, int right, int flag, const char* name)
1125 {
1126   int count = 0;
1127   int rc;
1128
1129   char buf[LONG_STRING];
1130
1131   if (!mutt_bit_isset (idata->ctx->rights, right))
1132     return 0;
1133
1134   if (right == M_ACL_WRITE && !imap_has_flag (idata->flags, name))
1135     return 0;
1136
1137   snprintf (buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
1138   if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 0)) < 0)
1139     return rc;
1140   count += rc;
1141
1142   buf[0] = '-';
1143   if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 1)) < 0)
1144     return rc;
1145   count += rc;
1146
1147   return count;
1148 }
1149
1150 /* update the IMAP server to reflect message changes done within mutt.
1151  * Arguments
1152  *   ctx: the current context
1153  *   expunge: 0 or 1 - do expunge?
1154  */
1155 int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint)
1156 {
1157   IMAP_DATA* idata;
1158   CONTEXT* appendctx = NULL;
1159   HEADER* h;
1160   HEADER** hdrs = NULL;
1161   int oldsort;
1162   int n;
1163   int rc;
1164
1165   idata = (IMAP_DATA*) ctx->data;
1166
1167   if (idata->state < IMAP_SELECTED)
1168   {
1169     dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n"));
1170     return -1;
1171   }
1172
1173   /* This function is only called when the calling code expects the context
1174    * to be changed. */
1175   imap_allow_reopen (ctx);
1176
1177   if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
1178     return rc;
1179
1180   /* if we are expunging anyway, we can do deleted messages very quickly... */
1181   if (expunge && mutt_bit_isset (ctx->rights, M_ACL_DELETE))
1182   {
1183     if ((rc = imap_exec_msgset (idata, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
1184                                 M_DELETED, 1, 0)) < 0)
1185     {
1186       mutt_error (_("Expunge failed"));
1187       mutt_sleep (1);
1188       goto out;
1189     }
1190
1191     if (rc > 0)
1192     {
1193       /* mark these messages as unchanged so second pass ignores them. Done
1194        * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1195       for (n = 0; n < ctx->msgcount; n++)
1196         if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
1197           ctx->hdrs[n]->active = 0;
1198       mutt_message (_("Marking %d messages deleted..."), rc);
1199     }
1200   }
1201
1202 #if USE_HCACHE
1203   idata->hcache = imap_hcache_open (idata, NULL);
1204 #endif
1205
1206   /* save messages with real (non-flag) changes */
1207   for (n = 0; n < ctx->msgcount; n++)
1208   {
1209     h = ctx->hdrs[n];
1210
1211     if (h->deleted)
1212     {
1213       imap_cache_del (idata, h);
1214 #if USE_HCACHE
1215       imap_hcache_del (idata, HEADER_DATA(h)->uid);
1216 #endif
1217     }
1218
1219     if (h->active && h->changed)
1220     {
1221 #if USE_HCACHE
1222       imap_hcache_put (idata, h);
1223 #endif
1224       /* if the message has been rethreaded or attachments have been deleted
1225        * we delete the message and reupload it.
1226        * This works better if we're expunging, of course. */
1227       if ((h->env && (h->env->refs_changed || h->env->irt_changed)) ||
1228           h->attach_del)
1229       {
1230         mutt_message (_("Saving changed messages... [%d/%d]"), n+1,
1231                       ctx->msgcount);
1232         if (!appendctx)
1233           appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
1234         if (!appendctx)
1235           dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n"));
1236         else
1237           _mutt_save_message (h, appendctx, 1, 0, 0);
1238       }
1239     }
1240   }
1241
1242 #if USE_HCACHE
1243   imap_hcache_close (idata);
1244 #endif
1245
1246   /* sync +/- flags for the five flags mutt cares about */
1247   rc = 0;
1248
1249   /* presort here to avoid doing 10 resorts in imap_exec_msgset */
1250   oldsort = Sort;
1251   if (Sort != SORT_ORDER)
1252   {
1253     hdrs = ctx->hdrs;
1254     ctx->hdrs = safe_malloc (ctx->msgcount * sizeof (HEADER*));
1255     memcpy (ctx->hdrs, hdrs, ctx->msgcount * sizeof (HEADER*));
1256
1257     Sort = SORT_ORDER;
1258     qsort (ctx->hdrs, ctx->msgcount, sizeof (HEADER*),
1259            mutt_get_sort_func (SORT_ORDER));
1260   }
1261
1262   rc += sync_helper (idata, M_ACL_DELETE, M_DELETED, "\\Deleted");
1263   rc += sync_helper (idata, M_ACL_WRITE, M_FLAG, "\\Flagged");
1264   rc += sync_helper (idata, M_ACL_WRITE, M_OLD, "Old");
1265   rc += sync_helper (idata, M_ACL_SEEN, M_READ, "\\Seen");
1266   rc += sync_helper (idata, M_ACL_WRITE, M_REPLIED, "\\Answered");
1267
1268   if (oldsort != Sort)
1269   {
1270     Sort = oldsort;
1271     FREE (&ctx->hdrs);
1272     ctx->hdrs = hdrs;
1273   }
1274
1275   if (rc && (imap_exec (idata, NULL, 0) != IMAP_CMD_OK))
1276   {
1277     if (ctx->closing)
1278     {
1279       if (mutt_yesorno (_("Error saving flags. Close anyway?"), 0) == M_YES)
1280       {
1281         rc = 0;
1282         idata->state = IMAP_AUTHENTICATED;
1283         goto out;
1284       }
1285     }
1286     else
1287       mutt_error _("Error saving flags");
1288     goto out;
1289   }
1290
1291   for (n = 0; n < ctx->msgcount; n++)
1292     ctx->hdrs[n]->changed = 0;
1293   ctx->changed = 0;
1294
1295   /* We must send an EXPUNGE command if we're not closing. */
1296   if (expunge && !(ctx->closing) &&
1297       mutt_bit_isset(ctx->rights, M_ACL_DELETE))
1298   {
1299     mutt_message _("Expunging messages from server...");
1300     /* Set expunge bit so we don't get spurious reopened messages */
1301     idata->reopen |= IMAP_EXPUNGE_EXPECTED;
1302     if (imap_exec (idata, "EXPUNGE", 0) != 0)
1303     {
1304       imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->buf);
1305       rc = -1;
1306       goto out;
1307     }
1308   }
1309
1310   if (expunge && ctx->closing)
1311   {
1312     imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE);
1313     idata->state = IMAP_AUTHENTICATED;
1314   }
1315
1316   if (option (OPTMESSAGECACHECLEAN))
1317     imap_cache_clean (idata);
1318
1319   rc = 0;
1320
1321  out:
1322   if (appendctx)
1323   {
1324     mx_fastclose_mailbox (appendctx);
1325     FREE (&appendctx);
1326   }
1327   return rc;
1328 }
1329
1330 /* imap_close_mailbox: clean up IMAP data in CONTEXT */
1331 int imap_close_mailbox (CONTEXT* ctx)
1332 {
1333   IMAP_DATA* idata;
1334   int i;
1335
1336   idata = (IMAP_DATA*) ctx->data;
1337   /* Check to see if the mailbox is actually open */
1338   if (!idata)
1339     return 0;
1340
1341   if (ctx == idata->ctx)
1342   {
1343     if (idata->status != IMAP_FATAL && idata->state >= IMAP_SELECTED)
1344     {
1345       /* mx_close_mailbox won't sync if there are no deleted messages
1346        * and the mailbox is unchanged, so we may have to close here */
1347       if (!ctx->deleted)
1348         imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE);
1349       idata->state = IMAP_AUTHENTICATED;
1350     }
1351
1352     idata->reopen &= IMAP_REOPEN_ALLOW;
1353     FREE (&(idata->mailbox));
1354     mutt_free_list (&idata->flags);
1355     idata->ctx = NULL;
1356   }
1357
1358   /* free IMAP part of headers */
1359   for (i = 0; i < ctx->msgcount; i++)
1360     /* mailbox may not have fully loaded */
1361     if (ctx->hdrs[i] && ctx->hdrs[i]->data)
1362       imap_free_header_data (&(ctx->hdrs[i]->data));
1363
1364   for (i = 0; i < IMAP_CACHE_LEN; i++)
1365   {
1366     if (idata->cache[i].path)
1367     {
1368       unlink (idata->cache[i].path);
1369       FREE (&idata->cache[i].path);
1370     }
1371   }
1372
1373   mutt_bcache_close (&idata->bcache);
1374
1375   return 0;
1376 }
1377
1378 /* use the NOOP or IDLE command to poll for new mail
1379  *
1380  * return values:
1381  *      M_REOPENED      mailbox has been externally modified
1382  *      M_NEW_MAIL      new mail has arrived!
1383  *      0               no change
1384  *      -1              error
1385  */
1386 int imap_check_mailbox (CONTEXT *ctx, int *index_hint, int force)
1387 {
1388   /* overload keyboard timeout to avoid many mailbox checks in a row.
1389    * Most users don't like having to wait exactly when they press a key. */
1390   IMAP_DATA* idata;
1391   int result = 0;
1392
1393   idata = (IMAP_DATA*) ctx->data;
1394
1395   /* try IDLE first, unless force is set */
1396   if (!force && option (OPTIMAPIDLE) && mutt_bit_isset (idata->capabilities, IDLE)
1397       && (idata->state != IMAP_IDLE || time(NULL) >= idata->lastread + ImapKeepalive))
1398   {
1399     if (imap_cmd_idle (idata) < 0)
1400       return -1;
1401   }
1402   if (idata->state == IMAP_IDLE)
1403   {
1404     while ((result = mutt_socket_poll (idata->conn)) > 0)
1405     {
1406       if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1407       {
1408         dprint (1, (debugfile, "Error reading IDLE response\n"));
1409         return -1;
1410       }
1411     }
1412     if (result < 0)
1413     {
1414       dprint (1, (debugfile, "Poll failed, disabling IDLE\n"));
1415       mutt_bit_unset (idata->capabilities, IDLE);
1416     }
1417   }
1418
1419   if ((force ||
1420        (idata->state != IMAP_IDLE && time(NULL) >= idata->lastread + Timeout))
1421       && imap_exec (idata, "NOOP", 0) != 0)
1422     return -1;
1423
1424   /* We call this even when we haven't run NOOP in case we have pending
1425    * changes to process, since we can reopen here. */
1426   imap_cmd_finish (idata);
1427
1428   if (idata->check_status & IMAP_EXPUNGE_PENDING)
1429     result = M_REOPENED;
1430   else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1431     result = M_NEW_MAIL;
1432   else if (idata->check_status & IMAP_FLAGS_PENDING)
1433     result = M_FLAGS;
1434
1435   idata->check_status = 0;
1436
1437   return result;
1438 }
1439
1440 /* split path into (idata,mailbox name) */
1441 static int imap_get_mailbox (const char* path, IMAP_DATA** hidata, char* buf, size_t blen)
1442 {
1443   IMAP_MBOX mx;
1444
1445   if (imap_parse_path (path, &mx))
1446   {
1447     dprint (1, (debugfile, "imap_get_mailbox: Error parsing %s\n", path));
1448     return -1;
1449   }
1450   if (!(*hidata = imap_conn_find (&(mx.account), option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0))
1451       || (*hidata)->state < IMAP_AUTHENTICATED)
1452   {
1453     FREE (&mx.mbox);
1454     return -1;
1455   }
1456
1457   imap_fix_path (*hidata, mx.mbox, buf, blen);
1458   if (!*buf)
1459     strfcpy (buf, "INBOX", blen);
1460   FREE (&mx.mbox);
1461
1462   return 0;
1463 }
1464
1465 /* check for new mail in any subscribed mailboxes. Given a list of mailboxes
1466  * rather than called once for each so that it can batch the commands and
1467  * save on round trips. Returns number of mailboxes with new mail. */
1468 int imap_buffy_check (int force)
1469 {
1470   IMAP_DATA* idata;
1471   IMAP_DATA* lastdata = NULL;
1472   BUFFY* mailbox;
1473   char name[LONG_STRING];
1474   char command[LONG_STRING];
1475   char munged[LONG_STRING];
1476   int buffies = 0;
1477
1478   for (mailbox = Incoming; mailbox; mailbox = mailbox->next)
1479   {
1480     /* Init newly-added mailboxes */
1481     if (! mailbox->magic)
1482     {
1483       if (mx_is_imap (mailbox->path))
1484         mailbox->magic = M_IMAP;
1485     }
1486
1487     if (mailbox->magic != M_IMAP)
1488       continue;
1489
1490     mailbox->new = 0;
1491
1492     if (imap_get_mailbox (mailbox->path, &idata, name, sizeof (name)) < 0)
1493       continue;
1494
1495     /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1496      * IDLEd elsewhere.
1497      * idata->mailbox may be NULL for connections other than the current
1498      * mailbox's, and shouldn't expand to INBOX in that case. #3216. */
1499     if (idata->mailbox && !imap_mxcmp (name, idata->mailbox))
1500       continue;
1501
1502     if (!mutt_bit_isset (idata->capabilities, IMAP4REV1) &&
1503         !mutt_bit_isset (idata->capabilities, STATUS))
1504     {
1505       dprint (2, (debugfile, "Server doesn't support STATUS\n"));
1506       continue;
1507     }
1508
1509     if (lastdata && idata != lastdata)
1510     {
1511       /* Send commands to previous server. Sorting the buffy list
1512        * may prevent some infelicitous interleavings */
1513       if (imap_exec (lastdata, NULL, IMAP_CMD_FAIL_OK) == -1)
1514         dprint (1, (debugfile, "Error polling mailboxes\n"));
1515
1516       lastdata = NULL;
1517     }
1518
1519     if (!lastdata)
1520       lastdata = idata;
1521
1522     imap_munge_mbox_name (munged, sizeof (munged), name);
1523     snprintf (command, sizeof (command),
1524               "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged);
1525
1526     if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0)
1527     {
1528       dprint (1, (debugfile, "Error queueing command\n"));
1529       return 0;
1530     }
1531   }
1532
1533   if (lastdata && (imap_exec (lastdata, NULL, IMAP_CMD_FAIL_OK) == -1))
1534   {
1535     dprint (1, (debugfile, "Error polling mailboxes\n"));
1536     return 0;
1537   }
1538
1539   /* collect results */
1540   for (mailbox = Incoming; mailbox; mailbox = mailbox->next)
1541   {
1542     if (mailbox->magic == M_IMAP && mailbox->new)
1543       buffies++;
1544   }
1545
1546   return buffies;
1547 }
1548
1549 /* imap_status: returns count of messages in mailbox, or -1 on error.
1550  * if queue != 0, queue the command and expect it to have been run
1551  * on the next call (for pipelining the postponed count) */
1552 int imap_status (char* path, int queue)
1553 {
1554   static int queued = 0;
1555
1556   IMAP_DATA *idata;
1557   char buf[LONG_STRING];
1558   char mbox[LONG_STRING];
1559   IMAP_STATUS* status;
1560
1561   if (imap_get_mailbox (path, &idata, buf, sizeof (buf)) < 0)
1562     return -1;
1563
1564   if (!imap_mxcmp (buf, idata->mailbox))
1565     /* We are in the folder we're polling - just return the mailbox count */
1566     return idata->ctx->msgcount;
1567   else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) ||
1568            mutt_bit_isset(idata->capabilities,STATUS))
1569   {
1570     imap_munge_mbox_name (mbox, sizeof(mbox), buf);
1571     snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox, "MESSAGES");
1572     imap_unmunge_mbox_name (mbox);
1573   }
1574   else
1575     /* Server does not support STATUS, and this is not the current mailbox.
1576      * There is no lightweight way to check recent arrivals */
1577     return -1;
1578
1579   if (queue)
1580   {
1581     imap_exec (idata, buf, IMAP_CMD_QUEUE);
1582     queued = 1;
1583     return 0;
1584   }
1585   else if (!queued)
1586     imap_exec (idata, buf, 0);
1587
1588   queued = 0;
1589   if ((status = imap_mboxcache_get (idata, mbox, 0)))
1590     return status->messages;
1591
1592   return 0;
1593 }
1594
1595 /* return cached mailbox stats or NULL if create is 0 */
1596 IMAP_STATUS* imap_mboxcache_get (IMAP_DATA* idata, const char* mbox, int create)
1597 {
1598   LIST* cur;
1599   IMAP_STATUS* status;
1600   IMAP_STATUS scache;
1601 #ifdef USE_HCACHE
1602   header_cache_t *hc = NULL;
1603   unsigned int *uidvalidity = NULL;
1604   unsigned int *uidnext = NULL;
1605 #endif
1606
1607   for (cur = idata->mboxcache; cur; cur = cur->next)
1608   {
1609     status = (IMAP_STATUS*)cur->data;
1610
1611     if (!imap_mxcmp (mbox, status->name))
1612       return status;
1613   }
1614   status = NULL;
1615
1616   /* lame */
1617   if (create)
1618   {
1619     memset (&scache, 0, sizeof (scache));
1620     scache.name = (char*)mbox;
1621     idata->mboxcache = mutt_add_list_n (idata->mboxcache, &scache,
1622                                         sizeof (scache));
1623     status = imap_mboxcache_get (idata, mbox, 0);
1624     status->name = safe_strdup (mbox);
1625   }
1626
1627 #ifdef USE_HCACHE
1628   hc = imap_hcache_open (idata, mbox);
1629   if (hc)
1630   {
1631     uidvalidity = mutt_hcache_fetch_raw (hc, "/UIDVALIDITY", imap_hcache_keylen);
1632     uidnext = mutt_hcache_fetch_raw (hc, "/UIDNEXT", imap_hcache_keylen);
1633     mutt_hcache_close (hc);
1634     if (uidvalidity)
1635     {
1636       if (!status)
1637       {
1638         FREE (&uidvalidity);
1639         FREE (&uidnext);
1640         return imap_mboxcache_get (idata, mbox, 1);
1641       }
1642       status->uidvalidity = *uidvalidity;
1643       status->uidnext = uidnext ? *uidnext: 0;
1644       dprint (3, (debugfile, "mboxcache: hcache uidvalidity %d, uidnext %d\n",
1645                   status->uidvalidity, status->uidnext));
1646     }
1647     FREE (&uidvalidity);
1648     FREE (&uidnext);
1649   }
1650 #endif
1651
1652   return status;
1653 }
1654
1655 void imap_mboxcache_free (IMAP_DATA* idata)
1656 {
1657   LIST* cur;
1658   IMAP_STATUS* status;
1659
1660   for (cur = idata->mboxcache; cur; cur = cur->next)
1661   {
1662     status = (IMAP_STATUS*)cur->data;
1663
1664     FREE (&status->name);
1665   }
1666
1667   mutt_free_list (&idata->mboxcache);
1668 }
1669
1670 /* returns number of patterns in the search that should be done server-side
1671  * (eg are full-text) */
1672 static int do_search (const pattern_t* search, int allpats)
1673 {
1674   int rc = 0;
1675   const pattern_t* pat;
1676
1677   for (pat = search; pat; pat = pat->next)
1678   {
1679     switch (pat->op)
1680     {
1681       case M_BODY:
1682       case M_HEADER:
1683       case M_WHOLE_MSG:
1684         if (pat->stringmatch)
1685           rc++;
1686         break;
1687       default:
1688         if (pat->child && do_search (pat->child, 1))
1689           rc++;
1690     }
1691
1692     if (!allpats)
1693       break;
1694   }
1695
1696   return rc;
1697 }
1698
1699 /* convert mutt pattern_t to IMAP SEARCH command containing only elements
1700  * that require full-text search (mutt already has what it needs for most
1701  * match types, and does a better job (eg server doesn't support regexps). */
1702 static int imap_compile_search (const pattern_t* pat, BUFFER* buf)
1703 {
1704   if (! do_search (pat, 0))
1705     return 0;
1706
1707   if (pat->not)
1708     mutt_buffer_addstr (buf, "NOT ");
1709
1710   if (pat->child)
1711   {
1712     int clauses;
1713
1714     if ((clauses = do_search (pat->child, 1)) > 0)
1715     {
1716       const pattern_t* clause = pat->child;
1717
1718       mutt_buffer_addch (buf, '(');
1719
1720       while (clauses)
1721       {
1722         if (do_search (clause, 0))
1723         {
1724           if (pat->op == M_OR && clauses > 1)
1725             mutt_buffer_addstr (buf, "OR ");
1726           clauses--;
1727
1728           if (imap_compile_search (clause, buf) < 0)
1729             return -1;
1730
1731           if (clauses)
1732             mutt_buffer_addch (buf, ' ');
1733
1734         }
1735         clause = clause->next;
1736       }
1737
1738       mutt_buffer_addch (buf, ')');
1739     }
1740   }
1741   else
1742   {
1743     char term[STRING];
1744     char *delim;
1745
1746     switch (pat->op)
1747     {
1748       case M_HEADER:
1749         mutt_buffer_addstr (buf, "HEADER ");
1750
1751         /* extract header name */
1752         if (! (delim = strchr (pat->p.str, ':')))
1753         {
1754           mutt_error (_("Header search without header name: %s"), pat->p.str);
1755           return -1;
1756         }
1757         *delim = '\0';
1758         imap_quote_string (term, sizeof (term), pat->p.str);
1759         mutt_buffer_addstr (buf, term);
1760         mutt_buffer_addch (buf, ' ');
1761
1762         /* and field */
1763         *delim = ':';
1764         delim++;
1765         SKIPWS(delim);
1766         imap_quote_string (term, sizeof (term), delim);
1767         mutt_buffer_addstr (buf, term);
1768         break;
1769       case M_BODY:
1770         mutt_buffer_addstr (buf, "BODY ");
1771         imap_quote_string (term, sizeof (term), pat->p.str);
1772         mutt_buffer_addstr (buf, term);
1773         break;
1774       case M_WHOLE_MSG:
1775         mutt_buffer_addstr (buf, "TEXT ");
1776         imap_quote_string (term, sizeof (term), pat->p.str);
1777         mutt_buffer_addstr (buf, term);
1778         break;
1779     }
1780   }
1781
1782   return 0;
1783 }
1784
1785 int imap_search (CONTEXT* ctx, const pattern_t* pat)
1786 {
1787   BUFFER buf;
1788   IMAP_DATA* idata = (IMAP_DATA*)ctx->data;
1789   int i;
1790
1791   for (i = 0; i < ctx->msgcount; i++)
1792     ctx->hdrs[i]->matched = 0;
1793
1794   if (!do_search (pat, 1))
1795     return 0;
1796
1797   memset (&buf, 0, sizeof (buf));
1798   mutt_buffer_addstr (&buf, "UID SEARCH ");
1799   if (imap_compile_search (pat, &buf) < 0)
1800   {
1801     FREE (&buf.data);
1802     return -1;
1803   }
1804   if (imap_exec (idata, buf.data, 0) < 0)
1805   {
1806     FREE (&buf.data);
1807     return -1;
1808   }
1809
1810   FREE (&buf.data);
1811   return 0;
1812 }
1813
1814 int imap_subscribe (char *path, int subscribe)
1815 {
1816   CONNECTION *conn;
1817   IMAP_DATA *idata;
1818   char buf[LONG_STRING];
1819   char mbox[LONG_STRING];
1820   char errstr[STRING];
1821   BUFFER err, token;
1822   IMAP_MBOX mx;
1823
1824   if (!mx_is_imap (path) || imap_parse_path (path, &mx) || !mx.mbox)
1825   {
1826     mutt_error (_("Bad mailbox name"));
1827     return -1;
1828   }
1829   if (!(idata = imap_conn_find (&(mx.account), 0)))
1830     goto fail;
1831
1832   conn = idata->conn;
1833
1834   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1835   if (!*buf)
1836     strfcpy (buf, "INBOX", sizeof (buf));
1837
1838   if (option (OPTIMAPCHECKSUBSCRIBED))
1839   {
1840     memset (&token, 0, sizeof (token));
1841     err.data = errstr;
1842     err.dsize = sizeof (errstr);
1843     snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"",
1844               subscribe ? "" : "un", path);
1845     if (mutt_parse_rc_line (mbox, &token, &err))
1846       dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
1847     FREE (&token.data);
1848   }
1849
1850   if (subscribe)
1851     mutt_message (_("Subscribing to %s..."), buf);
1852   else
1853     mutt_message (_("Unsubscribing from %s..."), buf);
1854   imap_munge_mbox_name (mbox, sizeof(mbox), buf);
1855
1856   snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox);
1857
1858   if (imap_exec (idata, buf, 0) < 0)
1859     goto fail;
1860
1861   imap_unmunge_mbox_name(mx.mbox);
1862   if (subscribe)
1863     mutt_message (_("Subscribed to %s"), mx.mbox);
1864   else
1865     mutt_message (_("Unsubscribed from %s"), mx.mbox);
1866   FREE (&mx.mbox);
1867   return 0;
1868
1869  fail:
1870   FREE (&mx.mbox);
1871   return -1;
1872 }
1873
1874 /* trim dest to the length of the longest prefix it shares with src,
1875  * returning the length of the trimmed string */
1876 static int
1877 longest_common_prefix (char *dest, const char* src, int start, size_t dlen)
1878 {
1879   int pos = start;
1880
1881   while (pos < dlen && dest[pos] && dest[pos] == src[pos])
1882     pos++;
1883   dest[pos] = '\0';
1884
1885   return pos;
1886 }
1887
1888 /* look for IMAP URLs to complete from defined mailboxes. Could be extended
1889  * to complete over open connections and account/folder hooks too. */
1890 static int
1891 imap_complete_hosts (char *dest, size_t len)
1892 {
1893   BUFFY* mailbox;
1894   CONNECTION* conn;
1895   int rc = -1;
1896   int matchlen;
1897
1898   matchlen = mutt_strlen (dest);
1899   for (mailbox = Incoming; mailbox; mailbox = mailbox->next)
1900   {
1901     if (!mutt_strncmp (dest, mailbox->path, matchlen))
1902     {
1903       if (rc)
1904       {
1905         strfcpy (dest, mailbox->path, len);
1906         rc = 0;
1907       }
1908       else
1909         longest_common_prefix (dest, mailbox->path, matchlen, len);
1910     }
1911   }
1912
1913   for (conn = mutt_socket_head (); conn; conn = conn->next)
1914   {
1915     ciss_url_t url;
1916     char urlstr[LONG_STRING];
1917
1918     if (conn->account.type != M_ACCT_TYPE_IMAP)
1919       continue;
1920
1921     mutt_account_tourl (&conn->account, &url);
1922     /* FIXME: how to handle multiple users on the same host? */
1923     url.user = NULL;
1924     url.path = NULL;
1925     url_ciss_tostring (&url, urlstr, sizeof (urlstr), 0);
1926     if (!mutt_strncmp (dest, urlstr, matchlen))
1927     {
1928       if (rc)
1929       {
1930         strfcpy (dest, urlstr, len);
1931         rc = 0;
1932       }
1933       else
1934         longest_common_prefix (dest, urlstr, matchlen, len);
1935     }
1936   }
1937
1938   return rc;
1939 }
1940
1941 /* imap_complete: given a partial IMAP folder path, return a string which
1942  *   adds as much to the path as is unique */
1943 int imap_complete(char* dest, size_t dlen, char* path) {
1944   CONNECTION* conn;
1945   IMAP_DATA* idata;
1946   char list[LONG_STRING];
1947   char buf[LONG_STRING];
1948   IMAP_LIST listresp;
1949   char completion[LONG_STRING];
1950   int clen, matchlen = 0;
1951   int completions = 0;
1952   IMAP_MBOX mx;
1953   int rc;
1954
1955   if (imap_parse_path (path, &mx))
1956   {
1957     strfcpy (dest, path, dlen);
1958     return imap_complete_hosts (dest, dlen);
1959   }
1960
1961   /* don't open a new socket just for completion. Instead complete over
1962    * known mailboxes/hooks/etc */
1963   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW)))
1964   {
1965     FREE (&mx.mbox);
1966     strfcpy (dest, path, dlen);
1967     return imap_complete_hosts (dest, dlen);
1968   }
1969   conn = idata->conn;
1970
1971   /* reformat path for IMAP list, and append wildcard */
1972   /* don't use INBOX in place of "" */
1973   if (mx.mbox && mx.mbox[0])
1974     imap_fix_path (idata, mx.mbox, list, sizeof(list));
1975   else
1976     list[0] = '\0';
1977
1978   /* fire off command */
1979   snprintf (buf, sizeof(buf), "%s \"\" \"%s%%\"",
1980     option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
1981
1982   imap_cmd_start (idata, buf);
1983
1984   /* and see what the results are */
1985   strfcpy (completion, NONULL(mx.mbox), sizeof(completion));
1986   idata->cmdtype = IMAP_CT_LIST;
1987   idata->cmddata = &listresp;
1988   do
1989   {
1990     listresp.name = NULL;
1991     rc = imap_cmd_step (idata);
1992
1993     if (rc == IMAP_CMD_CONTINUE && listresp.name)
1994     {
1995       /* if the folder isn't selectable, append delimiter to force browse
1996        * to enter it on second tab. */
1997       if (listresp.noselect)
1998       {
1999         clen = strlen(listresp.name);
2000         listresp.name[clen++] = listresp.delim;
2001         listresp.name[clen] = '\0';
2002       }
2003       /* copy in first word */
2004       if (!completions)
2005       {
2006         strfcpy (completion, listresp.name, sizeof(completion));
2007         matchlen = strlen (completion);
2008         completions++;
2009         continue;
2010       }
2011
2012       matchlen = longest_common_prefix (completion, listresp.name, 0, matchlen);
2013       completions++;
2014     }
2015   }
2016   while (rc == IMAP_CMD_CONTINUE);
2017   idata->cmddata = NULL;
2018
2019   if (completions)
2020   {
2021     /* reformat output */
2022     imap_qualify_path (dest, dlen, &mx, completion);
2023     mutt_pretty_mailbox (dest, dlen);
2024
2025     FREE (&mx.mbox);
2026     return 0;
2027   }
2028
2029   return -1;
2030 }