Initial import of mutt-ng.
[apps/madmutt.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-2002 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
19  */ 
20
21 /* command.c: routines for sending commands to an IMAP server and parsing
22  *  responses */
23
24 #include "mutt.h"
25 #include "imap_private.h"
26 #include "message.h"
27 #include "mx.h"
28
29 #include <ctype.h>
30 #include <stdlib.h>
31
32 #define IMAP_CMD_BUFSIZE 512
33
34 /* forward declarations */
35 static void cmd_handle_fatal (IMAP_DATA* idata);
36 static int cmd_handle_untagged (IMAP_DATA* idata);
37 static void cmd_make_sequence (IMAP_DATA* idata);
38 static void cmd_parse_capabilities (IMAP_DATA* idata, char* s);
39 static void cmd_parse_expunge (IMAP_DATA* idata, const char* s);
40 static void cmd_parse_fetch (IMAP_DATA* idata, char* s);
41 static void cmd_parse_myrights (IMAP_DATA* idata, char* s);
42
43 static char *Capabilities[] = {
44   "IMAP4",
45   "IMAP4rev1",
46   "STATUS",
47   "ACL", 
48   "NAMESPACE",
49   "AUTH=CRAM-MD5",
50   "AUTH=GSSAPI",
51   "AUTH=ANONYMOUS",
52   "STARTTLS",
53   "LOGINDISABLED",
54
55   NULL
56 };
57
58 /* imap_cmd_start: Given an IMAP command, send it to the server.
59  *   Currently a minor convenience, but helps to route all IMAP commands
60  *   through a single interface. */
61 int imap_cmd_start (IMAP_DATA* idata, const char* cmd)
62 {
63   char* out;
64   int outlen;
65   int rc;
66
67   if (idata->status == IMAP_FATAL)
68   {
69     cmd_handle_fatal (idata);
70     return IMAP_CMD_BAD;
71   }
72
73   cmd_make_sequence (idata);
74   /* seq, space, cmd, \r\n\0 */
75   outlen = strlen (idata->cmd.seq) + strlen (cmd) + 4;
76   out = (char*) safe_malloc (outlen);
77   snprintf (out, outlen, "%s %s\r\n", idata->cmd.seq, cmd);
78
79   rc = mutt_socket_write (idata->conn, out);
80
81   FREE (&out);
82
83   return (rc < 0) ? IMAP_CMD_BAD : 0;
84 }
85
86 /* imap_cmd_step: Reads server responses from an IMAP command, detects
87  *   tagged completion response, handles untagged messages, can read
88  *   arbitrarily large strings (using malloc, so don't make it _too_
89  *   large!). */
90 int imap_cmd_step (IMAP_DATA* idata)
91 {
92   IMAP_COMMAND* cmd = &idata->cmd;
93   unsigned int len = 0;
94   int c;
95
96   if (idata->status == IMAP_FATAL)
97   {
98     cmd_handle_fatal (idata);
99     return IMAP_CMD_BAD;
100   }
101
102   /* read into buffer, expanding buffer as necessary until we have a full
103    * line */
104   do
105   {
106     if (len == cmd->blen)
107     {
108       safe_realloc (&cmd->buf, cmd->blen + IMAP_CMD_BUFSIZE);
109       cmd->blen = cmd->blen + IMAP_CMD_BUFSIZE;
110       dprint (3, (debugfile, "imap_cmd_step: grew buffer to %u bytes\n",
111                   cmd->blen));
112     }
113
114     c = mutt_socket_readln (cmd->buf + len, cmd->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 == cmd->blen);
128
129   /* don't let one large string make cmd->buf hog memory forever */
130   if ((cmd->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
131   {
132     safe_realloc (&cmd->buf, IMAP_CMD_BUFSIZE);
133     cmd->blen = IMAP_CMD_BUFSIZE;
134     dprint (3, (debugfile, "imap_cmd_step: shrank buffer to %u bytes\n", cmd->blen));
135   }
136
137   idata->lastread = time(NULL);
138
139   /* handle untagged messages. The caller still gets its shot afterwards. */
140   if (!ascii_strncmp (cmd->buf, "* ", 2) &&
141       cmd_handle_untagged (idata))
142     return IMAP_CMD_BAD;
143
144   /* server demands a continuation response from us */
145   if (!ascii_strncmp (cmd->buf, "+ ", 2))
146   {
147     return IMAP_CMD_RESPOND;
148   }
149
150   /* tagged completion code */
151   if (!ascii_strncmp (cmd->buf, cmd->seq, SEQLEN))
152   {
153     imap_cmd_finish (idata);
154     return imap_code (cmd->buf) ? IMAP_CMD_OK : IMAP_CMD_NO;
155   }
156
157   return IMAP_CMD_CONTINUE;
158 }
159
160 /* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
161 int imap_code (const char *s)
162 {
163   s += SEQLEN;
164   SKIPWS (s);
165   return (ascii_strncasecmp ("OK", s, 2) == 0);
166 }
167
168 /* imap_exec: execute a command, and wait for the response from the server.
169  * Also, handle untagged responses.
170  * Flags:
171  *   IMAP_CMD_FAIL_OK: the calling procedure can handle failure. This is used
172  *     for checking for a mailbox on append and login
173  *   IMAP_CMD_PASS: command contains a password. Suppress logging.
174  * Return 0 on success, -1 on Failure, -2 on OK Failure
175  */
176 int imap_exec (IMAP_DATA* idata, const char* cmd, int flags)
177 {
178   char* out;
179   int outlen;
180   int rc;
181
182   if (idata->status == IMAP_FATAL)
183   {
184     cmd_handle_fatal (idata);
185     return -1;
186   }
187
188   /* create sequence for command */
189   cmd_make_sequence (idata);
190   /* seq, space, cmd, \r\n\0 */
191   outlen = strlen (idata->cmd.seq) + strlen (cmd) + 4;
192   out = (char*) safe_malloc (outlen);
193   snprintf (out, outlen, "%s %s\r\n", idata->cmd.seq, cmd);
194
195   rc = mutt_socket_write_d (idata->conn, out,
196     flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
197   FREE (&out);
198
199   if (rc < 0)
200   {
201     cmd_handle_fatal (idata);
202     return -1;
203   }
204
205   do
206     rc = imap_cmd_step (idata);
207   while (rc == IMAP_CMD_CONTINUE);
208
209   if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
210     return -2;
211
212   if (rc != IMAP_CMD_OK)
213   {
214     if (flags & IMAP_CMD_FAIL_OK)
215       return -2;
216
217     dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->cmd.buf));
218     return -1;
219   }
220
221   return 0;
222 }
223
224 /* imap_cmd_running: Returns whether an IMAP command is in progress. */
225 int imap_cmd_running (IMAP_DATA* idata)
226 {
227   if (idata->cmd.state == IMAP_CMD_CONTINUE ||
228       idata->cmd.state == IMAP_CMD_RESPOND)
229     return 1;
230
231   return 0;
232 }
233
234 /* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
235  *   detected, do expunge). Called automatically by imap_cmd_step, but
236  *   may be called at any time. Called by imap_check_mailbox just before
237  *   the index is refreshed, for instance. */
238 void imap_cmd_finish (IMAP_DATA* idata)
239 {
240   if (idata->status == IMAP_FATAL)
241   {
242     cmd_handle_fatal (idata);
243     return;
244   }
245
246   if (!(idata->state == IMAP_SELECTED) || idata->ctx->closing)
247     return;
248   
249   if (idata->reopen & IMAP_REOPEN_ALLOW)
250   {
251     int count = idata->newMailCount;
252
253     if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
254         (idata->reopen & IMAP_NEWMAIL_PENDING)
255         && count > idata->ctx->msgcount)
256     {
257       /* read new mail messages */
258       dprint (2, (debugfile, "imap_cmd_finish: Fetching new mail\n"));
259       /* check_status: curs_main uses imap_check_mailbox to detect
260        *   whether the index needs updating */
261       idata->check_status = IMAP_NEWMAIL_PENDING;
262       imap_read_headers (idata, idata->ctx->msgcount, count-1);
263     }
264     else if (idata->reopen & IMAP_EXPUNGE_PENDING)
265     {
266       dprint (2, (debugfile, "imap_cmd_finish: Expunging mailbox\n"));
267       imap_expunge_mailbox (idata);
268       /* Detect whether we've gotten unexpected EXPUNGE messages */
269       if (idata->reopen & IMAP_EXPUNGE_PENDING &&
270           !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
271         idata->check_status = IMAP_EXPUNGE_PENDING;
272       idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
273                          IMAP_EXPUNGE_EXPECTED);
274     }
275   }
276
277   idata->status = 0;
278 }
279
280 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
281 static void cmd_handle_fatal (IMAP_DATA* idata)
282 {
283   idata->status = IMAP_FATAL;
284
285   if ((idata->state == IMAP_SELECTED) &&
286       (idata->reopen & IMAP_REOPEN_ALLOW) &&
287       !idata->ctx->closing)
288   {
289     mx_fastclose_mailbox (idata->ctx);
290     mutt_error (_("Mailbox closed"));
291     mutt_sleep (1);
292     idata->state = IMAP_DISCONNECTED;
293   }
294
295   if (idata->state != IMAP_SELECTED)
296   {
297     idata->state = IMAP_DISCONNECTED;
298     idata->status = 0;
299   }
300 }
301
302 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
303 static int cmd_handle_untagged (IMAP_DATA* idata)
304 {
305   char* s;
306   char* pn;
307   int count;
308
309   s = imap_next_word (idata->cmd.buf);
310
311   if ((idata->state == IMAP_SELECTED) && isdigit ((unsigned char) *s))
312   {
313     pn = s;
314     s = imap_next_word (s);
315
316     /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
317      * connection, so update that one.
318      */
319     if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
320     {
321       dprint (2, (debugfile, "Handling EXISTS\n"));
322
323       /* new mail arrived */
324       count = atoi (pn);
325
326       if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
327            count < idata->ctx->msgcount)
328       {
329         /* something is wrong because the server reported fewer messages
330          * than we previously saw
331          */
332         mutt_error _("Fatal error.  Message count is out of sync!");
333         idata->status = IMAP_FATAL;
334         return -1;
335       }
336       /* at least the InterChange server sends EXISTS messages freely,
337        * even when there is no new mail */
338       else if (count == idata->ctx->msgcount)
339         dprint (3, (debugfile,
340           "cmd_handle_untagged: superfluous EXISTS message.\n"));
341       else
342       {
343         if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
344         {
345           dprint (2, (debugfile,
346             "cmd_handle_untagged: New mail in %s - %d messages total.\n",
347             idata->mailbox, count));
348           idata->reopen |= IMAP_NEWMAIL_PENDING;
349         }
350         idata->newMailCount = count;
351       }
352     }
353     /* pn vs. s: need initial seqno */
354     else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
355       cmd_parse_expunge (idata, pn);
356     else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
357       cmd_parse_fetch (idata, pn);
358   }
359   else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
360     cmd_parse_capabilities (idata, s);
361   else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
362     cmd_parse_myrights (idata, s);
363   else if (ascii_strncasecmp ("BYE", s, 3) == 0)
364   {
365     dprint (2, (debugfile, "Handling BYE\n"));
366
367     /* check if we're logging out */
368     if (idata->status == IMAP_BYE)
369       return 0;
370
371     /* server shut down our connection */
372     s += 3;
373     SKIPWS (s);
374     mutt_error ("%s", s);
375     idata->status = IMAP_BYE;
376     if (idata->state == IMAP_SELECTED)
377       mx_fastclose_mailbox (idata->ctx);
378     mutt_socket_close (idata->conn);
379     idata->state = IMAP_DISCONNECTED;
380
381     return -1;
382   }
383   else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
384   {
385     dprint (2, (debugfile, "Handling untagged NO\n"));
386
387     /* Display the warning message from the server */
388     mutt_error ("%s", s+3);
389     mutt_sleep (2);
390   }
391
392   return 0;
393 }
394
395 /* cmd_make_sequence: make a tag suitable for starting an IMAP command */
396 static void cmd_make_sequence (IMAP_DATA* idata)
397 {
398   snprintf (idata->cmd.seq, sizeof (idata->cmd.seq), "a%04d", idata->seqno++);
399
400   if (idata->seqno > 9999)
401     idata->seqno = 0;
402 }
403
404 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
405  *   response */
406 static void cmd_parse_capabilities (IMAP_DATA* idata, char* s)
407 {
408   int x;
409
410   dprint (2, (debugfile, "Handling CAPABILITY\n"));
411
412   s = imap_next_word (s);
413   FREE(&idata->capstr);
414   idata->capstr = safe_strdup (s);
415
416   memset (idata->capabilities, 0, sizeof (idata->capabilities));
417
418   while (*s)
419   {
420     for (x = 0; x < CAPMAX; x++)
421       if (imap_wordcasecmp(Capabilities[x], s) == 0)
422       {
423         mutt_bit_set (idata->capabilities, x);
424         break;
425       }
426     s = imap_next_word (s);
427   }
428 }
429
430 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
431  *   be reopened at our earliest convenience */
432 static void cmd_parse_expunge (IMAP_DATA* idata, const char* s)
433 {
434   int expno, cur;
435   HEADER* h;
436
437   dprint (2, (debugfile, "Handling EXPUNGE\n"));
438
439   expno = atoi (s);
440
441   /* walk headers, zero seqno of expunged message, decrement seqno of those
442    * above. Possibly we could avoid walking the whole list by resorting
443    * and guessing a good starting point, but I'm guessing the resort would
444    * nullify the gains */
445   for (cur = 0; cur < idata->ctx->msgcount; cur++)
446   {
447     h = idata->ctx->hdrs[cur];
448
449     if (h->index+1 == expno)
450       h->index = -1;
451     else if (h->index+1 > expno)
452       h->index--;
453   }
454
455   idata->reopen |= IMAP_EXPUNGE_PENDING;
456 }
457
458 /* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
459  *   handles unanticipated FETCH responses, and only FLAGS data. We get
460  *   these if another client has changed flags for a mailbox we've selected.
461  *   Of course, a lot of code here duplicates code in message.c. */
462 static void cmd_parse_fetch (IMAP_DATA* idata, char* s)
463 {
464   int msgno, cur;
465   HEADER* h = NULL;
466
467   dprint (2, (debugfile, "Handling FETCH\n"));
468
469   msgno = atoi (s);
470   
471   /* see cmd_parse_expunge */
472   for (cur = 0; cur < idata->ctx->msgcount; cur++)
473   {
474     h = idata->ctx->hdrs[cur];
475     
476     if (h->active && h->index+1 == msgno)
477     {
478       dprint (2, (debugfile, "Message UID %d updated\n", HEADER_DATA(h)->uid));
479       break;
480     }
481
482     h = NULL;
483   }
484   
485   if (!h)
486   {
487     dprint (1, (debugfile, "FETCH response ignored for this message\n"));
488     return;
489   }
490   
491   /* skip FETCH */
492   s = imap_next_word (s);
493   s = imap_next_word (s);
494
495   if (*s != '(')
496   {
497     dprint (1, (debugfile, "Malformed FETCH response"));
498     return;
499   }
500   s++;
501
502   if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
503   {
504     dprint (2, (debugfile, "Only handle FLAGS updates\n"));
505     return;
506   }
507
508   /* If server flags could conflict with mutt's flags, reopen the mailbox. */
509   if (h->changed)
510     idata->reopen |= IMAP_EXPUNGE_PENDING;
511   else {
512     imap_set_flags (idata, h, s);
513     idata->check_status = IMAP_FLAGS_PENDING;
514   }
515 }
516
517 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
518 static void cmd_parse_myrights (IMAP_DATA* idata, char* s)
519 {
520   dprint (2, (debugfile, "Handling MYRIGHTS\n"));
521
522   s = imap_next_word (s);
523   s = imap_next_word (s);
524
525   /* zero out current rights set */
526   memset (idata->rights, 0, sizeof (idata->rights));
527
528   while (*s && !isspace((unsigned char) *s))
529   {
530     switch (*s) 
531     {
532       case 'l':
533         mutt_bit_set (idata->rights, IMAP_ACL_LOOKUP);
534         break;
535       case 'r':
536         mutt_bit_set (idata->rights, IMAP_ACL_READ);
537         break;
538       case 's':
539         mutt_bit_set (idata->rights, IMAP_ACL_SEEN);
540         break;
541       case 'w':
542         mutt_bit_set (idata->rights, IMAP_ACL_WRITE);
543         break;
544       case 'i':
545         mutt_bit_set (idata->rights, IMAP_ACL_INSERT);
546         break;
547       case 'p':
548         mutt_bit_set (idata->rights, IMAP_ACL_POST);
549         break;
550       case 'c':
551         mutt_bit_set (idata->rights, IMAP_ACL_CREATE);
552         break;
553       case 'd':
554         mutt_bit_set (idata->rights, IMAP_ACL_DELETE);
555         break;
556       case 'a':
557         mutt_bit_set (idata->rights, IMAP_ACL_ADMIN);
558         break;
559     }
560     s++;
561   }
562 }