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