]> git.llucax.com Git - software/mutt-debian.git/blob - imap/command.c
Move Mutt with NNTP support to mutt-nntp package
[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   {
237     cmd_handle_fatal (idata);
238     return -1;
239   }
240
241   if (flags & IMAP_CMD_QUEUE)
242     return 0;
243
244   do
245     rc = imap_cmd_step (idata);
246   while (rc == IMAP_CMD_CONTINUE);
247
248   if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
249     return -2;
250
251   if (rc != IMAP_CMD_OK)
252   {
253     if (flags & IMAP_CMD_FAIL_OK && idata->status != IMAP_FATAL)
254       return -2;
255
256     dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->buf));
257     return -1;
258   }
259
260   return 0;
261 }
262
263 /* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
264  *   detected, do expunge). Called automatically by imap_cmd_step, but
265  *   may be called at any time. Called by imap_check_mailbox just before
266  *   the index is refreshed, for instance. */
267 void imap_cmd_finish (IMAP_DATA* idata)
268 {
269   if (idata->status == IMAP_FATAL)
270   {
271     cmd_handle_fatal (idata);
272     return;
273   }
274
275   if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
276     return;
277   
278   if (idata->reopen & IMAP_REOPEN_ALLOW)
279   {
280     int count = idata->newMailCount;
281
282     if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
283         (idata->reopen & IMAP_NEWMAIL_PENDING)
284         && count > idata->ctx->msgcount)
285     {
286       /* read new mail messages */
287       dprint (2, (debugfile, "imap_cmd_finish: Fetching new mail\n"));
288       /* check_status: curs_main uses imap_check_mailbox to detect
289        *   whether the index needs updating */
290       idata->check_status = IMAP_NEWMAIL_PENDING;
291       imap_read_headers (idata, idata->ctx->msgcount, count-1);
292     }
293     else if (idata->reopen & IMAP_EXPUNGE_PENDING)
294     {
295       dprint (2, (debugfile, "imap_cmd_finish: Expunging mailbox\n"));
296       imap_expunge_mailbox (idata);
297       /* Detect whether we've gotten unexpected EXPUNGE messages */
298       if (idata->reopen & IMAP_EXPUNGE_PENDING &&
299           !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
300         idata->check_status = IMAP_EXPUNGE_PENDING;
301       idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
302                          IMAP_EXPUNGE_EXPECTED);
303     }
304   }
305
306   idata->status = 0;
307 }
308
309 /* imap_cmd_idle: Enter the IDLE state. */
310 int imap_cmd_idle (IMAP_DATA* idata)
311 {
312   int rc;
313
314   imap_cmd_start (idata, "IDLE");
315   do
316     rc = imap_cmd_step (idata);
317   while (rc == IMAP_CMD_CONTINUE);
318
319   if (rc == IMAP_CMD_RESPOND)
320   {
321     /* successfully entered IDLE state */
322     idata->state = IMAP_IDLE;
323     /* queue automatic exit when next command is issued */
324     mutt_buffer_printf (idata->cmdbuf, "DONE\r\n");
325     rc = IMAP_CMD_OK;
326   }
327   if (rc != IMAP_CMD_OK)
328   {
329     dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
330     return -1;
331   }
332   
333   return 0;
334 }
335
336 static int cmd_queue_full (IMAP_DATA* idata)
337 {
338   if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
339     return 1;
340
341   return 0;
342 }
343
344 /* sets up a new command control block and adds it to the queue.
345  * Returns NULL if the pipeline is full. */
346 static IMAP_COMMAND* cmd_new (IMAP_DATA* idata)
347 {
348   IMAP_COMMAND* cmd;
349
350   if (cmd_queue_full (idata))
351   {
352     dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
353     return NULL;
354   }
355
356   cmd = idata->cmds + idata->nextcmd;
357   idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
358
359   snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
360   if (idata->seqno > 9999)
361     idata->seqno = 0;
362
363   cmd->state = IMAP_CMD_NEW;
364
365   return cmd;
366 }
367
368 /* queues command. If the queue is full, attempts to drain it. */
369 static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
370 {
371   IMAP_COMMAND* cmd;
372   int rc;
373
374   if (cmd_queue_full (idata))
375   {
376     dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
377
378     rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
379
380     if (rc < 0 && rc != -2)
381       return rc;
382   }
383
384   if (!(cmd = cmd_new (idata)))
385     return IMAP_CMD_BAD;
386
387   if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
388     return IMAP_CMD_BAD;
389
390   return 0;
391 }
392
393 static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
394 {
395   int rc;
396
397   if (idata->status == IMAP_FATAL)
398   {
399     cmd_handle_fatal (idata);
400     return -1;
401   }
402
403   if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
404     return rc;
405
406   if (flags & IMAP_CMD_QUEUE)
407     return 0;
408
409   if (idata->cmdbuf->dptr == idata->cmdbuf->data)
410     return IMAP_CMD_BAD;
411
412   rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1,
413                             flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
414   idata->cmdbuf->dptr = idata->cmdbuf->data;
415
416   /* unidle when command queue is flushed */
417   if (idata->state == IMAP_IDLE)
418     idata->state = IMAP_SELECTED;
419
420   return (rc < 0) ? IMAP_CMD_BAD : 0;
421 }
422
423 /* parse response line for tagged OK/NO/BAD */
424 static int cmd_status (const char *s)
425 {
426   s = imap_next_word((char*)s);
427   
428   if (!ascii_strncasecmp("OK", s, 2))
429     return IMAP_CMD_OK;
430   if (!ascii_strncasecmp("NO", s, 2))
431     return IMAP_CMD_NO;
432
433   return IMAP_CMD_BAD;
434 }
435
436 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
437 static void cmd_handle_fatal (IMAP_DATA* idata)
438 {
439   idata->status = IMAP_FATAL;
440
441   if ((idata->state >= IMAP_SELECTED) &&
442       (idata->reopen & IMAP_REOPEN_ALLOW))
443   {
444     mx_fastclose_mailbox (idata->ctx);
445     mutt_error (_("Mailbox closed"));
446     mutt_sleep (1);
447     idata->state = IMAP_DISCONNECTED;
448   }
449
450   if (idata->state < IMAP_SELECTED)
451     imap_close_connection (idata);
452 }
453
454 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
455 static int cmd_handle_untagged (IMAP_DATA* idata)
456 {
457   char* s;
458   char* pn;
459   int count;
460
461   s = imap_next_word (idata->buf);
462   pn = imap_next_word (s);
463
464   if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s))
465   {
466     pn = s;
467     s = imap_next_word (s);
468
469     /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
470      * connection, so update that one.
471      */
472     if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
473     {
474       dprint (2, (debugfile, "Handling EXISTS\n"));
475
476       /* new mail arrived */
477       count = atoi (pn);
478
479       if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
480            count < idata->ctx->msgcount)
481       {
482         /* Notes 6.0.3 has a tendency to report fewer messages exist than
483          * it should. */
484         dprint (1, (debugfile, "Message count is out of sync"));
485         return 0;
486       }
487       /* at least the InterChange server sends EXISTS messages freely,
488        * even when there is no new mail */
489       else if (count == idata->ctx->msgcount)
490         dprint (3, (debugfile,
491           "cmd_handle_untagged: superfluous EXISTS message.\n"));
492       else
493       {
494         if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
495         {
496           dprint (2, (debugfile,
497             "cmd_handle_untagged: New mail in %s - %d messages total.\n",
498             idata->mailbox, count));
499           idata->reopen |= IMAP_NEWMAIL_PENDING;
500         }
501         idata->newMailCount = count;
502       }
503     }
504     /* pn vs. s: need initial seqno */
505     else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
506       cmd_parse_expunge (idata, pn);
507     else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
508       cmd_parse_fetch (idata, pn);
509   }
510   else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
511     cmd_parse_capability (idata, s);
512   else if (!ascii_strncasecmp ("OK [CAPABILITY", s, 14))
513     cmd_parse_capability (idata, pn);
514   else if (!ascii_strncasecmp ("OK [CAPABILITY", pn, 14))
515     cmd_parse_capability (idata, imap_next_word (pn));
516   else if (ascii_strncasecmp ("LIST", s, 4) == 0)
517     cmd_parse_list (idata, s);
518   else if (ascii_strncasecmp ("LSUB", s, 4) == 0)
519     cmd_parse_lsub (idata, s);
520   else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
521     cmd_parse_myrights (idata, s);
522   else if (ascii_strncasecmp ("SEARCH", s, 6) == 0)
523     cmd_parse_search (idata, s);
524   else if (ascii_strncasecmp ("STATUS", s, 6) == 0)
525     cmd_parse_status (idata, s);
526   else if (ascii_strncasecmp ("BYE", s, 3) == 0)
527   {
528     dprint (2, (debugfile, "Handling BYE\n"));
529
530     /* check if we're logging out */
531     if (idata->status == IMAP_BYE)
532       return 0;
533
534     /* server shut down our connection */
535     s += 3;
536     SKIPWS (s);
537     mutt_error ("%s", s);
538     mutt_sleep (2);
539     cmd_handle_fatal (idata);
540
541     return -1;
542   }
543   else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
544   {
545     dprint (2, (debugfile, "Handling untagged NO\n"));
546
547     /* Display the warning message from the server */
548     mutt_error ("%s", s+3);
549     mutt_sleep (2);
550   }
551
552   return 0;
553 }
554
555 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
556  *   response */
557 static void cmd_parse_capability (IMAP_DATA* idata, char* s)
558 {
559   int x;
560   char* bracket;
561
562   dprint (3, (debugfile, "Handling CAPABILITY\n"));
563
564   s = imap_next_word (s);
565   if ((bracket = strchr (s, ']')))
566     *bracket = '\0';
567   FREE(&idata->capstr);
568   idata->capstr = safe_strdup (s);
569
570   memset (idata->capabilities, 0, sizeof (idata->capabilities));
571
572   while (*s)
573   {
574     for (x = 0; x < CAPMAX; x++)
575       if (imap_wordcasecmp(Capabilities[x], s) == 0)
576       {
577         mutt_bit_set (idata->capabilities, x);
578         break;
579       }
580     s = imap_next_word (s);
581   }
582 }
583
584 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
585  *   be reopened at our earliest convenience */
586 static void cmd_parse_expunge (IMAP_DATA* idata, const char* s)
587 {
588   int expno, cur;
589   HEADER* h;
590
591   dprint (2, (debugfile, "Handling EXPUNGE\n"));
592
593   expno = atoi (s);
594
595   /* walk headers, zero seqno of expunged message, decrement seqno of those
596    * above. Possibly we could avoid walking the whole list by resorting
597    * and guessing a good starting point, but I'm guessing the resort would
598    * nullify the gains */
599   for (cur = 0; cur < idata->ctx->msgcount; cur++)
600   {
601     h = idata->ctx->hdrs[cur];
602
603     if (h->index+1 == expno)
604       h->index = -1;
605     else if (h->index+1 > expno)
606       h->index--;
607   }
608
609   idata->reopen |= IMAP_EXPUNGE_PENDING;
610 }
611
612 /* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
613  *   handles unanticipated FETCH responses, and only FLAGS data. We get
614  *   these if another client has changed flags for a mailbox we've selected.
615  *   Of course, a lot of code here duplicates code in message.c. */
616 static void cmd_parse_fetch (IMAP_DATA* idata, char* s)
617 {
618   int msgno, cur;
619   HEADER* h = NULL;
620
621   dprint (3, (debugfile, "Handling FETCH\n"));
622
623   msgno = atoi (s);
624   
625   if (msgno <= idata->ctx->msgcount)
626   /* see cmd_parse_expunge */
627     for (cur = 0; cur < idata->ctx->msgcount; cur++)
628     {
629       h = idata->ctx->hdrs[cur];
630       
631       if (h && h->active && h->index+1 == msgno)
632       {
633         dprint (2, (debugfile, "Message UID %d updated\n", HEADER_DATA(h)->uid));
634         break;
635       }
636       
637       h = NULL;
638     }
639   
640   if (!h)
641   {
642     dprint (3, (debugfile, "FETCH response ignored for this message\n"));
643     return;
644   }
645   
646   /* skip FETCH */
647   s = imap_next_word (s);
648   s = imap_next_word (s);
649
650   if (*s != '(')
651   {
652     dprint (1, (debugfile, "Malformed FETCH response"));
653     return;
654   }
655   s++;
656
657   if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
658   {
659     dprint (2, (debugfile, "Only handle FLAGS updates\n"));
660     return;
661   }
662
663   /* If server flags could conflict with mutt's flags, reopen the mailbox. */
664   if (h->changed)
665     idata->reopen |= IMAP_EXPUNGE_PENDING;
666   else {
667     imap_set_flags (idata, h, s);
668     idata->check_status = IMAP_FLAGS_PENDING;
669   }
670 }
671
672 static void cmd_parse_list (IMAP_DATA* idata, char* s)
673 {
674   IMAP_LIST* list;
675   IMAP_LIST lb;
676   char delimbuf[5]; /* worst case: "\\"\0 */
677   long litlen;
678
679   if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
680     list = (IMAP_LIST*)idata->cmddata;
681   else
682     list = &lb;
683
684   memset (list, 0, sizeof (IMAP_LIST));
685
686   /* flags */
687   s = imap_next_word (s);
688   if (*s != '(')
689   {
690     dprint (1, (debugfile, "Bad LIST response\n"));
691     return;
692   }
693   s++;
694   while (*s)
695   {
696     if (!ascii_strncasecmp (s, "\\NoSelect", 9))
697       list->noselect = 1;
698     else if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
699       list->noinferiors = 1;
700     /* See draft-gahrns-imap-child-mailbox-?? */
701     else if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
702       list->noinferiors = 1;
703     
704     s = imap_next_word (s);
705     if (*(s - 2) == ')')
706       break;
707   }
708
709   /* Delimiter */
710   if (ascii_strncasecmp (s, "NIL", 3))
711   {
712     delimbuf[0] = '\0';
713     safe_strcat (delimbuf, 5, s); 
714     imap_unquote_string (delimbuf);
715     list->delim = delimbuf[0];
716   }
717
718   /* Name */
719   s = imap_next_word (s);
720   /* Notes often responds with literals here. We need a real tokenizer. */
721   if (!imap_get_literal_count (s, &litlen))
722   {
723     if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
724     {
725       idata->status = IMAP_FATAL;
726       return;
727     }
728     list->name = idata->buf;
729   }
730   else
731   {
732     imap_unmunge_mbox_name (s);
733     list->name = s;
734   }
735
736   if (list->name[0] == '\0')
737   {
738     idata->delim = list->delim;
739     dprint (3, (debugfile, "Root delimiter: %c\n", idata->delim));
740   }
741 }
742
743 static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
744 {
745   char buf[STRING];
746   char errstr[STRING];
747   BUFFER err, token;
748   ciss_url_t url;
749   IMAP_LIST list;
750
751   if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
752   {
753     /* caller will handle response itself */
754     cmd_parse_list (idata, s);
755     return;
756   }
757
758   if (!option (OPTIMAPCHECKSUBSCRIBED))
759     return;
760
761   idata->cmdtype = IMAP_CT_LIST;
762   idata->cmddata = &list;
763   cmd_parse_list (idata, s);
764   idata->cmddata = NULL;
765   if (!list.name)
766     return;
767
768   dprint (3, (debugfile, "Subscribing to %s\n", list.name));
769
770   strfcpy (buf, "mailboxes \"", sizeof (buf));
771   mutt_account_tourl (&idata->conn->account, &url);
772   /* escape \ and " */
773   imap_quote_string(errstr, sizeof (errstr), list.name);
774   url.path = errstr + 1;
775   url.path[strlen(url.path) - 1] = '\0';
776   if (!mutt_strcmp (url.user, ImapUser))
777     url.user = NULL;
778   url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
779   safe_strcat (buf, sizeof (buf), "\"");
780   memset (&token, 0, sizeof (token));
781   err.data = errstr;
782   err.dsize = sizeof (errstr);
783   if (mutt_parse_rc_line (buf, &token, &err))
784     dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
785   FREE (&token.data);
786 }
787
788 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
789 static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
790 {
791   dprint (2, (debugfile, "Handling MYRIGHTS\n"));
792
793   s = imap_next_word ((char*)s);
794   s = imap_next_word ((char*)s);
795
796   /* zero out current rights set */
797   memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
798
799   while (*s && !isspace((unsigned char) *s))
800   {
801     switch (*s) 
802     {
803       case 'l':
804         mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
805         break;
806       case 'r':
807         mutt_bit_set (idata->ctx->rights, M_ACL_READ);
808         break;
809       case 's':
810         mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
811         break;
812       case 'w':
813         mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
814         break;
815       case 'i':
816         mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
817         break;
818       case 'p':
819         mutt_bit_set (idata->ctx->rights, M_ACL_POST);
820         break;
821       case 'a':
822         mutt_bit_set (idata->ctx->rights, M_ACL_ADMIN);
823         break;
824       case 'k':
825         mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
826         break;
827       case 'x':
828         mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
829         break;
830       case 't':
831         mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
832         break;
833       case 'e':
834         mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
835         break;
836
837         /* obsolete rights */
838       case 'c':
839         mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
840         mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
841         break;
842       case 'd':
843         mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
844         mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
845         break;
846       default:
847         dprint(1, (debugfile, "Unknown right: %c\n", *s));
848     }
849     s++;
850   }
851 }
852
853 /* This should be optimised (eg with a tree or hash) */
854 static int uid2msgno (IMAP_DATA* idata, unsigned int uid)
855 {
856   int i;
857   
858   for (i = 0; i < idata->ctx->msgcount; i++)
859   {
860     HEADER* h = idata->ctx->hdrs[i];
861     if (HEADER_DATA(h)->uid == uid)
862       return i;
863   }
864   
865   return -1;
866 }
867
868 /* cmd_parse_search: store SEARCH response for later use */
869 static void cmd_parse_search (IMAP_DATA* idata, const char* s)
870 {
871   unsigned int uid;
872   int msgno;
873
874   dprint (2, (debugfile, "Handling SEARCH\n"));
875
876   while ((s = imap_next_word ((char*)s)) && *s != '\0')
877   {
878     uid = atoi (s);
879     msgno = uid2msgno (idata, uid);
880     
881     if (msgno >= 0)
882       idata->ctx->hdrs[uid2msgno (idata, uid)]->matched = 1;
883   }
884 }
885
886 /* first cut: just do buffy update. Later we may wish to cache all
887  * mailbox information, even that not desired by buffy */
888 static void cmd_parse_status (IMAP_DATA* idata, char* s)
889 {
890   char* mailbox;
891   char* value;
892   BUFFY* inc;
893   IMAP_MBOX mx;
894   int count;
895   IMAP_STATUS *status;
896   unsigned int olduv, oldun;
897   long litlen;
898
899   mailbox = imap_next_word (s);
900
901   /* We need a real tokenizer. */
902   if (!imap_get_literal_count (mailbox, &litlen))
903   {
904     if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
905     {
906       idata->status = IMAP_FATAL;
907       return;
908     }
909     mailbox = idata->buf;
910     s = mailbox + litlen;
911     *s = '\0';
912     s++;
913     SKIPWS(s);
914   }
915   else
916   {
917     s = imap_next_word (mailbox);
918     *(s - 1) = '\0';
919     imap_unmunge_mbox_name (mailbox);
920   }
921
922   status = imap_mboxcache_get (idata, mailbox, 1);
923   olduv = status->uidvalidity;
924   oldun = status->uidnext;
925
926   if (*s++ != '(')
927   {
928     dprint (1, (debugfile, "Error parsing STATUS\n"));
929     return;
930   }
931   while (*s && *s != ')')
932   {
933     value = imap_next_word (s);
934     count = strtol (value, &value, 10);
935
936     if (!ascii_strncmp ("MESSAGES", s, 8))
937       status->messages = count;
938     else if (!ascii_strncmp ("RECENT", s, 6))
939       status->recent = count;
940     else if (!ascii_strncmp ("UIDNEXT", s, 7))
941       status->uidnext = count;
942     else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
943       status->uidvalidity = count;
944     else if (!ascii_strncmp ("UNSEEN", s, 6))
945       status->unseen = count;
946
947     s = value;
948     if (*s && *s != ')')
949       s = imap_next_word (s);
950   }
951   dprint (3, (debugfile, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
952               status->name, status->uidvalidity, status->uidnext,
953               status->messages, status->recent, status->unseen));
954
955   /* caller is prepared to handle the result herself */
956   if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
957   {
958     memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
959     return;
960   }
961
962   dprint (3, (debugfile, "Running default STATUS handler\n"));
963
964   /* should perhaps move this code back to imap_buffy_check */
965   for (inc = Incoming; inc; inc = inc->next)
966   {
967     if (inc->magic != M_IMAP)
968       continue;
969     
970     if (imap_parse_path (inc->path, &mx) < 0)
971     {
972       dprint (1, (debugfile, "Error parsing mailbox %s, skipping\n", inc->path));
973       continue;
974     }
975     /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
976     
977     if (imap_account_match (&idata->conn->account, &mx.account))
978     {
979       if (mx.mbox)
980       {
981         value = safe_strdup (mx.mbox);
982         imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
983         FREE (&mx.mbox);
984       }
985       else
986         value = safe_strdup ("INBOX");
987
988       if (value && !imap_mxcmp (mailbox, value))
989       {
990         dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
991                     mailbox, olduv, oldun, status->unseen));
992         
993         if (option(OPTMAILCHECKRECENT))
994         {
995           if (olduv && olduv == status->uidvalidity)
996           {
997             if (oldun < status->uidnext)
998               inc->new = status->unseen;
999           }
1000           else if (!olduv && !oldun)
1001             /* first check per session, use recent. might need a flag for this. */
1002             inc->new = status->recent;
1003           else
1004             inc->new = status->unseen;
1005         }
1006         else
1007           inc->new = status->unseen;
1008
1009         if (inc->new)
1010           /* force back to keep detecting new mail until the mailbox is
1011              opened */
1012           status->uidnext = oldun;
1013
1014         FREE (&value);
1015         return;
1016       }
1017
1018       FREE (&value);
1019     }
1020
1021     FREE (&mx.mbox);
1022   }
1023 }