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)
237 cmd_handle_fatal (idata);
241 if (flags & IMAP_CMD_QUEUE)
245 rc = imap_cmd_step (idata);
246 while (rc == IMAP_CMD_CONTINUE);
248 if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
251 if (rc != IMAP_CMD_OK)
253 if (flags & IMAP_CMD_FAIL_OK && idata->status != IMAP_FATAL)
256 dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->buf));
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)
269 if (idata->status == IMAP_FATAL)
271 cmd_handle_fatal (idata);
275 if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
278 if (idata->reopen & IMAP_REOPEN_ALLOW)
280 int count = idata->newMailCount;
282 if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
283 (idata->reopen & IMAP_NEWMAIL_PENDING)
284 && count > idata->ctx->msgcount)
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);
293 else if (idata->reopen & IMAP_EXPUNGE_PENDING)
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);
309 /* imap_cmd_idle: Enter the IDLE state. */
310 int imap_cmd_idle (IMAP_DATA* idata)
314 imap_cmd_start (idata, "IDLE");
316 rc = imap_cmd_step (idata);
317 while (rc == IMAP_CMD_CONTINUE);
319 if (rc == IMAP_CMD_RESPOND)
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");
327 if (rc != IMAP_CMD_OK)
329 dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
336 static int cmd_queue_full (IMAP_DATA* idata)
338 if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
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)
350 if (cmd_queue_full (idata))
352 dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
356 cmd = idata->cmds + idata->nextcmd;
357 idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
359 snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
360 if (idata->seqno > 9999)
363 cmd->state = IMAP_CMD_NEW;
368 /* queues command. If the queue is full, attempts to drain it. */
369 static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
374 if (cmd_queue_full (idata))
376 dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
378 rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
380 if (rc < 0 && rc != -2)
384 if (!(cmd = cmd_new (idata)))
387 if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
393 static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
397 if (idata->status == IMAP_FATAL)
399 cmd_handle_fatal (idata);
403 if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
406 if (flags & IMAP_CMD_QUEUE)
409 if (idata->cmdbuf->dptr == idata->cmdbuf->data)
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;
416 /* unidle when command queue is flushed */
417 if (idata->state == IMAP_IDLE)
418 idata->state = IMAP_SELECTED;
420 return (rc < 0) ? IMAP_CMD_BAD : 0;
423 /* parse response line for tagged OK/NO/BAD */
424 static int cmd_status (const char *s)
426 s = imap_next_word((char*)s);
428 if (!ascii_strncasecmp("OK", s, 2))
430 if (!ascii_strncasecmp("NO", s, 2))
436 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
437 static void cmd_handle_fatal (IMAP_DATA* idata)
439 idata->status = IMAP_FATAL;
441 if ((idata->state >= IMAP_SELECTED) &&
442 (idata->reopen & IMAP_REOPEN_ALLOW))
444 mx_fastclose_mailbox (idata->ctx);
445 mutt_error (_("Mailbox closed"));
447 idata->state = IMAP_DISCONNECTED;
450 if (idata->state < IMAP_SELECTED)
451 imap_close_connection (idata);
454 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
455 static int cmd_handle_untagged (IMAP_DATA* idata)
461 s = imap_next_word (idata->buf);
462 pn = imap_next_word (s);
464 if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s))
467 s = imap_next_word (s);
469 /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
470 * connection, so update that one.
472 if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
474 dprint (2, (debugfile, "Handling EXISTS\n"));
476 /* new mail arrived */
479 if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
480 count < idata->ctx->msgcount)
482 /* Notes 6.0.3 has a tendency to report fewer messages exist than
484 dprint (1, (debugfile, "Message count is out of sync"));
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"));
494 if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
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;
501 idata->newMailCount = count;
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);
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)
528 dprint (2, (debugfile, "Handling BYE\n"));
530 /* check if we're logging out */
531 if (idata->status == IMAP_BYE)
534 /* server shut down our connection */
537 mutt_error ("%s", s);
539 cmd_handle_fatal (idata);
543 else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
545 dprint (2, (debugfile, "Handling untagged NO\n"));
547 /* Display the warning message from the server */
548 mutt_error ("%s", s+3);
555 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
557 static void cmd_parse_capability (IMAP_DATA* idata, char* s)
562 dprint (3, (debugfile, "Handling CAPABILITY\n"));
564 s = imap_next_word (s);
565 if ((bracket = strchr (s, ']')))
567 FREE(&idata->capstr);
568 idata->capstr = safe_strdup (s);
570 memset (idata->capabilities, 0, sizeof (idata->capabilities));
574 for (x = 0; x < CAPMAX; x++)
575 if (imap_wordcasecmp(Capabilities[x], s) == 0)
577 mutt_bit_set (idata->capabilities, x);
580 s = imap_next_word (s);
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)
591 dprint (2, (debugfile, "Handling EXPUNGE\n"));
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++)
601 h = idata->ctx->hdrs[cur];
603 if (h->index+1 == expno)
605 else if (h->index+1 > expno)
609 idata->reopen |= IMAP_EXPUNGE_PENDING;
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)
621 dprint (3, (debugfile, "Handling FETCH\n"));
625 if (msgno <= idata->ctx->msgcount)
626 /* see cmd_parse_expunge */
627 for (cur = 0; cur < idata->ctx->msgcount; cur++)
629 h = idata->ctx->hdrs[cur];
631 if (h && h->active && h->index+1 == msgno)
633 dprint (2, (debugfile, "Message UID %d updated\n", HEADER_DATA(h)->uid));
642 dprint (3, (debugfile, "FETCH response ignored for this message\n"));
647 s = imap_next_word (s);
648 s = imap_next_word (s);
652 dprint (1, (debugfile, "Malformed FETCH response"));
657 if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
659 dprint (2, (debugfile, "Only handle FLAGS updates\n"));
663 /* If server flags could conflict with mutt's flags, reopen the mailbox. */
665 idata->reopen |= IMAP_EXPUNGE_PENDING;
667 imap_set_flags (idata, h, s);
668 idata->check_status = IMAP_FLAGS_PENDING;
672 static void cmd_parse_list (IMAP_DATA* idata, char* s)
676 char delimbuf[5]; /* worst case: "\\"\0 */
679 if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
680 list = (IMAP_LIST*)idata->cmddata;
684 memset (list, 0, sizeof (IMAP_LIST));
687 s = imap_next_word (s);
690 dprint (1, (debugfile, "Bad LIST response\n"));
696 if (!ascii_strncasecmp (s, "\\NoSelect", 9))
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;
704 s = imap_next_word (s);
710 if (ascii_strncasecmp (s, "NIL", 3))
713 safe_strcat (delimbuf, 5, s);
714 imap_unquote_string (delimbuf);
715 list->delim = delimbuf[0];
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))
723 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
725 idata->status = IMAP_FATAL;
728 list->name = idata->buf;
732 imap_unmunge_mbox_name (s);
736 if (list->name[0] == '\0')
738 idata->delim = list->delim;
739 dprint (3, (debugfile, "Root delimiter: %c\n", idata->delim));
743 static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
751 if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
753 /* caller will handle response itself */
754 cmd_parse_list (idata, s);
758 if (!option (OPTIMAPCHECKSUBSCRIBED))
761 idata->cmdtype = IMAP_CT_LIST;
762 idata->cmddata = &list;
763 cmd_parse_list (idata, s);
764 idata->cmddata = NULL;
768 dprint (3, (debugfile, "Subscribing to %s\n", list.name));
770 strfcpy (buf, "mailboxes \"", sizeof (buf));
771 mutt_account_tourl (&idata->conn->account, &url);
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))
778 url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
779 safe_strcat (buf, sizeof (buf), "\"");
780 memset (&token, 0, sizeof (token));
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));
788 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
789 static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
791 dprint (2, (debugfile, "Handling MYRIGHTS\n"));
793 s = imap_next_word ((char*)s);
794 s = imap_next_word ((char*)s);
796 /* zero out current rights set */
797 memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
799 while (*s && !isspace((unsigned char) *s))
804 mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
807 mutt_bit_set (idata->ctx->rights, M_ACL_READ);
810 mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
813 mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
816 mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
819 mutt_bit_set (idata->ctx->rights, M_ACL_POST);
822 mutt_bit_set (idata->ctx->rights, M_ACL_ADMIN);
825 mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
828 mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
831 mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
834 mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
837 /* obsolete rights */
839 mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
840 mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
843 mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
844 mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
847 dprint(1, (debugfile, "Unknown right: %c\n", *s));
853 /* This should be optimised (eg with a tree or hash) */
854 static int uid2msgno (IMAP_DATA* idata, unsigned int uid)
858 for (i = 0; i < idata->ctx->msgcount; i++)
860 HEADER* h = idata->ctx->hdrs[i];
861 if (HEADER_DATA(h)->uid == uid)
868 /* cmd_parse_search: store SEARCH response for later use */
869 static void cmd_parse_search (IMAP_DATA* idata, const char* s)
874 dprint (2, (debugfile, "Handling SEARCH\n"));
876 while ((s = imap_next_word ((char*)s)) && *s != '\0')
879 msgno = uid2msgno (idata, uid);
882 idata->ctx->hdrs[uid2msgno (idata, uid)]->matched = 1;
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)
896 unsigned int olduv, oldun;
899 mailbox = imap_next_word (s);
901 /* We need a real tokenizer. */
902 if (!imap_get_literal_count (mailbox, &litlen))
904 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
906 idata->status = IMAP_FATAL;
909 mailbox = idata->buf;
910 s = mailbox + litlen;
917 s = imap_next_word (mailbox);
919 imap_unmunge_mbox_name (mailbox);
922 status = imap_mboxcache_get (idata, mailbox, 1);
923 olduv = status->uidvalidity;
924 oldun = status->uidnext;
928 dprint (1, (debugfile, "Error parsing STATUS\n"));
931 while (*s && *s != ')')
933 value = imap_next_word (s);
934 count = strtol (value, &value, 10);
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;
949 s = imap_next_word (s);
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));
955 /* caller is prepared to handle the result herself */
956 if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
958 memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
962 dprint (3, (debugfile, "Running default STATUS handler\n"));
964 /* should perhaps move this code back to imap_buffy_check */
965 for (inc = Incoming; inc; inc = inc->next)
967 if (inc->magic != M_IMAP)
970 if (imap_parse_path (inc->path, &mx) < 0)
972 dprint (1, (debugfile, "Error parsing mailbox %s, skipping\n", inc->path));
975 /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
977 if (imap_account_match (&idata->conn->account, &mx.account))
981 value = safe_strdup (mx.mbox);
982 imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
986 value = safe_strdup ("INBOX");
988 if (value && !imap_mxcmp (mailbox, value))
990 dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
991 mailbox, olduv, oldun, status->unseen));
993 if (option(OPTMAILCHECKRECENT))
995 if (olduv && olduv == status->uidvalidity)
997 if (oldun < status->uidnext)
998 inc->new = status->unseen;
1000 else if (!olduv && !oldun)
1001 /* first check per session, use recent. might need a flag for this. */
1002 inc->new = status->recent;
1004 inc->new = status->unseen;
1007 inc->new = status->unseen;
1010 /* force back to keep detecting new mail until the mailbox is
1012 status->uidnext = oldun;