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