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