ba9d66631cdc610c5216d7a6c2aff6087cccccfe
[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) {
172     mutt_error (_("No mailbox is open."));
173     mutt_sleep (1);
174     return (-1);
175   }
176
177   if (idata->status == IMAP_FATAL) {
178     cmd_handle_fatal (idata);
179     return -1;
180   }
181
182   /* create sequence for command */
183   cmd_make_sequence (idata);
184   /* seq, space, cmd, \r\n\0 */
185   outlen = mutt_strlen (idata->cmd.seq) + mutt_strlen (cmd) + 4;
186   out = (char *) safe_malloc (outlen);
187   snprintf (out, outlen, "%s %s\r\n", idata->cmd.seq, cmd);
188
189   rc = mutt_socket_write_d (idata->conn, out,
190                             flags & IMAP_CMD_PASS ? IMAP_LOG_PASS :
191                             IMAP_LOG_CMD);
192   FREE (&out);
193
194   if (rc < 0) {
195     cmd_handle_fatal (idata);
196     return -1;
197   }
198
199   do
200     rc = imap_cmd_step (idata);
201   while (rc == IMAP_CMD_CONTINUE);
202
203   if (rc == IMAP_CMD_BAD) {
204     if (imap_reconnect (idata->ctx) != 0) {
205       return -1;
206     }
207     return 0;
208   }
209
210   if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
211     return -2;
212
213   if (rc != IMAP_CMD_OK) {
214     if (flags & IMAP_CMD_FAIL_OK)
215       return -2;
216
217     debug_print (1, ("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     cmd_handle_fatal (idata);
242     return;
243   }
244
245   if (!(idata->state == IMAP_SELECTED) || idata->ctx->closing)
246     return;
247
248   if (idata->reopen & IMAP_REOPEN_ALLOW) {
249     int count = idata->newMailCount;
250
251     if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
252         (idata->reopen & IMAP_NEWMAIL_PENDING)
253         && count > idata->ctx->msgcount) {
254       /* read new mail messages */
255       debug_print (2, ("Fetching new mail\n"));
256       /* check_status: curs_main uses imap_check_mailbox to detect
257        *   whether the index needs updating */
258       idata->check_status = IMAP_NEWMAIL_PENDING;
259       imap_read_headers (idata, idata->ctx->msgcount, count - 1);
260     }
261     else if (idata->reopen & IMAP_EXPUNGE_PENDING) {
262       debug_print (2, ("Expunging mailbox\n"));
263       imap_expunge_mailbox (idata);
264       /* Detect whether we've gotten unexpected EXPUNGE messages */
265       if (idata->reopen & IMAP_EXPUNGE_PENDING &&
266           !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
267         idata->check_status = IMAP_EXPUNGE_PENDING;
268       idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
269                          IMAP_EXPUNGE_EXPECTED);
270     }
271   }
272
273   idata->status = 0;
274 }
275
276 /* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
277 static void cmd_handle_fatal (IMAP_DATA * idata)
278 {
279   idata->status = IMAP_FATAL;
280
281   if ((idata->state == IMAP_SELECTED) &&
282       (idata->reopen & IMAP_REOPEN_ALLOW) && !idata->ctx->closing) {
283     /*mx_fastclose_mailbox (idata->ctx); */
284     mutt_error (_("Mailbox closed"));
285     mutt_sleep (1);
286     idata->state = IMAP_DISCONNECTED;
287     if (imap_reconnect (idata->ctx) != 0)
288       mx_fastclose_mailbox (idata->ctx);
289   }
290
291   if (idata->state != IMAP_SELECTED) {
292     idata->state = IMAP_DISCONNECTED;
293     idata->status = 0;
294   }
295 }
296
297 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
298 static int cmd_handle_untagged (IMAP_DATA * idata)
299 {
300   char *s;
301   char *pn;
302   int count;
303
304   s = imap_next_word (idata->cmd.buf);
305
306   if ((idata->state == IMAP_SELECTED) && isdigit ((unsigned char) *s)) {
307     pn = s;
308     s = imap_next_word (s);
309
310     /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
311      * connection, so update that one.
312      */
313     if (ascii_strncasecmp ("EXISTS", s, 6) == 0) {
314       debug_print (2, ("Handling EXISTS\n"));
315
316       /* new mail arrived */
317       count = atoi (pn);
318
319       if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
320           count < idata->ctx->msgcount) {
321         /* something is wrong because the server reported fewer messages
322          * than we previously saw
323          */
324         mutt_error _("Fatal error.  Message count is out of sync!");
325
326         idata->status = IMAP_FATAL;
327         return -1;
328       }
329       /* at least the InterChange server sends EXISTS messages freely,
330        * even when there is no new mail */
331       else if (count == idata->ctx->msgcount)
332         debug_print (3, ("superfluous EXISTS message.\n"));
333       else {
334         if (!(idata->reopen & IMAP_EXPUNGE_PENDING)) {
335           debug_print (2, ("New mail in %s - %d messages total.\n", idata->mailbox, count));
336           idata->reopen |= IMAP_NEWMAIL_PENDING;
337         }
338         idata->newMailCount = count;
339       }
340     }
341     /* pn vs. s: need initial seqno */
342     else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
343       cmd_parse_expunge (idata, pn);
344     else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
345       cmd_parse_fetch (idata, pn);
346   }
347   else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
348     cmd_parse_capabilities (idata, s);
349   else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
350     cmd_parse_myrights (idata, s);
351   else if (ascii_strncasecmp ("BYE", s, 3) == 0) {
352     debug_print (2, ("Handling BYE\n"));
353
354     /* check if we're logging out */
355     if (idata->status == IMAP_BYE)
356       return 0;
357
358     /* server shut down our connection */
359     s += 3;
360     SKIPWS (s);
361     mutt_error ("%s", s);
362     idata->status = IMAP_BYE;
363
364     /*if (imap_reconnect(idata->ctx)!=0) {
365        if (idata->state == IMAP_SELECTED)
366        mx_fastclose_mailbox (idata->ctx); *//* XXX memleak? */
367     mutt_socket_close (idata->conn);
368     idata->state = IMAP_DISCONNECTED;
369     return -1;
370     /*} else {
371        return 0;
372        } */
373   }
374   else if (option (OPTIMAPSERVERNOISE)
375            && (ascii_strncasecmp ("NO", s, 2) == 0)) {
376     debug_print (2, ("Handling untagged NO\n"));
377
378     /* Display the warning message from the server */
379     mutt_error ("%s", s + 3);
380     mutt_sleep (2);
381   }
382
383   return 0;
384 }
385
386 /* cmd_make_sequence: make a tag suitable for starting an IMAP command */
387 static void cmd_make_sequence (IMAP_DATA * idata)
388 {
389   snprintf (idata->cmd.seq, sizeof (idata->cmd.seq), "a%04u", idata->seqno++);
390
391   if (idata->seqno > 9999)
392     idata->seqno = 0;
393 }
394
395 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
396  *   response */
397 static void cmd_parse_capabilities (IMAP_DATA * idata, char *s)
398 {
399   int x;
400
401   debug_print (2, ("Handling CAPABILITY\n"));
402
403   s = imap_next_word (s);
404   FREE (&idata->capstr);
405   idata->capstr = safe_strdup (s);
406
407   memset (idata->capabilities, 0, sizeof (idata->capabilities));
408
409   while (*s) {
410     for (x = 0; x < CAPMAX; x++)
411       if (imap_wordcasecmp (Capabilities[x], s) == 0) {
412         mutt_bit_set (idata->capabilities, x);
413         break;
414       }
415     s = imap_next_word (s);
416   }
417 }
418
419 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
420  *   be reopened at our earliest convenience */
421 static void cmd_parse_expunge (IMAP_DATA * idata, const char *s)
422 {
423   int expno, cur;
424   HEADER *h;
425
426   debug_print (2, ("Handling EXPUNGE\n"));
427
428   expno = atoi (s);
429
430   /* walk headers, zero seqno of expunged message, decrement seqno of those
431    * above. Possibly we could avoid walking the whole list by resorting
432    * and guessing a good starting point, but I'm guessing the resort would
433    * nullify the gains */
434   for (cur = 0; cur < idata->ctx->msgcount; cur++) {
435     h = idata->ctx->hdrs[cur];
436
437     if (h->index + 1 == expno)
438       h->index = -1;
439     else if (h->index + 1 > expno)
440       h->index--;
441   }
442
443   idata->reopen |= IMAP_EXPUNGE_PENDING;
444 }
445
446 /* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
447  *   handles unanticipated FETCH responses, and only FLAGS data. We get
448  *   these if another client has changed flags for a mailbox we've selected.
449  *   Of course, a lot of code here duplicates code in message.c. */
450 static void cmd_parse_fetch (IMAP_DATA * idata, char *s)
451 {
452   int msgno, cur;
453   HEADER *h = NULL;
454
455   debug_print (2, ("Handling FETCH\n"));
456
457   msgno = atoi (s);
458
459   if (msgno <= idata->ctx->msgcount)
460     /* see cmd_parse_expunge */
461     for (cur = 0; cur < idata->ctx->msgcount; cur++) {
462       h = idata->ctx->hdrs[cur];
463
464       if (h->active && h->index + 1 == msgno) {
465         debug_print (2, ("Message UID %d updated\n", HEADER_DATA (h)->uid));
466         break;
467       }
468
469       h = NULL;
470     }
471
472   if (!h) {
473     debug_print (1, ("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     debug_print (1, ("Malformed FETCH response\n"));
483     return;
484   }
485   s++;
486
487   if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
488     debug_print (2, ("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   debug_print (2, ("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, ACL_LOOKUP);
516       break;
517     case 'r':
518       mutt_bit_set (idata->rights, ACL_READ);
519       break;
520     case 's':
521       mutt_bit_set (idata->rights, ACL_SEEN);
522       break;
523     case 'w':
524       mutt_bit_set (idata->rights, ACL_WRITE);
525       break;
526     case 'i':
527       mutt_bit_set (idata->rights, ACL_INSERT);
528       break;
529     case 'p':
530       mutt_bit_set (idata->rights, ACL_POST);
531       break;
532     case 'c':
533       mutt_bit_set (idata->rights, ACL_CREATE);
534       break;
535     case 'd':
536       mutt_bit_set (idata->rights, ACL_DELETE);
537       break;
538     case 'a':
539       mutt_bit_set (idata->rights, ACL_ADMIN);
540       break;
541     }
542     s++;
543   }
544 }