]> git.llucax.com Git - software/mutt-debian.git/blob - imap/command.c
test upstream patch to fix 533439
[software/mutt-debian.git] / imap / command.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 /* command.c: routines for sending commands to an IMAP server and parsing
22  *  responses */
23
24 #if HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include "mutt.h"
29 #include "imap_private.h"
30 #include "message.h"
31 #include "mx.h"
32 #include "buffy.h"
33
34 #include <ctype.h>
35 #include <stdlib.h>
36
37 #define IMAP_CMD_BUFSIZE 512
38
39 /* forward declarations */
40 static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags);
41 static int cmd_queue_full (IMAP_DATA* idata);
42 static int cmd_queue (IMAP_DATA* idata, const char* cmdstr);
43 static IMAP_COMMAND* cmd_new (IMAP_DATA* idata);
44 static int cmd_status (const char *s);
45 static void cmd_handle_fatal (IMAP_DATA* idata);
46 static int cmd_handle_untagged (IMAP_DATA* idata);
47 static void cmd_parse_capability (IMAP_DATA* idata, char* s);
48 static void cmd_parse_expunge (IMAP_DATA* idata, const char* s);
49 static void cmd_parse_list (IMAP_DATA* idata, char* s);
50 static void cmd_parse_lsub (IMAP_DATA* idata, char* s);
51 static void cmd_parse_fetch (IMAP_DATA* idata, char* s);
52 static void cmd_parse_myrights (IMAP_DATA* idata, const char* s);
53 static void cmd_parse_search (IMAP_DATA* idata, const char* s);
54 static void cmd_parse_status (IMAP_DATA* idata, char* s);
55
56 static char *Capabilities[] = {
57   "IMAP4",
58   "IMAP4rev1",
59   "STATUS",
60   "ACL", 
61   "NAMESPACE",
62   "AUTH=CRAM-MD5",
63   "AUTH=GSSAPI",
64   "AUTH=ANONYMOUS",
65   "STARTTLS",
66   "LOGINDISABLED",
67   "IDLE",
68   "SASL-IR",
69
70   NULL
71 };
72
73 /* imap_cmd_start: Given an IMAP command, send it to the server.
74  *   If cmdstr is NULL, sends queued commands. */
75 int imap_cmd_start (IMAP_DATA* idata, const char* cmdstr)
76 {
77   return cmd_start (idata, cmdstr, 0);
78 }
79
80 /* imap_cmd_step: Reads server responses from an IMAP command, detects
81  *   tagged completion response, handles untagged messages, can read
82  *   arbitrarily large strings (using malloc, so don't make it _too_
83  *   large!). */
84 int imap_cmd_step (IMAP_DATA* idata)
85 {
86   size_t len = 0;
87   int c;
88   int rc;
89   int stillrunning = 0;
90   IMAP_COMMAND* cmd;
91
92   if (idata->status == IMAP_FATAL)
93   {
94     cmd_handle_fatal (idata);
95     return IMAP_CMD_BAD;
96   }
97
98   /* read into buffer, expanding buffer as necessary until we have a full
99    * line */
100   do
101   {
102     if (len == idata->blen)
103     {
104       safe_realloc (&idata->buf, idata->blen + IMAP_CMD_BUFSIZE);
105       idata->blen = idata->blen + IMAP_CMD_BUFSIZE;
106       dprint (3, (debugfile, "imap_cmd_step: grew buffer to %u bytes\n",
107                   idata->blen));
108     }
109
110     /* back up over '\0' */
111     if (len)
112       len--;
113     c = mutt_socket_readln (idata->buf + len, idata->blen - len, idata->conn);
114     if (c <= 0)
115     {
116       dprint (1, (debugfile, "imap_cmd_step: Error reading server response.\n"));
117       cmd_handle_fatal (idata);
118       return IMAP_CMD_BAD;
119     }
120
121     len += c;
122   }
123   /* if we've read all the way to the end of the buffer, we haven't read a
124    * full line (mutt_socket_readln strips the \r, so we always have at least
125    * one character free when we've read a full line) */
126   while (len == idata->blen);
127
128   /* don't let one large string make cmd->buf hog memory forever */
129   if ((idata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
130   {
131     safe_realloc (&idata->buf, IMAP_CMD_BUFSIZE);
132     idata->blen = IMAP_CMD_BUFSIZE;
133     dprint (3, (debugfile, "imap_cmd_step: shrank buffer to %u bytes\n", idata->blen));
134   }
135
136   idata->lastread = time (NULL);
137
138   /* handle untagged messages. The caller still gets its shot afterwards. */
139   if ((!ascii_strncmp (idata->buf, "* ", 2)
140        || !ascii_strncmp (imap_next_word (idata->buf), "OK [", 4))
141       && cmd_handle_untagged (idata))
142     return IMAP_CMD_BAD;
143
144   /* server demands a continuation response from us */
145   if (idata->buf[0] == '+')
146     return IMAP_CMD_RESPOND;
147
148   /* look for tagged command completions */
149   rc = IMAP_CMD_CONTINUE;
150   c = idata->lastcmd;
151   do
152   {
153     cmd = &idata->cmds[c];
154     if (cmd->state == IMAP_CMD_NEW)
155     {
156       if (!ascii_strncmp (idata->buf, cmd->seq, SEQLEN)) {
157         if (!stillrunning)
158         {
159           /* first command in queue has finished - move queue pointer up */
160           idata->lastcmd = (idata->lastcmd + 1) % idata->cmdslots;
161         }
162         cmd->state = cmd_status (idata->buf);
163         /* bogus - we don't know which command result to return here. Caller
164          * should provide a tag. */
165         rc = cmd->state;
166       }
167       else
168         stillrunning++;
169     }
170
171     c = (c + 1) % idata->cmdslots;
172   }
173   while (c != idata->nextcmd);
174
175   if (stillrunning)
176     rc = IMAP_CMD_CONTINUE;
177   else
178   {
179     dprint (3, (debugfile, "IMAP queue drained\n"));
180     imap_cmd_finish (idata);
181   }
182   
183
184   return rc;
185 }
186
187 /* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
188 int imap_code (const char *s)
189 {
190   return cmd_status (s) == IMAP_CMD_OK;
191 }
192
193 /* imap_cmd_trailer: extra information after tagged command response if any */
194 const char* imap_cmd_trailer (IMAP_DATA* idata)
195 {
196   static const char* notrailer = "";
197   const char* s = idata->buf;
198
199   if (!s)
200   {
201     dprint (2, (debugfile, "imap_cmd_trailer: not a tagged response"));
202     return notrailer;
203   }
204
205   s = imap_next_word ((char *)s);
206   if (!s || (ascii_strncasecmp (s, "OK", 2) &&
207              ascii_strncasecmp (s, "NO", 2) &&
208              ascii_strncasecmp (s, "BAD", 3)))
209   {
210     dprint (2, (debugfile, "imap_cmd_trailer: not a command completion: %s",
211                 idata->buf));
212     return notrailer;
213   }
214
215   s = imap_next_word ((char *)s);
216   if (!s)
217     return notrailer;
218
219   return s;
220 }
221
222 /* imap_exec: execute a command, and wait for the response from the server.
223  * Also, handle untagged responses.
224  * Flags:
225  *   IMAP_CMD_FAIL_OK: the calling procedure can handle failure. This is used
226  *     for checking for a mailbox on append and login
227  *   IMAP_CMD_PASS: command contains a password. Suppress logging.
228  *   IMAP_CMD_QUEUE: only queue command, do not execute.
229  * Return 0 on success, -1 on Failure, -2 on OK Failure
230  */
231 int imap_exec (IMAP_DATA* idata, const char* cmdstr, int flags)
232 {
233   int rc;
234
235   if ((rc = cmd_start (idata, cmdstr, flags)) < 0)
236     return rc;
237
238   if (rc < 0)
239   {
240     cmd_handle_fatal (idata);
241     return -1;
242   }
243
244   if (flags & IMAP_CMD_QUEUE)
245     return 0;
246
247   do
248     rc = imap_cmd_step (idata);
249   while (rc == IMAP_CMD_CONTINUE);
250
251   if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
252     return -2;
253
254   if (rc != IMAP_CMD_OK)
255   {
256     if (flags & IMAP_CMD_FAIL_OK && idata->status != IMAP_FATAL)
257       return -2;
258
259     dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->buf));
260     return -1;
261   }
262
263   return 0;
264 }
265
266 /* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
267  *   detected, do expunge). Called automatically by imap_cmd_step, but
268  *   may be called at any time. Called by imap_check_mailbox just before
269  *   the index is refreshed, for instance. */
270 void imap_cmd_finish (IMAP_DATA* idata)
271 {
272   if (idata->status == IMAP_FATAL)
273   {
274     cmd_handle_fatal (idata);
275     return;
276   }
277
278   if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
279     return;
280   
281   if (idata->reopen & IMAP_REOPEN_ALLOW)
282   {
283     int count = idata->newMailCount;
284
285     if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
286         (idata->reopen & IMAP_NEWMAIL_PENDING)
287         && count > idata->ctx->msgcount)
288     {
289       /* read new mail messages */
290       dprint (2, (debugfile, "imap_cmd_finish: Fetching new mail\n"));
291       /* check_status: curs_main uses imap_check_mailbox to detect
292        *   whether the index needs updating */
293       idata->check_status = IMAP_NEWMAIL_PENDING;
294       imap_read_headers (idata, idata->ctx->msgcount, count-1);
295     }
296     else if (idata->reopen & IMAP_EXPUNGE_PENDING)
297     {
298       dprint (2, (debugfile, "imap_cmd_finish: Expunging mailbox\n"));
299       imap_expunge_mailbox (idata);
300       /* Detect whether we've gotten unexpected EXPUNGE messages */
301       if (idata->reopen & IMAP_EXPUNGE_PENDING &&
302           !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
303         idata->check_status = IMAP_EXPUNGE_PENDING;
304       idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
305                          IMAP_EXPUNGE_EXPECTED);
306     }
307   }
308
309   idata->status = 0;
310 }
311
312 /* imap_cmd_idle: Enter the IDLE state. */
313 int imap_cmd_idle (IMAP_DATA* idata)
314 {
315   int rc;
316
317   imap_cmd_start (idata, "IDLE");
318   do
319     rc = imap_cmd_step (idata);
320   while (rc == IMAP_CMD_CONTINUE);
321
322   if (rc == IMAP_CMD_RESPOND)
323   {
324     /* successfully entered IDLE state */
325     idata->state = IMAP_IDLE;
326     /* queue automatic exit when next command is issued */
327     mutt_buffer_printf (idata->cmdbuf, "DONE\r\n");
328     rc = IMAP_CMD_OK;
329   }
330   if (rc != IMAP_CMD_OK)
331   {
332     dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
333     return -1;
334   }
335   
336   return 0;
337 }
338
339 static int cmd_queue_full (IMAP_DATA* idata)
340 {
341   if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
342     return 1;
343
344   return 0;
345 }
346
347 /* sets up a new command control block and adds it to the queue.
348  * Returns NULL if the pipeline is full. */
349 static IMAP_COMMAND* cmd_new (IMAP_DATA* idata)
350 {
351   IMAP_COMMAND* cmd;
352
353   if (cmd_queue_full (idata))
354   {
355     dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
356     return NULL;
357   }
358
359   cmd = idata->cmds + idata->nextcmd;
360   idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
361
362   snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
363   if (idata->seqno > 9999)
364     idata->seqno = 0;
365
366   cmd->state = IMAP_CMD_NEW;
367
368   return cmd;
369 }
370
371 /* queues command. If the queue is full, attempts to drain it. */
372 static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
373 {
374   IMAP_COMMAND* cmd;
375   int rc;
376
377   if (cmd_queue_full (idata))
378   {
379     dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
380
381     rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
382
383     if (rc < 0 && rc != -2)
384       return rc;
385   }
386
387   if (!(cmd = cmd_new (idata)))
388     return IMAP_CMD_BAD;
389
390   if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
391     return IMAP_CMD_BAD;
392
393   return 0;
394 }
395
396 static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
397 {
398   int rc;
399
400   if (idata->status == IMAP_FATAL)
401   {
402     cmd_handle_fatal (idata);
403     return -1;
404   }
405
406   if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
407     return rc;
408
409   if (flags & IMAP_CMD_QUEUE)
410     return 0;
411
412   if (idata->cmdbuf->dptr == idata->cmdbuf->data)
413     return IMAP_CMD_BAD;
414
415   rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1,
416                             flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
417   idata->cmdbuf->dptr = idata->cmdbuf->data;
418
419   /* unidle when command queue is flushed */
420   if (idata->state == IMAP_IDLE)
421     idata->state = IMAP_SELECTED;
422
423   return (rc < 0) ? IMAP_CMD_BAD : 0;
424 }
425
426 /* parse response line for tagged OK/NO/BAD */
427 static int cmd_status (const char *s)
428 {
429   s = imap_next_word((char*)s);
430   
431   if (!ascii_strncasecmp("OK", s, 2))
432     return IMAP_CMD_OK;
433   if (!ascii_strncasecmp("NO", s, 2))
434     return IMAP_CMD_NO;
435
436   return IMAP_CMD_BAD;
437 }
438
439 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
440 static void cmd_handle_fatal (IMAP_DATA* idata)
441 {
442   idata->status = IMAP_FATAL;
443
444   if ((idata->state >= IMAP_SELECTED) &&
445       (idata->reopen & IMAP_REOPEN_ALLOW))
446   {
447     mx_fastclose_mailbox (idata->ctx);
448     mutt_error (_("Mailbox closed"));
449     mutt_sleep (1);
450     idata->state = IMAP_DISCONNECTED;
451   }
452
453   if (idata->state < IMAP_SELECTED)
454     imap_close_connection (idata);
455 }
456
457 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
458 static int cmd_handle_untagged (IMAP_DATA* idata)
459 {
460   char* s;
461   char* pn;
462   int count;
463
464   s = imap_next_word (idata->buf);
465   pn = imap_next_word (s);
466
467   if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s))
468   {
469     pn = s;
470     s = imap_next_word (s);
471
472     /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
473      * connection, so update that one.
474      */
475     if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
476     {
477       dprint (2, (debugfile, "Handling EXISTS\n"));
478
479       /* new mail arrived */
480       count = atoi (pn);
481
482       if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
483            count < idata->ctx->msgcount)
484       {
485         /* Notes 6.0.3 has a tendency to report fewer messages exist than
486          * it should. */
487         dprint (1, (debugfile, "Message count is out of sync"));
488         return 0;
489       }
490       /* at least the InterChange server sends EXISTS messages freely,
491        * even when there is no new mail */
492       else if (count == idata->ctx->msgcount)
493         dprint (3, (debugfile,
494           "cmd_handle_untagged: superfluous EXISTS message.\n"));
495       else
496       {
497         if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
498         {
499           dprint (2, (debugfile,
500             "cmd_handle_untagged: New mail in %s - %d messages total.\n",
501             idata->mailbox, count));
502           idata->reopen |= IMAP_NEWMAIL_PENDING;
503         }
504         idata->newMailCount = count;
505       }
506     }
507     /* pn vs. s: need initial seqno */
508     else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
509       cmd_parse_expunge (idata, pn);
510     else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
511       cmd_parse_fetch (idata, pn);
512   }
513   else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
514     cmd_parse_capability (idata, s);
515   else if (!ascii_strncasecmp ("OK [CAPABILITY", s, 14))
516     cmd_parse_capability (idata, pn);
517   else if (!ascii_strncasecmp ("OK [CAPABILITY", pn, 14))
518     cmd_parse_capability (idata, imap_next_word (pn));
519   else if (ascii_strncasecmp ("LIST", s, 4) == 0)
520     cmd_parse_list (idata, s);
521   else if (ascii_strncasecmp ("LSUB", s, 4) == 0)
522     cmd_parse_lsub (idata, s);
523   else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
524     cmd_parse_myrights (idata, s);
525   else if (ascii_strncasecmp ("SEARCH", s, 6) == 0)
526     cmd_parse_search (idata, s);
527   else if (ascii_strncasecmp ("STATUS", s, 6) == 0)
528     cmd_parse_status (idata, s);
529   else if (ascii_strncasecmp ("BYE", s, 3) == 0)
530   {
531     dprint (2, (debugfile, "Handling BYE\n"));
532
533     /* check if we're logging out */
534     if (idata->status == IMAP_BYE)
535       return 0;
536
537     /* server shut down our connection */
538     s += 3;
539     SKIPWS (s);
540     mutt_error ("%s", s);
541     mutt_sleep (2);
542     cmd_handle_fatal (idata);
543
544     return -1;
545   }
546   else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
547   {
548     dprint (2, (debugfile, "Handling untagged NO\n"));
549
550     /* Display the warning message from the server */
551     mutt_error ("%s", s+3);
552     mutt_sleep (2);
553   }
554
555   return 0;
556 }
557
558 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
559  *   response */
560 static void cmd_parse_capability (IMAP_DATA* idata, char* s)
561 {
562   int x;
563   char* bracket;
564
565   dprint (3, (debugfile, "Handling CAPABILITY\n"));
566
567   s = imap_next_word (s);
568   if ((bracket = strchr (s, ']')))
569     *bracket = '\0';
570   FREE(&idata->capstr);
571   idata->capstr = safe_strdup (s);
572
573   memset (idata->capabilities, 0, sizeof (idata->capabilities));
574
575   while (*s)
576   {
577     for (x = 0; x < CAPMAX; x++)
578       if (imap_wordcasecmp(Capabilities[x], s) == 0)
579       {
580         mutt_bit_set (idata->capabilities, x);
581         break;
582       }
583     s = imap_next_word (s);
584   }
585 }
586
587 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
588  *   be reopened at our earliest convenience */
589 static void cmd_parse_expunge (IMAP_DATA* idata, const char* s)
590 {
591   int expno, cur;
592   HEADER* h;
593
594   dprint (2, (debugfile, "Handling EXPUNGE\n"));
595
596   expno = atoi (s);
597
598   /* walk headers, zero seqno of expunged message, decrement seqno of those
599    * above. Possibly we could avoid walking the whole list by resorting
600    * and guessing a good starting point, but I'm guessing the resort would
601    * nullify the gains */
602   for (cur = 0; cur < idata->ctx->msgcount; cur++)
603   {
604     h = idata->ctx->hdrs[cur];
605
606     if (h->index+1 == expno)
607       h->index = -1;
608     else if (h->index+1 > expno)
609       h->index--;
610   }
611
612   idata->reopen |= IMAP_EXPUNGE_PENDING;
613 }
614
615 /* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
616  *   handles unanticipated FETCH responses, and only FLAGS data. We get
617  *   these if another client has changed flags for a mailbox we've selected.
618  *   Of course, a lot of code here duplicates code in message.c. */
619 static void cmd_parse_fetch (IMAP_DATA* idata, char* s)
620 {
621   int msgno, cur;
622   HEADER* h = NULL;
623
624   dprint (3, (debugfile, "Handling FETCH\n"));
625
626   msgno = atoi (s);
627   
628   if (msgno <= idata->ctx->msgcount)
629   /* see cmd_parse_expunge */
630     for (cur = 0; cur < idata->ctx->msgcount; cur++)
631     {
632       h = idata->ctx->hdrs[cur];
633       
634       if (h && h->active && h->index+1 == msgno)
635       {
636         dprint (2, (debugfile, "Message UID %d updated\n", HEADER_DATA(h)->uid));
637         break;
638       }
639       
640       h = NULL;
641     }
642   
643   if (!h)
644   {
645     dprint (3, (debugfile, "FETCH response ignored for this message\n"));
646     return;
647   }
648   
649   /* skip FETCH */
650   s = imap_next_word (s);
651   s = imap_next_word (s);
652
653   if (*s != '(')
654   {
655     dprint (1, (debugfile, "Malformed FETCH response"));
656     return;
657   }
658   s++;
659
660   if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
661   {
662     dprint (2, (debugfile, "Only handle FLAGS updates\n"));
663     return;
664   }
665
666   /* If server flags could conflict with mutt's flags, reopen the mailbox. */
667   if (h->changed)
668     idata->reopen |= IMAP_EXPUNGE_PENDING;
669   else {
670     imap_set_flags (idata, h, s);
671     idata->check_status = IMAP_FLAGS_PENDING;
672   }
673 }
674
675 static void cmd_parse_list (IMAP_DATA* idata, char* s)
676 {
677   IMAP_LIST* list;
678   IMAP_LIST lb;
679   char delimbuf[5]; /* worst case: "\\"\0 */
680   long litlen;
681
682   if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
683     list = (IMAP_LIST*)idata->cmddata;
684   else
685     list = &lb;
686
687   memset (list, 0, sizeof (IMAP_LIST));
688
689   /* flags */
690   s = imap_next_word (s);
691   if (*s != '(')
692   {
693     dprint (1, (debugfile, "Bad LIST response\n"));
694     return;
695   }
696   s++;
697   while (*s)
698   {
699     if (!ascii_strncasecmp (s, "\\NoSelect", 9))
700       list->noselect = 1;
701     else if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
702       list->noinferiors = 1;
703     /* See draft-gahrns-imap-child-mailbox-?? */
704     else if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
705       list->noinferiors = 1;
706     
707     s = imap_next_word (s);
708     if (*(s - 2) == ')')
709       break;
710   }
711
712   /* Delimiter */
713   if (ascii_strncasecmp (s, "NIL", 3))
714   {
715     delimbuf[0] = '\0';
716     safe_strcat (delimbuf, 5, s); 
717     imap_unquote_string (delimbuf);
718     list->delim = delimbuf[0];
719   }
720
721   /* Name */
722   s = imap_next_word (s);
723   /* Notes often responds with literals here. We need a real tokenizer. */
724   if (!imap_get_literal_count (s, &litlen))
725   {
726     if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
727     {
728       idata->status = IMAP_FATAL;
729       return;
730     }
731     list->name = idata->buf;
732   }
733   else
734   {
735     imap_unmunge_mbox_name (s);
736     list->name = s;
737   }
738
739   if (list->name[0] == '\0')
740   {
741     idata->delim = list->delim;
742     dprint (3, (debugfile, "Root delimiter: %c\n", idata->delim));
743   }
744 }
745
746 static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
747 {
748   char buf[STRING];
749   char errstr[STRING];
750   BUFFER err, token;
751   ciss_url_t url;
752   IMAP_LIST list;
753
754   if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
755   {
756     /* caller will handle response itself */
757     cmd_parse_list (idata, s);
758     return;
759   }
760
761   if (!option (OPTIMAPCHECKSUBSCRIBED))
762     return;
763
764   idata->cmdtype = IMAP_CT_LIST;
765   idata->cmddata = &list;
766   cmd_parse_list (idata, s);
767   idata->cmddata = NULL;
768   if (!list.name)
769     return;
770
771   dprint (3, (debugfile, "Subscribing to %s\n", list.name));
772
773   strfcpy (buf, "mailboxes \"", sizeof (buf));
774   mutt_account_tourl (&idata->conn->account, &url);
775   /* escape \ and " */
776   imap_quote_string(errstr, sizeof (errstr), list.name);
777   url.path = errstr + 1;
778   url.path[strlen(url.path) - 1] = '\0';
779   if (!mutt_strcmp (url.user, ImapUser))
780     url.user = NULL;
781   url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
782   safe_strcat (buf, sizeof (buf), "\"");
783   memset (&token, 0, sizeof (token));
784   err.data = errstr;
785   err.dsize = sizeof (errstr);
786   if (mutt_parse_rc_line (buf, &token, &err))
787     dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
788   FREE (&token.data);
789 }
790
791 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
792 static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
793 {
794   dprint (2, (debugfile, "Handling MYRIGHTS\n"));
795
796   s = imap_next_word ((char*)s);
797   s = imap_next_word ((char*)s);
798
799   /* zero out current rights set */
800   memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
801
802   while (*s && !isspace((unsigned char) *s))
803   {
804     switch (*s) 
805     {
806       case 'l':
807         mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
808         break;
809       case 'r':
810         mutt_bit_set (idata->ctx->rights, M_ACL_READ);
811         break;
812       case 's':
813         mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
814         break;
815       case 'w':
816         mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
817         break;
818       case 'i':
819         mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
820         break;
821       case 'p':
822         mutt_bit_set (idata->ctx->rights, M_ACL_POST);
823         break;
824       case 'a':
825         mutt_bit_set (idata->ctx->rights, M_ACL_ADMIN);
826         break;
827       case 'k':
828         mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
829         break;
830       case 'x':
831         mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
832         break;
833       case 't':
834         mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
835         break;
836       case 'e':
837         mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
838         break;
839
840         /* obsolete rights */
841       case 'c':
842         mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
843         mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
844         break;
845       case 'd':
846         mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
847         mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
848         break;
849       default:
850         dprint(1, (debugfile, "Unknown right: %c\n", *s));
851     }
852     s++;
853   }
854 }
855
856 /* This should be optimised (eg with a tree or hash) */
857 static int uid2msgno (IMAP_DATA* idata, unsigned int uid)
858 {
859   int i;
860   
861   for (i = 0; i < idata->ctx->msgcount; i++)
862   {
863     HEADER* h = idata->ctx->hdrs[i];
864     if (HEADER_DATA(h)->uid == uid)
865       return i;
866   }
867   
868   return -1;
869 }
870
871 /* cmd_parse_search: store SEARCH response for later use */
872 static void cmd_parse_search (IMAP_DATA* idata, const char* s)
873 {
874   unsigned int uid;
875   int msgno;
876
877   dprint (2, (debugfile, "Handling SEARCH\n"));
878
879   while ((s = imap_next_word ((char*)s)) && *s != '\0')
880   {
881     uid = atoi (s);
882     msgno = uid2msgno (idata, uid);
883     
884     if (msgno >= 0)
885       idata->ctx->hdrs[uid2msgno (idata, uid)]->matched = 1;
886   }
887 }
888
889 /* first cut: just do buffy update. Later we may wish to cache all
890  * mailbox information, even that not desired by buffy */
891 static void cmd_parse_status (IMAP_DATA* idata, char* s)
892 {
893   char* mailbox;
894   char* value;
895   BUFFY* inc;
896   IMAP_MBOX mx;
897   int count;
898   IMAP_STATUS *status;
899   unsigned int olduv, oldun;
900   long litlen;
901
902   mailbox = imap_next_word (s);
903
904   /* We need a real tokenizer. */
905   if (!imap_get_literal_count (mailbox, &litlen))
906   {
907     if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
908     {
909       idata->status = IMAP_FATAL;
910       return;
911     }
912     mailbox = idata->buf;
913     s = mailbox + litlen;
914     *s = '\0';
915     s++;
916     SKIPWS(s);
917   }
918   else
919   {
920     s = imap_next_word (mailbox);
921     *(s - 1) = '\0';
922     imap_unmunge_mbox_name (mailbox);
923   }
924
925   status = imap_mboxcache_get (idata, mailbox, 1);
926   olduv = status->uidvalidity;
927   oldun = status->uidnext;
928
929   if (*s++ != '(')
930   {
931     dprint (1, (debugfile, "Error parsing STATUS\n"));
932     return;
933   }
934   while (*s && *s != ')')
935   {
936     value = imap_next_word (s);
937     count = strtol (value, &value, 10);
938
939     if (!ascii_strncmp ("MESSAGES", s, 8))
940       status->messages = count;
941     else if (!ascii_strncmp ("RECENT", s, 6))
942       status->recent = count;
943     else if (!ascii_strncmp ("UIDNEXT", s, 7))
944       status->uidnext = count;
945     else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
946       status->uidvalidity = count;
947     else if (!ascii_strncmp ("UNSEEN", s, 6))
948       status->unseen = count;
949
950     s = value;
951     if (*s && *s != ')')
952       s = imap_next_word (s);
953   }
954   dprint (3, (debugfile, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
955               status->name, status->uidvalidity, status->uidnext,
956               status->messages, status->recent, status->unseen));
957
958   /* caller is prepared to handle the result herself */
959   if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
960   {
961     memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
962     return;
963   }
964
965   dprint (3, (debugfile, "Running default STATUS handler\n"));
966
967   /* should perhaps move this code back to imap_buffy_check */
968   for (inc = Incoming; inc; inc = inc->next)
969   {
970     if (inc->magic != M_IMAP)
971       continue;
972     
973     if (imap_parse_path (inc->path, &mx) < 0)
974     {
975       dprint (1, (debugfile, "Error parsing mailbox %s, skipping\n", inc->path));
976       continue;
977     }
978     /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
979     
980     if (imap_account_match (&idata->conn->account, &mx.account))
981     {
982       if (mx.mbox)
983       {
984         value = safe_strdup (mx.mbox);
985         imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
986         FREE (&mx.mbox);
987       }
988       else
989         value = safe_strdup ("INBOX");
990
991       if (value && !imap_mxcmp (mailbox, value))
992       {
993         dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
994                     mailbox, olduv, oldun, status->unseen));
995         
996         if (olduv && olduv == status->uidvalidity)
997         {
998           if (oldun < status->uidnext)
999             inc->new = status->unseen;
1000         }
1001         else if (!olduv && !oldun)
1002           /* first check per session, use recent. might need a flag for this. */
1003           inc->new = status->recent;
1004         else
1005           inc->new = status->unseen;
1006
1007         if (inc->new)
1008           /* force back to keep detecting new mail until the mailbox is
1009              opened */
1010           status->uidnext = oldun;
1011
1012         FREE (&value);
1013         return;
1014       }
1015
1016       FREE (&value);
1017     }
1018
1019     FREE (&mx.mbox);
1020   }
1021 }