mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-1998,2010,2012 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net>
4 * Copyright (C) 1999-2009,2011 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 "mx.h"
31#include "buffy.h"
32
33#include <ctype.h>
34#include <stdlib.h>
35
36#define IMAP_CMD_BUFSIZE 512
37
38/* forward declarations */
39static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags);
40static int cmd_queue_full (IMAP_DATA* idata);
41static int cmd_queue (IMAP_DATA* idata, const char* cmdstr);
42static IMAP_COMMAND* cmd_new (IMAP_DATA* idata);
43static int cmd_status (const char *s);
44static void cmd_handle_fatal (IMAP_DATA* idata);
45static int cmd_handle_untagged (IMAP_DATA* idata);
46static void cmd_parse_capability (IMAP_DATA* idata, char* s);
47static void cmd_parse_expunge (IMAP_DATA* idata, const char* s);
48static void cmd_parse_list (IMAP_DATA* idata, char* s);
49static void cmd_parse_lsub (IMAP_DATA* idata, char* s);
50static void cmd_parse_fetch (IMAP_DATA* idata, char* s);
51static void cmd_parse_myrights (IMAP_DATA* idata, const char* s);
52static void cmd_parse_search (IMAP_DATA* idata, const char* s);
53static void cmd_parse_status (IMAP_DATA* idata, char* s);
54static void cmd_parse_enabled (IMAP_DATA* idata, const char* s);
55
56static const char * const 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 "ENABLE",
70
71 NULL
72};
73
74/* imap_cmd_start: Given an IMAP command, send it to the server.
75 * If cmdstr is NULL, sends queued commands. */
76int imap_cmd_start (IMAP_DATA* idata, const char* cmdstr)
77{
78 return cmd_start (idata, cmdstr, 0);
79}
80
81/* imap_cmd_step: Reads server responses from an IMAP command, detects
82 * tagged completion response, handles untagged messages, can read
83 * arbitrarily large strings (using malloc, so don't make it _too_
84 * large!). */
85int imap_cmd_step (IMAP_DATA* idata)
86{
87 size_t len = 0;
88 int c;
89 int rc;
90 int stillrunning = 0;
91 IMAP_COMMAND* cmd;
92
93 if (idata->status == IMAP_FATAL)
94 {
95 cmd_handle_fatal (idata);
96 return IMAP_CMD_BAD;
97 }
98
99 /* read into buffer, expanding buffer as necessary until we have a full
100 * line */
101 do
102 {
103 if (len == idata->blen)
104 {
105 safe_realloc (&idata->buf, idata->blen + IMAP_CMD_BUFSIZE);
106 idata->blen = idata->blen + IMAP_CMD_BUFSIZE;
107 dprint (3, (debugfile, "imap_cmd_step: grew buffer to %u bytes\n",
108 idata->blen));
109 }
110
111 /* back up over '\0' */
112 if (len)
113 len--;
114 c = mutt_socket_readln (idata->buf + len, idata->blen - len, idata->conn);
115 if (c <= 0)
116 {
117 dprint (1, (debugfile, "imap_cmd_step: Error reading server response.\n"));
118 cmd_handle_fatal (idata);
119 return IMAP_CMD_BAD;
120 }
121
122 len += c;
123 }
124 /* if we've read all the way to the end of the buffer, we haven't read a
125 * full line (mutt_socket_readln strips the \r, so we always have at least
126 * one character free when we've read a full line) */
127 while (len == idata->blen);
128
129 /* don't let one large string make cmd->buf hog memory forever */
130 if ((idata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
131 {
132 safe_realloc (&idata->buf, IMAP_CMD_BUFSIZE);
133 idata->blen = IMAP_CMD_BUFSIZE;
134 dprint (3, (debugfile, "imap_cmd_step: shrank buffer to %u bytes\n", idata->blen));
135 }
136
137 idata->lastread = time (NULL);
138
139 /* handle untagged messages. The caller still gets its shot afterwards. */
140 if ((!ascii_strncmp (idata->buf, "* ", 2)
141 || !ascii_strncmp (imap_next_word (idata->buf), "OK [", 4))
142 && cmd_handle_untagged (idata))
143 return IMAP_CMD_BAD;
144
145 /* server demands a continuation response from us */
146 if (idata->buf[0] == '+')
147 return IMAP_CMD_RESPOND;
148
149 /* look for tagged command completions */
150 rc = IMAP_CMD_CONTINUE;
151 c = idata->lastcmd;
152 do
153 {
154 cmd = &idata->cmds[c];
155 if (cmd->state == IMAP_CMD_NEW)
156 {
157 if (!ascii_strncmp (idata->buf, cmd->seq, SEQLEN)) {
158 if (!stillrunning)
159 {
160 /* first command in queue has finished - move queue pointer up */
161 idata->lastcmd = (idata->lastcmd + 1) % idata->cmdslots;
162 }
163 cmd->state = cmd_status (idata->buf);
164 /* bogus - we don't know which command result to return here. Caller
165 * should provide a tag. */
166 rc = cmd->state;
167 }
168 else
169 stillrunning++;
170 }
171
172 c = (c + 1) % idata->cmdslots;
173 }
174 while (c != idata->nextcmd);
175
176 if (stillrunning)
177 rc = IMAP_CMD_CONTINUE;
178 else
179 {
180 dprint (3, (debugfile, "IMAP queue drained\n"));
181 imap_cmd_finish (idata);
182 }
183
184
185 return rc;
186}
187
188/* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
189int imap_code (const char *s)
190{
191 return cmd_status (s) == IMAP_CMD_OK;
192}
193
194/* imap_cmd_trailer: extra information after tagged command response if any */
195const char* imap_cmd_trailer (IMAP_DATA* idata)
196{
197 static const char* notrailer = "";
198 const char* s = idata->buf;
199
200 if (!s)
201 {
202 dprint (2, (debugfile, "imap_cmd_trailer: not a tagged response"));
203 return notrailer;
204 }
205
206 s = imap_next_word ((char *)s);
207 if (!s || (ascii_strncasecmp (s, "OK", 2) &&
208 ascii_strncasecmp (s, "NO", 2) &&
209 ascii_strncasecmp (s, "BAD", 3)))
210 {
211 dprint (2, (debugfile, "imap_cmd_trailer: not a command completion: %s",
212 idata->buf));
213 return notrailer;
214 }
215
216 s = imap_next_word ((char *)s);
217 if (!s)
218 return notrailer;
219
220 return s;
221}
222
223/* imap_exec: execute a command, and wait for the response from the server.
224 * Also, handle untagged responses.
225 * Flags:
226 * IMAP_CMD_FAIL_OK: the calling procedure can handle failure. This is used
227 * for checking for a mailbox on append and login
228 * IMAP_CMD_PASS: command contains a password. Suppress logging.
229 * IMAP_CMD_QUEUE: only queue command, do not execute.
230 * Return 0 on success, -1 on Failure, -2 on OK Failure
231 */
232int imap_exec (IMAP_DATA* idata, const char* cmdstr, int flags)
233{
234 int rc;
235
236 if ((rc = cmd_start (idata, cmdstr, flags)) < 0)
237 {
238 cmd_handle_fatal (idata);
239 return -1;
240 }
241
242 if (flags & IMAP_CMD_QUEUE)
243 return 0;
244
245 do
246 rc = imap_cmd_step (idata);
247 while (rc == IMAP_CMD_CONTINUE);
248
249 if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
250 return -2;
251
252 if (rc != IMAP_CMD_OK)
253 {
254 if ((flags & IMAP_CMD_FAIL_OK) && idata->status != IMAP_FATAL)
255 return -2;
256
257 dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->buf));
258 return -1;
259 }
260
261 return 0;
262}
263
264/* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
265 * detected, do expunge). Called automatically by imap_cmd_step, but
266 * may be called at any time. Called by imap_check_mailbox just before
267 * the index is refreshed, for instance. */
268void imap_cmd_finish (IMAP_DATA* idata)
269{
270 if (idata->status == IMAP_FATAL)
271 {
272 cmd_handle_fatal (idata);
273 return;
274 }
275
276 if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
277 return;
278
279 if (idata->reopen & IMAP_REOPEN_ALLOW)
280 {
281 int count = idata->newMailCount;
282
283 if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
284 (idata->reopen & IMAP_NEWMAIL_PENDING)
285 && count > idata->ctx->msgcount)
286 {
287 /* read new mail messages */
288 dprint (2, (debugfile, "imap_cmd_finish: Fetching new mail\n"));
289 /* check_status: curs_main uses imap_check_mailbox to detect
290 * whether the index needs updating */
291 idata->check_status = IMAP_NEWMAIL_PENDING;
292 imap_read_headers (idata, idata->ctx->msgcount, count-1);
293 }
294 else if (idata->reopen & IMAP_EXPUNGE_PENDING)
295 {
296 dprint (2, (debugfile, "imap_cmd_finish: Expunging mailbox\n"));
297 imap_expunge_mailbox (idata);
298 /* Detect whether we've gotten unexpected EXPUNGE messages */
299 if ((idata->reopen & IMAP_EXPUNGE_PENDING) &&
300 !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
301 idata->check_status = IMAP_EXPUNGE_PENDING;
302 idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
303 IMAP_EXPUNGE_EXPECTED);
304 }
305 }
306
307 idata->status = 0;
308}
309
310/* imap_cmd_idle: Enter the IDLE state. */
311int imap_cmd_idle (IMAP_DATA* idata)
312{
313 int rc;
314
315 imap_cmd_start (idata, "IDLE");
316 do
317 rc = imap_cmd_step (idata);
318 while (rc == IMAP_CMD_CONTINUE);
319
320 if (rc == IMAP_CMD_RESPOND)
321 {
322 /* successfully entered IDLE state */
323 idata->state = IMAP_IDLE;
324 /* queue automatic exit when next command is issued */
325 mutt_buffer_printf (idata->cmdbuf, "DONE\r\n");
326 rc = IMAP_CMD_OK;
327 }
328 if (rc != IMAP_CMD_OK)
329 {
330 dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
331 return -1;
332 }
333
334 return 0;
335}
336
337static int cmd_queue_full (IMAP_DATA* idata)
338{
339 if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
340 return 1;
341
342 return 0;
343}
344
345/* sets up a new command control block and adds it to the queue.
346 * Returns NULL if the pipeline is full. */
347static IMAP_COMMAND* cmd_new (IMAP_DATA* idata)
348{
349 IMAP_COMMAND* cmd;
350
351 if (cmd_queue_full (idata))
352 {
353 dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
354 return NULL;
355 }
356
357 cmd = idata->cmds + idata->nextcmd;
358 idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
359
360 snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
361 if (idata->seqno > 9999)
362 idata->seqno = 0;
363
364 cmd->state = IMAP_CMD_NEW;
365
366 return cmd;
367}
368
369/* queues command. If the queue is full, attempts to drain it. */
370static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
371{
372 IMAP_COMMAND* cmd;
373 int rc;
374
375 if (cmd_queue_full (idata))
376 {
377 dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
378
379 rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
380
381 if (rc < 0 && rc != -2)
382 return rc;
383 }
384
385 if (!(cmd = cmd_new (idata)))
386 return IMAP_CMD_BAD;
387
388 if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
389 return IMAP_CMD_BAD;
390
391 return 0;
392}
393
394static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
395{
396 int rc;
397
398 if (idata->status == IMAP_FATAL)
399 {
400 cmd_handle_fatal (idata);
401 return -1;
402 }
403
404 if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
405 return rc;
406
407 if (flags & IMAP_CMD_QUEUE)
408 return 0;
409
410 if (idata->cmdbuf->dptr == idata->cmdbuf->data)
411 return IMAP_CMD_BAD;
412
413 rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1,
414 flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
415 idata->cmdbuf->dptr = idata->cmdbuf->data;
416
417 /* unidle when command queue is flushed */
418 if (idata->state == IMAP_IDLE)
419 idata->state = IMAP_SELECTED;
420
421 return (rc < 0) ? IMAP_CMD_BAD : 0;
422}
423
424/* parse response line for tagged OK/NO/BAD */
425static int cmd_status (const char *s)
426{
427 s = imap_next_word((char*)s);
428
429 if (!ascii_strncasecmp("OK", s, 2))
430 return IMAP_CMD_OK;
431 if (!ascii_strncasecmp("NO", s, 2))
432 return IMAP_CMD_NO;
433
434 return IMAP_CMD_BAD;
435}
436
437/* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
438static void cmd_handle_fatal (IMAP_DATA* idata)
439{
440 idata->status = IMAP_FATAL;
441
442 if ((idata->state >= IMAP_SELECTED) &&
443 (idata->reopen & IMAP_REOPEN_ALLOW))
444 {
445 mx_fastclose_mailbox (idata->ctx);
446 mutt_error (_("Mailbox closed"));
447 mutt_sleep (1);
448 idata->state = IMAP_DISCONNECTED;
449 }
450
451 if (idata->state < IMAP_SELECTED)
452 imap_close_connection (idata);
453}
454
455/* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
456static int cmd_handle_untagged (IMAP_DATA* idata)
457{
458 char* s;
459 char* pn;
460 int count;
461
462 s = imap_next_word (idata->buf);
463 pn = imap_next_word (s);
464
465 if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s))
466 {
467 pn = s;
468 s = imap_next_word (s);
469
470 /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
471 * connection, so update that one.
472 */
473 if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
474 {
475 dprint (2, (debugfile, "Handling EXISTS\n"));
476
477 /* new mail arrived */
478 count = atoi (pn);
479
480 if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
481 count < idata->ctx->msgcount)
482 {
483 /* Notes 6.0.3 has a tendency to report fewer messages exist than
484 * it should. */
485 dprint (1, (debugfile, "Message count is out of sync"));
486 return 0;
487 }
488 /* at least the InterChange server sends EXISTS messages freely,
489 * even when there is no new mail */
490 else if (count == idata->ctx->msgcount)
491 dprint (3, (debugfile,
492 "cmd_handle_untagged: superfluous EXISTS message.\n"));
493 else
494 {
495 if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
496 {
497 dprint (2, (debugfile,
498 "cmd_handle_untagged: New mail in %s - %d messages total.\n",
499 idata->mailbox, count));
500 idata->reopen |= IMAP_NEWMAIL_PENDING;
501 }
502 idata->newMailCount = count;
503 }
504 }
505 /* pn vs. s: need initial seqno */
506 else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
507 cmd_parse_expunge (idata, pn);
508 else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
509 cmd_parse_fetch (idata, pn);
510 }
511 else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
512 cmd_parse_capability (idata, s);
513 else if (!ascii_strncasecmp ("OK [CAPABILITY", s, 14))
514 cmd_parse_capability (idata, pn);
515 else if (!ascii_strncasecmp ("OK [CAPABILITY", pn, 14))
516 cmd_parse_capability (idata, imap_next_word (pn));
517 else if (ascii_strncasecmp ("LIST", s, 4) == 0)
518 cmd_parse_list (idata, s);
519 else if (ascii_strncasecmp ("LSUB", s, 4) == 0)
520 cmd_parse_lsub (idata, s);
521 else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
522 cmd_parse_myrights (idata, s);
523 else if (ascii_strncasecmp ("SEARCH", s, 6) == 0)
524 cmd_parse_search (idata, s);
525 else if (ascii_strncasecmp ("STATUS", s, 6) == 0)
526 cmd_parse_status (idata, s);
527 else if (ascii_strncasecmp ("ENABLED", s, 7) == 0)
528 cmd_parse_enabled (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 */
560static 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 */
589static 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. */
619static 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
675static 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 (idata, 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
746static 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 /* noselect is for a gmail quirk (#3445) */
769 if (!list.name || list.noselect)
770 return;
771
772 dprint (3, (debugfile, "Subscribing to %s\n", list.name));
773
774 strfcpy (buf, "mailboxes \"", sizeof (buf));
775 mutt_account_tourl (&idata->conn->account, &url);
776 /* escape \ and " */
777 imap_quote_string(errstr, sizeof (errstr), list.name);
778 url.path = errstr + 1;
779 url.path[strlen(url.path) - 1] = '\0';
780 if (!mutt_strcmp (url.user, ImapUser))
781 url.user = NULL;
782 url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
783 safe_strcat (buf, sizeof (buf), "\"");
784 mutt_buffer_init (&token);
785 mutt_buffer_init (&err);
786 err.data = errstr;
787 err.dsize = sizeof (errstr);
788 if (mutt_parse_rc_line (buf, &token, &err))
789 dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
790 FREE (&token.data);
791}
792
793/* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
794static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
795{
796 dprint (2, (debugfile, "Handling MYRIGHTS\n"));
797
798 s = imap_next_word ((char*)s);
799 s = imap_next_word ((char*)s);
800
801 /* zero out current rights set */
802 memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
803
804 while (*s && !isspace((unsigned char) *s))
805 {
806 switch (*s)
807 {
808 case 'l':
809 mutt_bit_set (idata->ctx->rights, MUTT_ACL_LOOKUP);
810 break;
811 case 'r':
812 mutt_bit_set (idata->ctx->rights, MUTT_ACL_READ);
813 break;
814 case 's':
815 mutt_bit_set (idata->ctx->rights, MUTT_ACL_SEEN);
816 break;
817 case 'w':
818 mutt_bit_set (idata->ctx->rights, MUTT_ACL_WRITE);
819 break;
820 case 'i':
821 mutt_bit_set (idata->ctx->rights, MUTT_ACL_INSERT);
822 break;
823 case 'p':
824 mutt_bit_set (idata->ctx->rights, MUTT_ACL_POST);
825 break;
826 case 'a':
827 mutt_bit_set (idata->ctx->rights, MUTT_ACL_ADMIN);
828 break;
829 case 'k':
830 mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE);
831 break;
832 case 'x':
833 mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELMX);
834 break;
835 case 't':
836 mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE);
837 break;
838 case 'e':
839 mutt_bit_set (idata->ctx->rights, MUTT_ACL_EXPUNGE);
840 break;
841
842 /* obsolete rights */
843 case 'c':
844 mutt_bit_set (idata->ctx->rights, MUTT_ACL_CREATE);
845 mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELMX);
846 break;
847 case 'd':
848 mutt_bit_set (idata->ctx->rights, MUTT_ACL_DELETE);
849 mutt_bit_set (idata->ctx->rights, MUTT_ACL_EXPUNGE);
850 break;
851 default:
852 dprint(1, (debugfile, "Unknown right: %c\n", *s));
853 }
854 s++;
855 }
856}
857
858/* cmd_parse_search: store SEARCH response for later use */
859static void cmd_parse_search (IMAP_DATA* idata, const char* s)
860{
861 unsigned int uid;
862 HEADER *h;
863
864 dprint (2, (debugfile, "Handling SEARCH\n"));
865
866 while ((s = imap_next_word ((char*)s)) && *s != '\0')
867 {
868 uid = (unsigned int)atoi (s);
869 h = (HEADER *)int_hash_find (idata->uid_hash, uid);
870 if (h)
871 h->matched = 1;
872 }
873}
874
875/* first cut: just do buffy update. Later we may wish to cache all
876 * mailbox information, even that not desired by buffy */
877static void cmd_parse_status (IMAP_DATA* idata, char* s)
878{
879 char* mailbox;
880 char* value;
881 BUFFY* inc;
882 IMAP_MBOX mx;
883 int count;
884 IMAP_STATUS *status;
885 unsigned int olduv, oldun;
886 long litlen;
887 short new = 0;
888 short new_msg_count = 0;
889
890 mailbox = imap_next_word (s);
891
892 /* We need a real tokenizer. */
893 if (!imap_get_literal_count (mailbox, &litlen))
894 {
895 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
896 {
897 idata->status = IMAP_FATAL;
898 return;
899 }
900 mailbox = idata->buf;
901 s = mailbox + litlen;
902 *s = '\0';
903 s++;
904 SKIPWS(s);
905 }
906 else
907 {
908 s = imap_next_word (mailbox);
909 *(s - 1) = '\0';
910 imap_unmunge_mbox_name (idata, mailbox);
911 }
912
913 status = imap_mboxcache_get (idata, mailbox, 1);
914 olduv = status->uidvalidity;
915 oldun = status->uidnext;
916
917 if (*s++ != '(')
918 {
919 dprint (1, (debugfile, "Error parsing STATUS\n"));
920 return;
921 }
922 while (*s && *s != ')')
923 {
924 value = imap_next_word (s);
925 count = strtol (value, &value, 10);
926
927 if (!ascii_strncmp ("MESSAGES", s, 8))
928 {
929 status->messages = count;
930 new_msg_count = 1;
931 }
932 else if (!ascii_strncmp ("RECENT", s, 6))
933 status->recent = count;
934 else if (!ascii_strncmp ("UIDNEXT", s, 7))
935 status->uidnext = count;
936 else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
937 status->uidvalidity = count;
938 else if (!ascii_strncmp ("UNSEEN", s, 6))
939 status->unseen = count;
940
941 s = value;
942 if (*s && *s != ')')
943 s = imap_next_word (s);
944 }
945 dprint (3, (debugfile, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
946 status->name, status->uidvalidity, status->uidnext,
947 status->messages, status->recent, status->unseen));
948
949 /* caller is prepared to handle the result herself */
950 if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
951 {
952 memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
953 return;
954 }
955
956 dprint (3, (debugfile, "Running default STATUS handler\n"));
957
958 /* should perhaps move this code back to imap_buffy_check */
959 for (inc = Incoming; inc; inc = inc->next)
960 {
961 if (inc->magic != MUTT_IMAP)
962 continue;
963
964 if (imap_parse_path (inc->path, &mx) < 0)
965 {
966 dprint (1, (debugfile, "Error parsing mailbox %s, skipping\n", inc->path));
967 continue;
968 }
969 /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
970
971 if (imap_account_match (&idata->conn->account, &mx.account))
972 {
973 if (mx.mbox)
974 {
975 value = safe_strdup (mx.mbox);
976 imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
977 FREE (&mx.mbox);
978 }
979 else
980 value = safe_strdup ("INBOX");
981
982 if (value && !imap_mxcmp (mailbox, value))
983 {
984 dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
985 mailbox, olduv, oldun, status->unseen));
986
987 if (option(OPTMAILCHECKRECENT))
988 {
989 if (olduv && olduv == status->uidvalidity)
990 {
991 if (oldun < status->uidnext)
992 new = (status->unseen > 0);
993 }
994 else if (!olduv && !oldun)
995 /* first check per session, use recent. might need a flag for this. */
996 new = (status->recent > 0);
997 else
998 new = (status->unseen > 0);
999 }
1000 else
1001 new = (status->unseen > 0);
1002
1003#ifdef USE_SIDEBAR
1004 if ((inc->new != new) ||
1005 (inc->msg_count != status->messages) ||
1006 (inc->msg_unread != status->unseen))
1007 SidebarNeedsRedraw = 1;
1008#endif
1009 inc->new = new;
1010 if (new_msg_count)
1011 inc->msg_count = status->messages;
1012 inc->msg_unread = status->unseen;
1013
1014 if (inc->new)
1015 /* force back to keep detecting new mail until the mailbox is
1016 opened */
1017 status->uidnext = oldun;
1018
1019 FREE (&value);
1020 return;
1021 }
1022
1023 FREE (&value);
1024 }
1025
1026 FREE (&mx.mbox);
1027 }
1028}
1029
1030/* cmd_parse_enabled: record what the server has enabled */
1031static void cmd_parse_enabled (IMAP_DATA* idata, const char* s)
1032{
1033 dprint (2, (debugfile, "Handling ENABLED\n"));
1034
1035 while ((s = imap_next_word ((char*)s)) && *s != '\0')
1036 {
1037 if (ascii_strncasecmp(s, "UTF8=ACCEPT", 11) == 0 ||
1038 ascii_strncasecmp(s, "UTF8=ONLY", 9) == 0)
1039 idata->unicode = 1;
1040 }
1041}