Rocco Rutte:
[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 <ctype.h>
25 #include <stdlib.h>
26
27 #define IMAP_CMD_BUFSIZE 512
28
29 /* forward declarations */
30 static void cmd_handle_fatal (IMAP_DATA * idata);
31 static int cmd_handle_untagged (IMAP_DATA * idata);
32 static void cmd_make_sequence (IMAP_DATA * idata);
33 static void cmd_parse_capabilities (IMAP_DATA * idata, char *s);
34 static void cmd_parse_expunge (IMAP_DATA * idata, const char *s);
35 static void cmd_parse_fetch (IMAP_DATA * idata, char *s);
36 static void cmd_parse_myrights (IMAP_DATA * idata, char *s);
37
38 static char *Capabilities[] = {
39   "IMAP4",
40   "IMAP4rev1",
41   "STATUS",
42   "ACL",
43   "NAMESPACE",
44   "AUTH=CRAM-MD5",
45   "AUTH=GSSAPI",
46   "AUTH=ANONYMOUS",
47   "STARTTLS",
48   "LOGINDISABLED",
49
50   NULL
51 };
52
53 /* imap_cmd_start: Given an IMAP command, send it to the server.
54  *   Currently a minor convenience, but helps to route all IMAP commands
55  *   through a single interface. */
56 int imap_cmd_start (IMAP_DATA * idata, const char *cmd)
57 {
58   char *out;
59   int outlen;
60   int rc;
61
62   if (idata->status == IMAP_FATAL) {
63     cmd_handle_fatal (idata);
64     return IMAP_CMD_BAD;
65   }
66
67   cmd_make_sequence (idata);
68   /* seq, space, cmd, \r\n\0 */
69   outlen = mutt_strlen (idata->cmd.seq) + mutt_strlen (cmd) + 4;
70   out = (char *) safe_malloc (outlen);
71   snprintf (out, outlen, "%s %s\r\n", idata->cmd.seq, cmd);
72
73   rc = mutt_socket_write (idata->conn, out);
74
75   FREE (&out);
76
77   return (rc < 0) ? IMAP_CMD_BAD : 0;
78 }
79
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_
83  *   large!). */
84 int imap_cmd_step (IMAP_DATA * idata)
85 {
86   IMAP_COMMAND *cmd = &idata->cmd;
87   unsigned int len = 0;
88   int c;
89
90   if (idata->status == IMAP_FATAL) {
91     cmd_handle_fatal (idata);
92     return IMAP_CMD_BAD;
93   }
94
95   /* read into buffer, expanding buffer as necessary until we have a full
96    * line */
97   do {
98     if (len == cmd->blen) {
99       safe_realloc (&cmd->buf, cmd->blen + IMAP_CMD_BUFSIZE);
100       cmd->blen = cmd->blen + IMAP_CMD_BUFSIZE;
101       dprint (3, (debugfile, "imap_cmd_step: grew buffer to %u bytes\n",
102                   cmd->blen));
103     }
104
105     c = mutt_socket_readln (cmd->buf + len, cmd->blen - len, idata->conn);
106     if (c <= 0) {
107       dprint (1,
108               (debugfile, "imap_cmd_step: Error reading server response.\n"));
109       /* cmd_handle_fatal (idata); */
110       return IMAP_CMD_BAD;
111     }
112
113     len += c;
114   }
115   /* if we've read all the way to the end of the buffer, we haven't read a
116    * full line (mutt_socket_readln strips the \r, so we always have at least
117    * one character free when we've read a full line) */
118   while (len == cmd->blen);
119
120   /* don't let one large string make cmd->buf hog memory forever */
121   if ((cmd->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE)) {
122     safe_realloc (&cmd->buf, IMAP_CMD_BUFSIZE);
123     cmd->blen = IMAP_CMD_BUFSIZE;
124     dprint (3,
125             (debugfile, "imap_cmd_step: shrank buffer to %u bytes\n",
126              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     dprint (1,
212             (debugfile, "imap_exec: command failed: %s\n", idata->cmd.buf));
213     return -1;
214   }
215
216   return 0;
217 }
218
219 /* imap_cmd_running: Returns whether an IMAP command is in progress. */
220 int imap_cmd_running (IMAP_DATA * idata)
221 {
222   if (idata->cmd.state == IMAP_CMD_CONTINUE ||
223       idata->cmd.state == IMAP_CMD_RESPOND)
224     return 1;
225
226   return 0;
227 }
228
229 /* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
230  *   detected, do expunge). Called automatically by imap_cmd_step, but
231  *   may be called at any time. Called by imap_check_mailbox just before
232  *   the index is refreshed, for instance. */
233 void imap_cmd_finish (IMAP_DATA * idata)
234 {
235   if (idata->status == IMAP_FATAL) {
236     cmd_handle_fatal (idata);
237     return;
238   }
239
240   if (!(idata->state == IMAP_SELECTED) || idata->ctx->closing)
241     return;
242
243   if (idata->reopen & IMAP_REOPEN_ALLOW) {
244     int count = idata->newMailCount;
245
246     if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
247         (idata->reopen & IMAP_NEWMAIL_PENDING)
248         && count > idata->ctx->msgcount) {
249       /* read new mail messages */
250       dprint (2, (debugfile, "imap_cmd_finish: Fetching new mail\n"));
251       /* check_status: curs_main uses imap_check_mailbox to detect
252        *   whether the index needs updating */
253       idata->check_status = IMAP_NEWMAIL_PENDING;
254       imap_read_headers (idata, idata->ctx->msgcount, count - 1);
255     }
256     else if (idata->reopen & IMAP_EXPUNGE_PENDING) {
257       dprint (2, (debugfile, "imap_cmd_finish: Expunging mailbox\n"));
258       imap_expunge_mailbox (idata);
259       /* Detect whether we've gotten unexpected EXPUNGE messages */
260       if (idata->reopen & IMAP_EXPUNGE_PENDING &&
261           !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
262         idata->check_status = IMAP_EXPUNGE_PENDING;
263       idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
264                          IMAP_EXPUNGE_EXPECTED);
265     }
266   }
267
268   idata->status = 0;
269 }
270
271 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
272 static void cmd_handle_fatal (IMAP_DATA * idata)
273 {
274   idata->status = IMAP_FATAL;
275
276   if ((idata->state == IMAP_SELECTED) &&
277       (idata->reopen & IMAP_REOPEN_ALLOW) && !idata->ctx->closing) {
278     /*mx_fastclose_mailbox (idata->ctx); */
279     mutt_error (_("Mailbox closed"));
280     mutt_sleep (1);
281     idata->state = IMAP_DISCONNECTED;
282     if (imap_reconnect (idata->ctx) != 0)
283       mx_fastclose_mailbox (idata->ctx);
284   }
285
286   if (idata->state != IMAP_SELECTED) {
287     idata->state = IMAP_DISCONNECTED;
288     idata->status = 0;
289   }
290 }
291
292 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
293 static int cmd_handle_untagged (IMAP_DATA * idata)
294 {
295   char *s;
296   char *pn;
297   int count;
298
299   s = imap_next_word (idata->cmd.buf);
300
301   if ((idata->state == IMAP_SELECTED) && isdigit ((unsigned char) *s)) {
302     pn = s;
303     s = imap_next_word (s);
304
305     /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
306      * connection, so update that one.
307      */
308     if (ascii_strncasecmp ("EXISTS", s, 6) == 0) {
309       dprint (2, (debugfile, "Handling EXISTS\n"));
310
311       /* new mail arrived */
312       count = atoi (pn);
313
314       if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
315           count < idata->ctx->msgcount) {
316         /* something is wrong because the server reported fewer messages
317          * than we previously saw
318          */
319         mutt_error _("Fatal error.  Message count is out of sync!");
320
321         idata->status = IMAP_FATAL;
322         return -1;
323       }
324       /* at least the InterChange server sends EXISTS messages freely,
325        * even when there is no new mail */
326       else if (count == idata->ctx->msgcount)
327         dprint (3, (debugfile,
328                     "cmd_handle_untagged: superfluous EXISTS message.\n"));
329       else {
330         if (!(idata->reopen & IMAP_EXPUNGE_PENDING)) {
331           dprint (2, (debugfile,
332                       "cmd_handle_untagged: New mail in %s - %d messages total.\n",
333                       idata->mailbox, count));
334           idata->reopen |= IMAP_NEWMAIL_PENDING;
335         }
336         idata->newMailCount = count;
337       }
338     }
339     /* pn vs. s: need initial seqno */
340     else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
341       cmd_parse_expunge (idata, pn);
342     else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
343       cmd_parse_fetch (idata, pn);
344   }
345   else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
346     cmd_parse_capabilities (idata, s);
347   else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
348     cmd_parse_myrights (idata, s);
349   else if (ascii_strncasecmp ("BYE", s, 3) == 0) {
350     dprint (2, (debugfile, "Handling BYE\n"));
351
352     /* check if we're logging out */
353     if (idata->status == IMAP_BYE)
354       return 0;
355
356     /* server shut down our connection */
357     s += 3;
358     SKIPWS (s);
359     mutt_error ("%s", s);
360     idata->status = IMAP_BYE;
361
362     /*if (imap_reconnect(idata->ctx)!=0) {
363        if (idata->state == IMAP_SELECTED)
364        mx_fastclose_mailbox (idata->ctx); *//* XXX memleak? */
365     mutt_socket_close (idata->conn);
366     idata->state = IMAP_DISCONNECTED;
367     return -1;
368     /*} else {
369        return 0;
370        } */
371   }
372   else if (option (OPTIMAPSERVERNOISE)
373            && (ascii_strncasecmp ("NO", s, 2) == 0)) {
374     dprint (2, (debugfile, "Handling untagged NO\n"));
375
376     /* Display the warning message from the server */
377     mutt_error ("%s", s + 3);
378     mutt_sleep (2);
379   }
380
381   return 0;
382 }
383
384 /* cmd_make_sequence: make a tag suitable for starting an IMAP command */
385 static void cmd_make_sequence (IMAP_DATA * idata)
386 {
387   snprintf (idata->cmd.seq, sizeof (idata->cmd.seq), "a%04u", idata->seqno++);
388
389   if (idata->seqno > 9999)
390     idata->seqno = 0;
391 }
392
393 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
394  *   response */
395 static void cmd_parse_capabilities (IMAP_DATA * idata, char *s)
396 {
397   int x;
398
399   dprint (2, (debugfile, "Handling CAPABILITY\n"));
400
401   s = imap_next_word (s);
402   FREE (&idata->capstr);
403   idata->capstr = safe_strdup (s);
404
405   memset (idata->capabilities, 0, sizeof (idata->capabilities));
406
407   while (*s) {
408     for (x = 0; x < CAPMAX; x++)
409       if (imap_wordcasecmp (Capabilities[x], s) == 0) {
410         mutt_bit_set (idata->capabilities, x);
411         break;
412       }
413     s = imap_next_word (s);
414   }
415 }
416
417 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
418  *   be reopened at our earliest convenience */
419 static void cmd_parse_expunge (IMAP_DATA * idata, const char *s)
420 {
421   int expno, cur;
422   HEADER *h;
423
424   dprint (2, (debugfile, "Handling EXPUNGE\n"));
425
426   expno = atoi (s);
427
428   /* walk headers, zero seqno of expunged message, decrement seqno of those
429    * above. Possibly we could avoid walking the whole list by resorting
430    * and guessing a good starting point, but I'm guessing the resort would
431    * nullify the gains */
432   for (cur = 0; cur < idata->ctx->msgcount; cur++) {
433     h = idata->ctx->hdrs[cur];
434
435     if (h->index + 1 == expno)
436       h->index = -1;
437     else if (h->index + 1 > expno)
438       h->index--;
439   }
440
441   idata->reopen |= IMAP_EXPUNGE_PENDING;
442 }
443
444 /* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
445  *   handles unanticipated FETCH responses, and only FLAGS data. We get
446  *   these if another client has changed flags for a mailbox we've selected.
447  *   Of course, a lot of code here duplicates code in message.c. */
448 static void cmd_parse_fetch (IMAP_DATA * idata, char *s)
449 {
450   int msgno, cur;
451   HEADER *h = NULL;
452
453   dprint (2, (debugfile, "Handling FETCH\n"));
454
455   msgno = atoi (s);
456
457   if (msgno <= idata->ctx->msgcount)
458     /* see cmd_parse_expunge */
459     for (cur = 0; cur < idata->ctx->msgcount; cur++) {
460       h = idata->ctx->hdrs[cur];
461
462       if (h->active && h->index + 1 == msgno) {
463         dprint (2,
464                 (debugfile, "Message UID %d updated\n",
465                  HEADER_DATA (h)->uid));
466         break;
467       }
468
469       h = NULL;
470     }
471
472   if (!h) {
473     dprint (1, (debugfile, "FETCH response ignored for this message\n"));
474     return;
475   }
476
477   /* skip FETCH */
478   s = imap_next_word (s);
479   s = imap_next_word (s);
480
481   if (*s != '(') {
482     dprint (1, (debugfile, "Malformed FETCH response"));
483     return;
484   }
485   s++;
486
487   if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
488     dprint (2, (debugfile, "Only handle FLAGS updates\n"));
489     return;
490   }
491
492   /* If server flags could conflict with mutt's flags, reopen the mailbox. */
493   if (h->changed)
494     idata->reopen |= IMAP_EXPUNGE_PENDING;
495   else {
496     imap_set_flags (idata, h, s);
497     idata->check_status = IMAP_FLAGS_PENDING;
498   }
499 }
500
501 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
502 static void cmd_parse_myrights (IMAP_DATA * idata, char *s)
503 {
504   dprint (2, (debugfile, "Handling MYRIGHTS\n"));
505
506   s = imap_next_word (s);
507   s = imap_next_word (s);
508
509   /* zero out current rights set */
510   memset (idata->rights, 0, sizeof (idata->rights));
511
512   while (*s && !isspace ((unsigned char) *s)) {
513     switch (*s) {
514     case 'l':
515       mutt_bit_set (idata->rights, IMAP_ACL_LOOKUP);
516       break;
517     case 'r':
518       mutt_bit_set (idata->rights, IMAP_ACL_READ);
519       break;
520     case 's':
521       mutt_bit_set (idata->rights, IMAP_ACL_SEEN);
522       break;
523     case 'w':
524       mutt_bit_set (idata->rights, IMAP_ACL_WRITE);
525       break;
526     case 'i':
527       mutt_bit_set (idata->rights, IMAP_ACL_INSERT);
528       break;
529     case 'p':
530       mutt_bit_set (idata->rights, IMAP_ACL_POST);
531       break;
532     case 'c':
533       mutt_bit_set (idata->rights, IMAP_ACL_CREATE);
534       break;
535     case 'd':
536       mutt_bit_set (idata->rights, IMAP_ACL_DELETE);
537       break;
538     case 'a':
539       mutt_bit_set (idata->rights, IMAP_ACL_ADMIN);
540       break;
541     }
542     s++;
543   }
544 }