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 "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 = str_len (idata->cmd.seq) + str_len (cmd) + 4;
74   out = (char *) mem_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   mem_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       mem_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     mem_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 = str_len (idata->cmd.seq) + str_len (cmd) + 4;
186   out = (char *) mem_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   mem_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)) {
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     mutt_socket_close (idata->conn);
294     idata->status = 0;
295   }
296 }
297
298 /* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
299 static int cmd_handle_untagged (IMAP_DATA * idata)
300 {
301   char *s;
302   char *pn;
303   int count;
304
305   s = imap_next_word (idata->cmd.buf);
306
307   if ((idata->state == IMAP_SELECTED) && isdigit ((unsigned char) *s)) {
308     pn = s;
309     s = imap_next_word (s);
310
311     /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
312      * connection, so update that one.
313      */
314     if (ascii_strncasecmp ("EXISTS", s, 6) == 0) {
315       debug_print (2, ("Handling EXISTS\n"));
316
317       /* new mail arrived */
318       count = atoi (pn);
319
320       if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
321           count < idata->ctx->msgcount) {
322         /* something is wrong because the server reported fewer messages
323          * than we previously saw
324          */
325         mutt_error _("Fatal error.  Message count is out of sync!");
326
327         idata->status = IMAP_FATAL;
328         return -1;
329       }
330       /* at least the InterChange server sends EXISTS messages freely,
331        * even when there is no new mail */
332       else if (count == idata->ctx->msgcount)
333         debug_print (3, ("superfluous EXISTS message.\n"));
334       else {
335         if (!(idata->reopen & IMAP_EXPUNGE_PENDING)) {
336           debug_print (2, ("New mail in %s - %d messages total.\n", idata->mailbox, count));
337           idata->reopen |= IMAP_NEWMAIL_PENDING;
338         }
339         idata->newMailCount = count;
340       }
341     }
342     /* pn vs. s: need initial seqno */
343     else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
344       cmd_parse_expunge (idata, pn);
345     else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
346       cmd_parse_fetch (idata, pn);
347   }
348   else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
349     cmd_parse_capabilities (idata, s);
350   else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
351     cmd_parse_myrights (idata, s);
352   else if (ascii_strncasecmp ("BYE", s, 3) == 0) {
353     debug_print (2, ("Handling BYE\n"));
354
355     /* check if we're logging out */
356     if (idata->status == IMAP_BYE)
357       return 0;
358
359     /* server shut down our connection */
360     s += 3;
361     SKIPWS (s);
362     mutt_error ("%s", s);
363     mutt_sleep (2);
364     cmd_handle_fatal (idata);
365     return -1;
366   }
367   else if (option (OPTIMAPSERVERNOISE)
368            && (ascii_strncasecmp ("NO", s, 2) == 0)) {
369     debug_print (2, ("Handling untagged NO\n"));
370
371     /* Display the warning message from the server */
372     mutt_error ("%s", s + 3);
373     mutt_sleep (2);
374   }
375
376   return 0;
377 }
378
379 /* cmd_make_sequence: make a tag suitable for starting an IMAP command */
380 static void cmd_make_sequence (IMAP_DATA * idata)
381 {
382   snprintf (idata->cmd.seq, sizeof (idata->cmd.seq), "a%04u", idata->seqno++);
383
384   if (idata->seqno > 9999)
385     idata->seqno = 0;
386 }
387
388 /* cmd_parse_capabilities: set capability bits according to CAPABILITY
389  *   response */
390 static void cmd_parse_capabilities (IMAP_DATA * idata, char *s)
391 {
392   int x;
393
394   debug_print (2, ("Handling CAPABILITY\n"));
395
396   s = imap_next_word (s);
397   mem_free (&idata->capstr);
398   idata->capstr = str_dup (s);
399
400   memset (idata->capabilities, 0, sizeof (idata->capabilities));
401
402   while (*s) {
403     for (x = 0; x < CAPMAX; x++)
404       if (imap_wordcasecmp (Capabilities[x], s) == 0) {
405         mutt_bit_set (idata->capabilities, x);
406         break;
407       }
408     s = imap_next_word (s);
409   }
410 }
411
412 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
413  *   be reopened at our earliest convenience */
414 static void cmd_parse_expunge (IMAP_DATA * idata, const char *s)
415 {
416   int expno, cur;
417   HEADER *h;
418
419   debug_print (2, ("Handling EXPUNGE\n"));
420
421   expno = atoi (s);
422
423   /* walk headers, zero seqno of expunged message, decrement seqno of those
424    * above. Possibly we could avoid walking the whole list by resorting
425    * and guessing a good starting point, but I'm guessing the resort would
426    * nullify the gains */
427   for (cur = 0; cur < idata->ctx->msgcount; cur++) {
428     h = idata->ctx->hdrs[cur];
429
430     if (h->index + 1 == expno)
431       h->index = -1;
432     else if (h->index + 1 > expno)
433       h->index--;
434   }
435
436   idata->reopen |= IMAP_EXPUNGE_PENDING;
437 }
438
439 /* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
440  *   handles unanticipated FETCH responses, and only FLAGS data. We get
441  *   these if another client has changed flags for a mailbox we've selected.
442  *   Of course, a lot of code here duplicates code in message.c. */
443 static void cmd_parse_fetch (IMAP_DATA * idata, char *s)
444 {
445   int msgno, cur;
446   HEADER *h = NULL;
447
448   debug_print (2, ("Handling FETCH\n"));
449
450   msgno = atoi (s);
451
452   if (msgno <= idata->ctx->msgcount)
453     /* see cmd_parse_expunge */
454     for (cur = 0; cur < idata->ctx->msgcount; cur++) {
455       h = idata->ctx->hdrs[cur];
456
457       if (h->active && h->index + 1 == msgno) {
458         debug_print (2, ("Message UID %d updated\n", HEADER_DATA (h)->uid));
459         break;
460       }
461
462       h = NULL;
463     }
464
465   if (!h) {
466     debug_print (1, ("FETCH response ignored for this message\n"));
467     return;
468   }
469
470   /* skip FETCH */
471   s = imap_next_word (s);
472   s = imap_next_word (s);
473
474   if (*s != '(') {
475     debug_print (1, ("Malformed FETCH response\n"));
476     return;
477   }
478   s++;
479
480   if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
481     debug_print (2, ("Only handle FLAGS updates\n"));
482     return;
483   }
484
485   /* If server flags could conflict with mutt's flags, reopen the mailbox. */
486   if (h->changed)
487     idata->reopen |= IMAP_EXPUNGE_PENDING;
488   else {
489     imap_set_flags (idata, h, s);
490     idata->check_status = IMAP_FLAGS_PENDING;
491   }
492 }
493
494 /* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
495 static void cmd_parse_myrights (IMAP_DATA * idata, char *s)
496 {
497   debug_print (2, ("Handling MYRIGHTS\n"));
498
499   s = imap_next_word (s);
500   s = imap_next_word (s);
501
502   /* zero out current rights set */
503   memset (idata->rights, 0, sizeof (idata->rights));
504
505   while (*s && !isspace ((unsigned char) *s)) {
506     switch (*s) {
507     case 'l':
508       mutt_bit_set (idata->rights, ACL_LOOKUP);
509       break;
510     case 'r':
511       mutt_bit_set (idata->rights, ACL_READ);
512       break;
513     case 's':
514       mutt_bit_set (idata->rights, ACL_SEEN);
515       break;
516     case 'w':
517       mutt_bit_set (idata->rights, ACL_WRITE);
518       break;
519     case 'i':
520       mutt_bit_set (idata->rights, ACL_INSERT);
521       break;
522     case 'p':
523       mutt_bit_set (idata->rights, ACL_POST);
524       break;
525     case 'c':
526       mutt_bit_set (idata->rights, ACL_CREATE);
527       break;
528     case 'd':
529       mutt_bit_set (idata->rights, ACL_DELETE);
530       break;
531     case 'a':
532       mutt_bit_set (idata->rights, ACL_ADMIN);
533       break;
534     }
535     s++;
536   }
537 }