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