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