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>
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.
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.
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.
21 /* command.c: routines for sending commands to an IMAP server and parsing
29 #include "imap_private.h"
37 #define IMAP_CMD_BUFSIZE 512
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);
56 static char *Capabilities[] = {
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)
77 return cmd_start (idata, cmdstr, 0);
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_
84 int imap_cmd_step (IMAP_DATA* idata)
92 if (idata->status == IMAP_FATAL)
94 cmd_handle_fatal (idata);
98 /* read into buffer, expanding buffer as necessary until we have a full
102 if (len == idata->blen)
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",
110 /* back up over '\0' */
113 c = mutt_socket_readln (idata->buf + len, idata->blen - len, idata->conn);
116 dprint (1, (debugfile, "imap_cmd_step: Error reading server response.\n"));
117 cmd_handle_fatal (idata);
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);
128 /* don't let one large string make cmd->buf hog memory forever */
129 if ((idata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
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));
136 idata->lastread = time (NULL);
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))
144 /* server demands a continuation response from us */
145 if (idata->buf[0] == '+')
146 return IMAP_CMD_RESPOND;
148 /* look for tagged command completions */
149 rc = IMAP_CMD_CONTINUE;
153 cmd = &idata->cmds[c];
154 if (cmd->state == IMAP_CMD_NEW)
156 if (!ascii_strncmp (idata->buf, cmd->seq, SEQLEN)) {
159 /* first command in queue has finished - move queue pointer up */
160 idata->lastcmd = (idata->lastcmd + 1) % idata->cmdslots;
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. */
171 c = (c + 1) % idata->cmdslots;
173 while (c != idata->nextcmd);
176 rc = IMAP_CMD_CONTINUE;
179 dprint (3, (debugfile, "IMAP queue drained\n"));
180 imap_cmd_finish (idata);
187 /* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
188 int imap_code (const char *s)
190 return cmd_status (s) == IMAP_CMD_OK;
193 /* imap_cmd_trailer: extra information after tagged command response if any */
194 const char* imap_cmd_trailer (IMAP_DATA* idata)
196 static const char* notrailer = "";
197 const char* s = idata->buf;
201 dprint (2, (debugfile, "imap_cmd_trailer: not a tagged response"));
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)))
210 dprint (2, (debugfile, "imap_cmd_trailer: not a command completion: %s",
215 s = imap_next_word ((char *)s);
222 /* imap_exec: execute a command, and wait for the response from the server.
223 * Also, handle untagged responses.
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
231 int imap_exec (IMAP_DATA* idata, const char* cmdstr, int flags)
235 if ((rc = cmd_start (idata, cmdstr, flags)) < 0)
240 cmd_handle_fatal (idata);
244 if (flags & IMAP_CMD_QUEUE)
248 rc = imap_cmd_step (idata);
249 while (rc == IMAP_CMD_CONTINUE);
251 if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
254 if (rc != IMAP_CMD_OK)
256 if (flags & IMAP_CMD_FAIL_OK && idata->status != IMAP_FATAL)
259 dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->buf));
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)
272 if (idata->status == IMAP_FATAL)
274 cmd_handle_fatal (idata);
278 if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
281 if (idata->reopen & IMAP_REOPEN_ALLOW)
283 int count = idata->newMailCount;
285 if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
286 (idata->reopen & IMAP_NEWMAIL_PENDING)
287 && count > idata->ctx->msgcount)
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);
296 else if (idata->reopen & IMAP_EXPUNGE_PENDING)
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);
312 /* imap_cmd_idle: Enter the IDLE state. */
313 int imap_cmd_idle (IMAP_DATA* idata)
317 imap_cmd_start (idata, "IDLE");
319 rc = imap_cmd_step (idata);
320 while (rc == IMAP_CMD_CONTINUE);
322 if (rc == IMAP_CMD_RESPOND)
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");
330 if (rc != IMAP_CMD_OK)
332 dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
339 static int cmd_queue_full (IMAP_DATA* idata)
341 if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
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)
353 if (cmd_queue_full (idata))
355 dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
359 cmd = idata->cmds + idata->nextcmd;
360 idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
362 snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
363 if (idata->seqno > 9999)
366 cmd->state = IMAP_CMD_NEW;
371 /* queues command. If the queue is full, attempts to drain it. */
372 static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
377 if (cmd_queue_full (idata))
379 dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
381 rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
383 if (rc < 0 && rc != -2)
387 if (!(cmd = cmd_new (idata)))
390 if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
396 static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
400 if (idata->status == IMAP_FATAL)
402 cmd_handle_fatal (idata);
406 if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
409 if (flags & IMAP_CMD_QUEUE)
412 if (idata->cmdbuf->dptr == idata->cmdbuf->data)
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;
419 /* unidle when command queue is flushed */
420 if (idata->state == IMAP_IDLE)
421 idata->state = IMAP_SELECTED;
423 return (rc < 0) ? IMAP_CMD_BAD : 0;
426 /* parse response line for tagged OK/NO/BAD */
427 static int cmd_status (const char *s)
429 s = imap_next_word((char*)s);
431 if (!ascii_strncasecmp("OK", s, 2))
433 if (!ascii_strncasecmp("NO", s, 2))
439 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
440 static void cmd_handle_fatal (IMAP_DATA* idata)
442 idata->status = IMAP_FATAL;
444 if ((idata->state >= IMAP_SELECTED) &&
445 (idata->reopen & IMAP_REOPEN_ALLOW))
447 mx_fastclose_mailbox (idata->ctx);
448 mutt_error (_("Mailbox closed"));
450 idata->state = IMAP_DISCONNECTED;
453 if (idata->state < IMAP_SELECTED)
454 imap_close_connection (idata);
457 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
458 static int cmd_handle_untagged (IMAP_DATA* idata)
464 s = imap_next_word (idata->buf);
465 pn = imap_next_word (s);
467 if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s))
470 s = imap_next_word (s);
472 /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
473 * connection, so update that one.
475 if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
477 dprint (2, (debugfile, "Handling EXISTS\n"));
479 /* new mail arrived */
482 if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
483 count < idata->ctx->msgcount)
485 /* Notes 6.0.3 has a tendency to report fewer messages exist than
487 dprint (1, (debugfile, "Message count is out of sync"));
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"));
497 if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
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;
504 idata->newMailCount = count;
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);
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)
531 dprint (2, (debugfile, "Handling BYE\n"));
533 /* check if we're logging out */
534 if (idata->status == IMAP_BYE)
537 /* server shut down our connection */
540 mutt_error ("%s", s);
542 cmd_handle_fatal (idata);
546 else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
548 dprint (2, (debugfile, "Handling untagged NO\n"));
550 /* Display the warning message from the server */
551 mutt_error ("%s", s+3);
558 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
560 static void cmd_parse_capability (IMAP_DATA* idata, char* s)
565 dprint (3, (debugfile, "Handling CAPABILITY\n"));
567 s = imap_next_word (s);
568 if ((bracket = strchr (s, ']')))
570 FREE(&idata->capstr);
571 idata->capstr = safe_strdup (s);
573 memset (idata->capabilities, 0, sizeof (idata->capabilities));
577 for (x = 0; x < CAPMAX; x++)
578 if (imap_wordcasecmp(Capabilities[x], s) == 0)
580 mutt_bit_set (idata->capabilities, x);
583 s = imap_next_word (s);
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)
594 dprint (2, (debugfile, "Handling EXPUNGE\n"));
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++)
604 h = idata->ctx->hdrs[cur];
606 if (h->index+1 == expno)
608 else if (h->index+1 > expno)
612 idata->reopen |= IMAP_EXPUNGE_PENDING;
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)
624 dprint (3, (debugfile, "Handling FETCH\n"));
628 if (msgno <= idata->ctx->msgcount)
629 /* see cmd_parse_expunge */
630 for (cur = 0; cur < idata->ctx->msgcount; cur++)
632 h = idata->ctx->hdrs[cur];
634 if (h && h->active && h->index+1 == msgno)
636 dprint (2, (debugfile, "Message UID %d updated\n", HEADER_DATA(h)->uid));
645 dprint (3, (debugfile, "FETCH response ignored for this message\n"));
650 s = imap_next_word (s);
651 s = imap_next_word (s);
655 dprint (1, (debugfile, "Malformed FETCH response"));
660 if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
662 dprint (2, (debugfile, "Only handle FLAGS updates\n"));
666 /* If server flags could conflict with mutt's flags, reopen the mailbox. */
668 idata->reopen |= IMAP_EXPUNGE_PENDING;
670 imap_set_flags (idata, h, s);
671 idata->check_status = IMAP_FLAGS_PENDING;
675 static void cmd_parse_list (IMAP_DATA* idata, char* s)
679 char delimbuf[5]; /* worst case: "\\"\0 */
682 if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
683 list = (IMAP_LIST*)idata->cmddata;
687 memset (list, 0, sizeof (IMAP_LIST));
690 s = imap_next_word (s);
693 dprint (1, (debugfile, "Bad LIST response\n"));
699 if (!ascii_strncasecmp (s, "\\NoSelect", 9))
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;
707 s = imap_next_word (s);
713 if (ascii_strncasecmp (s, "NIL", 3))
716 safe_strcat (delimbuf, 5, s);
717 imap_unquote_string (delimbuf);
718 list->delim = delimbuf[0];
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))
726 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
728 idata->status = IMAP_FATAL;
731 list->name = idata->buf;
735 imap_unmunge_mbox_name (s);
739 if (list->name[0] == '\0')
741 idata->delim = list->delim;
742 dprint (3, (debugfile, "Root delimiter: %c\n", idata->delim));
746 static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
754 if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
756 /* caller will handle response itself */
757 cmd_parse_list (idata, s);
761 if (!option (OPTIMAPCHECKSUBSCRIBED))
764 idata->cmdtype = IMAP_CT_LIST;
765 idata->cmddata = &list;
766 cmd_parse_list (idata, s);
767 idata->cmddata = NULL;
771 dprint (3, (debugfile, "Subscribing to %s\n", list.name));
773 strfcpy (buf, "mailboxes \"", sizeof (buf));
774 mutt_account_tourl (&idata->conn->account, &url);
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))
781 url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
782 safe_strcat (buf, sizeof (buf), "\"");
783 memset (&token, 0, sizeof (token));
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));
791 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
792 static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
794 dprint (2, (debugfile, "Handling MYRIGHTS\n"));
796 s = imap_next_word ((char*)s);
797 s = imap_next_word ((char*)s);
799 /* zero out current rights set */
800 memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
802 while (*s && !isspace((unsigned char) *s))
807 mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
810 mutt_bit_set (idata->ctx->rights, M_ACL_READ);
813 mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
816 mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
819 mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
822 mutt_bit_set (idata->ctx->rights, M_ACL_POST);
825 mutt_bit_set (idata->ctx->rights, M_ACL_ADMIN);
828 mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
831 mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
834 mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
837 mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
840 /* obsolete rights */
842 mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
843 mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
846 mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
847 mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
850 dprint(1, (debugfile, "Unknown right: %c\n", *s));
856 /* This should be optimised (eg with a tree or hash) */
857 static int uid2msgno (IMAP_DATA* idata, unsigned int uid)
861 for (i = 0; i < idata->ctx->msgcount; i++)
863 HEADER* h = idata->ctx->hdrs[i];
864 if (HEADER_DATA(h)->uid == uid)
871 /* cmd_parse_search: store SEARCH response for later use */
872 static void cmd_parse_search (IMAP_DATA* idata, const char* s)
877 dprint (2, (debugfile, "Handling SEARCH\n"));
879 while ((s = imap_next_word ((char*)s)) && *s != '\0')
882 msgno = uid2msgno (idata, uid);
885 idata->ctx->hdrs[uid2msgno (idata, uid)]->matched = 1;
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)
899 unsigned int olduv, oldun;
902 mailbox = imap_next_word (s);
904 /* We need a real tokenizer. */
905 if (!imap_get_literal_count (mailbox, &litlen))
907 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
909 idata->status = IMAP_FATAL;
912 mailbox = idata->buf;
913 s = mailbox + litlen;
920 s = imap_next_word (mailbox);
922 imap_unmunge_mbox_name (mailbox);
925 status = imap_mboxcache_get (idata, mailbox, 1);
926 olduv = status->uidvalidity;
927 oldun = status->uidnext;
931 dprint (1, (debugfile, "Error parsing STATUS\n"));
934 while (*s && *s != ')')
936 value = imap_next_word (s);
937 count = strtol (value, &value, 10);
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;
952 s = imap_next_word (s);
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));
958 /* caller is prepared to handle the result herself */
959 if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
961 memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
965 dprint (3, (debugfile, "Running default STATUS handler\n"));
967 /* should perhaps move this code back to imap_buffy_check */
968 for (inc = Incoming; inc; inc = inc->next)
970 if (inc->magic != M_IMAP)
973 if (imap_parse_path (inc->path, &mx) < 0)
975 dprint (1, (debugfile, "Error parsing mailbox %s, skipping\n", inc->path));
978 /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
980 if (imap_account_match (&idata->conn->account, &mx.account))
984 value = safe_strdup (mx.mbox);
985 imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
989 value = safe_strdup ("INBOX");
991 if (value && !imap_mxcmp (mailbox, value))
993 dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
994 mailbox, olduv, oldun, status->unseen));
996 if (olduv && olduv == status->uidvalidity)
998 if (oldun < status->uidnext)
999 inc->new = status->unseen;
1001 else if (!olduv && !oldun)
1002 /* first check per session, use recent. might need a flag for this. */
1003 inc->new = status->recent;
1005 inc->new = status->unseen;
1008 /* force back to keep detecting new mail until the mailbox is
1010 status->uidnext = oldun;