]> git.llucax.com Git - software/mutt-debian.git/blob - imap/message.c
removing an unused PATCHES
[software/mutt-debian.git] / imap / message.c
1 /*
2  * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
3  * Copyright (C) 1999-2009 Brendan Cully <brendan@kublai.com>
4  * 
5  *     This program is free software; you can redistribute it and/or modify
6  *     it under the terms of the GNU General Public License as published by
7  *     the Free Software Foundation; either version 2 of the License, or
8  *     (at your option) any later version.
9  * 
10  *     This program is distributed in the hope that it will be useful,
11  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *     GNU General Public License for more details.
14  * 
15  *     You should have received a copy of the GNU General Public License
16  *     along with this program; if not, write to the Free Software
17  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  *
19  */ 
20
21 /* message parsing/updating functions */
22
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 #include "mutt.h"
32 #include "imap_private.h"
33 #include "message.h"
34 #include "mx.h"
35
36 #ifdef HAVE_PGP
37 #include "pgp.h"
38 #endif
39
40 #if USE_HCACHE
41 #include "hcache.h"
42 #endif
43
44 #include "bcache.h"
45
46 static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h);
47 static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h);
48 static int msg_cache_commit (IMAP_DATA* idata, HEADER* h);
49
50 static void flush_buffer(char* buf, size_t* len, CONNECTION* conn);
51 static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf,
52   FILE* fp);
53 static int msg_parse_fetch (IMAP_HEADER* h, char* s);
54 static char* msg_parse_flags (IMAP_HEADER* h, char* s);
55
56 /* imap_read_headers:
57  * Changed to read many headers instead of just one. It will return the
58  * msgno of the last message read. It will return a value other than
59  * msgend if mail comes in while downloading headers (in theory).
60  */
61 int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
62 {
63   CONTEXT* ctx;
64   char buf[LONG_STRING];
65   char hdrreq[STRING];
66   FILE *fp;
67   char tempfile[_POSIX_PATH_MAX];
68   int msgno, idx;
69   IMAP_HEADER h;
70   IMAP_STATUS* status;
71   int rc, mfhrc, oldmsgcount;
72   int fetchlast = 0;
73   int maxuid = 0;
74   const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL";
75   progress_t progress;
76
77 #if USE_HCACHE
78   unsigned int *uid_validity = NULL;
79   unsigned int *puidnext = NULL;
80   unsigned int uidnext = 0;
81   int evalhc = 0;
82 #endif /* USE_HCACHE */
83
84   ctx = idata->ctx;
85
86   if (mutt_bit_isset (idata->capabilities,IMAP4REV1))
87   {
88     snprintf (hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", 
89               want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : ""); 
90   } 
91   else if (mutt_bit_isset (idata->capabilities,IMAP4))
92   {
93     snprintf (hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s%s%s)", 
94               want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : "");
95   }
96   else
97   {     /* Unable to fetch headers for lower versions */
98     mutt_error _("Unable to fetch headers from this IMAP server version.");
99     mutt_sleep (2);     /* pause a moment to let the user see the error */
100     return -1;
101   }
102
103   /* instead of downloading all headers and then parsing them, we parse them
104    * as they come in. */
105   mutt_mktemp (tempfile);
106   if (!(fp = safe_fopen (tempfile, "w+")))
107   {
108     mutt_error (_("Could not create temporary file %s"), tempfile);
109     mutt_sleep (2);
110     return -1;
111   }
112   unlink (tempfile);
113
114   /* make sure context has room to hold the mailbox */
115   while ((msgend) >= idata->ctx->hdrmax)
116     mx_alloc_memory (idata->ctx);
117
118   oldmsgcount = ctx->msgcount;
119   idata->reopen &= ~(IMAP_REOPEN_ALLOW|IMAP_NEWMAIL_PENDING);
120   idata->newMailCount = 0;
121
122 #if USE_HCACHE
123   idata->hcache = imap_hcache_open (idata, NULL);
124
125   if (idata->hcache && !msgbegin)
126   {
127     uid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", imap_hcache_keylen);
128     puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen);
129     if (puidnext)
130     {
131       uidnext = *puidnext;
132       FREE (&puidnext);
133     }
134     if (uid_validity && uidnext && *uid_validity == idata->uid_validity)
135       evalhc = 1;
136     FREE (&uid_validity);
137   }
138   if (evalhc)
139   {
140     mutt_progress_init (&progress, _("Evaluating cache..."),
141                         M_PROGRESS_MSG, ReadInc, msgend + 1);
142
143     snprintf (buf, sizeof (buf),
144       "UID FETCH 1:%u (UID FLAGS)", uidnext - 1);
145   
146     imap_cmd_start (idata, buf);
147   
148     rc = IMAP_CMD_CONTINUE;
149     for (msgno = msgbegin; rc == IMAP_CMD_CONTINUE; msgno++)
150     {
151       mutt_progress_update (&progress, msgno + 1, -1);
152   
153       memset (&h, 0, sizeof (h));
154       h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
155       do
156       {
157         mfhrc = 0;
158
159         rc = imap_cmd_step (idata);
160         if (rc != IMAP_CMD_CONTINUE)
161         {
162           /* suppress GCC aliasing warning */
163           imap_free_header_data ((void**) (void*) &h.data);
164           break;
165         }
166
167         /* hole in the header cache */
168         if (!evalhc)
169           continue;
170
171         if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, NULL)) == -1)
172           continue;
173         else if (mfhrc < 0)
174         {
175           imap_free_header_data ((void**) (void*) &h.data);
176           break;
177         }
178
179         if (!h.data->uid)
180         {
181           dprint (2, (debugfile, "imap_read_headers: skipping hcache FETCH "
182                       "response for unknown message number %d\n", h.sid));
183           mfhrc = -1;
184           continue;
185         }
186         
187         idx = h.sid - 1;
188         ctx->hdrs[idx] = imap_hcache_get (idata, h.data->uid);
189         if (ctx->hdrs[idx])
190         {
191           ctx->hdrs[idx]->index = idx;
192           /* messages which have not been expunged are ACTIVE (borrowed from mh 
193            * folders) */
194           ctx->hdrs[idx]->active = 1;
195           ctx->hdrs[idx]->read = h.data->read;
196           ctx->hdrs[idx]->old = h.data->old;
197           ctx->hdrs[idx]->deleted = h.data->deleted;
198           ctx->hdrs[idx]->flagged = h.data->flagged;
199           ctx->hdrs[idx]->replied = h.data->replied;
200           ctx->hdrs[idx]->changed = h.data->changed;
201           /*  ctx->hdrs[msgno]->received is restored from mutt_hcache_restore */
202           ctx->hdrs[idx]->data = (void *) (h.data);
203
204           ctx->msgcount++;
205           ctx->size += ctx->hdrs[idx]->content->length;
206         }
207         else
208         {
209           /* bad header in the cache, we'll have to refetch. */
210           dprint (3, (debugfile, "bad cache entry at %d, giving up\n", h.sid - 1));
211           imap_free_header_data((void**) (void*) &h.data);
212           evalhc = 0;
213         }
214       }
215       while (rc != IMAP_CMD_OK && mfhrc == -1);
216       if (rc == IMAP_CMD_OK)
217         break;
218       if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK)))
219       {
220         if (h.data)
221           imap_free_header_data ((void**) (void*) &h.data);
222         imap_hcache_close (idata);
223         fclose (fp);
224         return -1;
225       }
226     }
227     /* could also look for first null header in case hcache is holey */
228     msgbegin = ctx->msgcount;
229   }
230 #endif /* USE_HCACHE */
231
232   mutt_progress_init (&progress, _("Fetching message headers..."),
233                       M_PROGRESS_MSG, ReadInc, msgend + 1);
234
235   for (msgno = msgbegin; msgno <= msgend ; msgno++)
236   {
237     mutt_progress_update (&progress, msgno + 1, -1);
238
239     /* we may get notification of new mail while fetching headers */
240     if (msgno + 1 > fetchlast)
241     {
242       fetchlast = msgend + 1;
243
244       snprintf (buf, sizeof (buf),
245         "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1,
246         fetchlast, hdrreq);
247
248       imap_cmd_start (idata, buf);
249     }
250
251     rewind (fp);
252     memset (&h, 0, sizeof (h));
253     h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
254
255     /* this DO loop does two things:
256      * 1. handles untagged messages, so we can try again on the same msg
257      * 2. fetches the tagged response at the end of the last message.
258      */
259     do
260     {
261       mfhrc = 0;
262
263       rc = imap_cmd_step (idata);
264       if (rc != IMAP_CMD_CONTINUE)
265         break;
266
267       if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, fp)) == -1)
268         continue;
269       else if (mfhrc < 0)
270         break;
271
272       if (!ftello (fp))
273       {
274         dprint (2, (debugfile, "msg_fetch_header: ignoring fetch response with no body\n"));
275         mfhrc = -1;
276         continue;
277       }
278
279       /* make sure we don't get remnants from older larger message headers */
280       fputs ("\n\n", fp);
281
282       idx = h.sid - 1;
283       if (idx > msgend)
284       {
285         dprint (1, (debugfile, "imap_read_headers: skipping FETCH response for "
286                     "unknown message number %d\n", h.sid));
287         mfhrc = -1;
288         continue;
289       }
290
291       ctx->hdrs[idx] = mutt_new_header ();
292
293       ctx->hdrs[idx]->index = h.sid - 1;
294       /* messages which have not been expunged are ACTIVE (borrowed from mh 
295        * folders) */
296       ctx->hdrs[idx]->active = 1;
297       ctx->hdrs[idx]->read = h.data->read;
298       ctx->hdrs[idx]->old = h.data->old;
299       ctx->hdrs[idx]->deleted = h.data->deleted;
300       ctx->hdrs[idx]->flagged = h.data->flagged;
301       ctx->hdrs[idx]->replied = h.data->replied;
302       ctx->hdrs[idx]->changed = h.data->changed;
303       ctx->hdrs[idx]->received = h.received;
304       ctx->hdrs[idx]->data = (void *) (h.data);
305
306       if (maxuid < h.data->uid)
307         maxuid = h.data->uid;
308
309       rewind (fp);
310       /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends
311        *   on h.received being set */
312       ctx->hdrs[idx]->env = mutt_read_rfc822_header (fp, ctx->hdrs[idx],
313         0, 0);
314       /* content built as a side-effect of mutt_read_rfc822_header */
315       ctx->hdrs[idx]->content->length = h.content_length;
316       ctx->size += h.content_length;
317
318 #if USE_HCACHE
319       imap_hcache_put (idata, ctx->hdrs[idx]);
320 #endif /* USE_HCACHE */
321
322       ctx->msgcount++;
323     }
324     while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) ||
325       ((msgno + 1) >= fetchlast)));
326
327     if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK)))
328     {
329       if (h.data)
330         imap_free_header_data ((void**) (void*) &h.data);
331 #if USE_HCACHE
332       imap_hcache_close (idata);
333 #endif
334       fclose (fp);
335       return -1;
336     }
337
338     /* in case we get new mail while fetching the headers */
339     if (idata->reopen & IMAP_NEWMAIL_PENDING)
340     {
341       msgend = idata->newMailCount - 1;
342       while ((msgend) >= ctx->hdrmax)
343         mx_alloc_memory (ctx);
344       idata->reopen &= ~IMAP_NEWMAIL_PENDING;
345       idata->newMailCount = 0;
346     }
347   }
348
349   if (maxuid && (status = imap_mboxcache_get (idata, idata->mailbox, 0)))
350   status->uidnext = maxuid + 1;
351
352 #if USE_HCACHE
353   mutt_hcache_store_raw (idata->hcache, "/UIDVALIDITY", &idata->uid_validity,
354                          sizeof (idata->uid_validity), imap_hcache_keylen);
355   if (maxuid && idata->uidnext < maxuid + 1)
356   {
357     dprint (2, (debugfile, "Overriding UIDNEXT: %u -> %u\n", idata->uidnext, maxuid + 1));
358     idata->uidnext = maxuid + 1;
359   }
360   if (idata->uidnext > 1)
361     mutt_hcache_store_raw (idata->hcache, "/UIDNEXT", &idata->uidnext,
362                            sizeof (idata->uidnext), imap_hcache_keylen);
363
364   imap_hcache_close (idata);
365 #endif /* USE_HCACHE */
366
367   fclose(fp);
368
369   if (ctx->msgcount > oldmsgcount)
370   {
371     mx_alloc_memory(ctx);
372     mx_update_context (ctx, ctx->msgcount - oldmsgcount);
373   }
374
375   idata->reopen |= IMAP_REOPEN_ALLOW;
376   return msgend;
377 }
378
379 int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
380 {
381   IMAP_DATA* idata;
382   HEADER* h;
383   ENVELOPE* newenv;
384   char buf[LONG_STRING];
385   char path[_POSIX_PATH_MAX];
386   char *pc;
387   long bytes;
388   progress_t progressbar;
389   int uid;
390   int cacheno;
391   IMAP_CACHE *cache;
392   int read;
393   int rc;
394   /* Sam's weird courier server returns an OK response even when FETCH
395    * fails. Thanks Sam. */
396   short fetched = 0;
397
398   idata = (IMAP_DATA*) ctx->data;
399   h = ctx->hdrs[msgno];
400
401   if ((msg->fp = msg_cache_get (idata, h)))
402   {
403     if (HEADER_DATA(h)->parsed)
404       return 0;
405     else
406       goto parsemsg;
407   }
408
409   /* we still do some caching even if imap_cachedir is unset */
410   /* see if we already have the message in our cache */
411   cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
412   cache = &idata->cache[cacheno];
413
414   if (cache->path)
415   {
416     /* don't treat cache errors as fatal, just fall back. */
417     if (cache->uid == HEADER_DATA(h)->uid &&
418         (msg->fp = fopen (cache->path, "r")))
419       return 0;
420     else
421     {
422       unlink (cache->path);
423       FREE (&cache->path);
424     }
425   }
426
427   if (!isendwin())
428     mutt_message _("Fetching message...");
429
430   if (!(msg->fp = msg_cache_put (idata, h)))
431   {
432     cache->uid = HEADER_DATA(h)->uid;
433     mutt_mktemp (path);
434     cache->path = safe_strdup (path);
435     if (!(msg->fp = safe_fopen (path, "w+")))
436     {
437       FREE (&cache->path);
438       return -1;
439     }
440   }
441
442   /* mark this header as currently inactive so the command handler won't
443    * also try to update it. HACK until all this code can be moved into the
444    * command handler */
445   h->active = 0;
446   
447   snprintf (buf, sizeof (buf), "UID FETCH %u %s", HEADER_DATA(h)->uid,
448             (mutt_bit_isset (idata->capabilities, IMAP4REV1) ?
449              (option (OPTIMAPPEEK) ? "BODY.PEEK[]" : "BODY[]") :
450              "RFC822"));
451
452   imap_cmd_start (idata, buf);
453   do
454   {
455     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
456       break;
457
458     pc = idata->buf;
459     pc = imap_next_word (pc);
460     pc = imap_next_word (pc);
461
462     if (!ascii_strncasecmp ("FETCH", pc, 5))
463     {
464       while (*pc)
465       {
466         pc = imap_next_word (pc);
467         if (pc[0] == '(')
468           pc++;
469         if (ascii_strncasecmp ("UID", pc, 3) == 0)
470         {
471           pc = imap_next_word (pc);
472           uid = atoi (pc);
473           if (uid != HEADER_DATA(h)->uid)
474             mutt_error (_("The message index is incorrect. Try reopening the mailbox."));
475         }
476         else if ((ascii_strncasecmp ("RFC822", pc, 6) == 0) ||
477                  (ascii_strncasecmp ("BODY[]", pc, 6) == 0))
478         {
479           pc = imap_next_word (pc);
480           if (imap_get_literal_count(pc, &bytes) < 0)
481           {
482             imap_error ("imap_fetch_message()", buf);
483             goto bail;
484           }
485           mutt_progress_init (&progressbar, _("Fetching message..."),
486                               M_PROGRESS_SIZE, NetInc, bytes);
487           if (imap_read_literal (msg->fp, idata, bytes, &progressbar) < 0)
488             goto bail;
489           /* pick up trailing line */
490           if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
491             goto bail;
492           pc = idata->buf;
493
494           fetched = 1;
495         }
496         /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
497          * change (eg from \Unseen to \Seen).
498          * Uncommitted changes in mutt take precedence. If we decide to
499          * incrementally update flags later, this won't stop us syncing */
500         else if ((ascii_strncasecmp ("FLAGS", pc, 5) == 0) && !h->changed)
501         {
502           if ((pc = imap_set_flags (idata, h, pc)) == NULL)
503             goto bail;
504         }
505       }
506     }
507   }
508   while (rc == IMAP_CMD_CONTINUE);
509
510   /* see comment before command start. */
511   h->active = 1;
512
513   fflush (msg->fp);
514   if (ferror (msg->fp))
515   {
516     mutt_perror (cache->path);
517     goto bail;
518   }
519   
520   if (rc != IMAP_CMD_OK)
521     goto bail;
522
523   if (!fetched || !imap_code (idata->buf))
524     goto bail;
525
526   msg_cache_commit (idata, h);
527
528   parsemsg:
529   /* Update the header information.  Previously, we only downloaded a
530    * portion of the headers, those required for the main display.
531    */
532   rewind (msg->fp);
533   /* It may be that the Status header indicates a message is read, but the
534    * IMAP server doesn't know the message has been \Seen. So we capture
535    * the server's notion of 'read' and if it differs from the message info
536    * picked up in mutt_read_rfc822_header, we mark the message (and context
537    * changed). Another possiblity: ignore Status on IMAP?*/
538   read = h->read;
539   newenv = mutt_read_rfc822_header (msg->fp, h, 0, 0);
540   mutt_merge_envelopes(h->env, &newenv);
541
542   /* see above. We want the new status in h->read, so we unset it manually
543    * and let mutt_set_flag set it correctly, updating context. */
544   if (read != h->read)
545   {
546     h->read = read;
547     mutt_set_flag (ctx, h, M_NEW, read);
548   }
549
550   h->lines = 0;
551   fgets (buf, sizeof (buf), msg->fp);
552   while (!feof (msg->fp))
553   {
554     h->lines++;
555     fgets (buf, sizeof (buf), msg->fp);
556   }
557
558   h->content->length = ftell (msg->fp) - h->content->offset;
559
560   /* This needs to be done in case this is a multipart message */
561 #if defined(HAVE_PGP) || defined(HAVE_SMIME)
562   h->security = crypt_query (h->content);
563 #endif
564
565   mutt_clear_error();
566   rewind (msg->fp);
567   HEADER_DATA(h)->parsed = 1;
568
569   return 0;
570
571 bail:
572   safe_fclose (&msg->fp);
573   imap_cache_del (idata, h);
574   if (cache->path)
575   {
576     unlink (cache->path);
577     FREE (&cache->path);
578   }
579
580   return -1;
581 }
582
583 int imap_append_message (CONTEXT *ctx, MESSAGE *msg)
584 {
585   IMAP_DATA* idata;
586   FILE *fp;
587   char buf[LONG_STRING];
588   char mbox[LONG_STRING];
589   char mailbox[LONG_STRING]; 
590   size_t len;
591   progress_t progressbar;
592   size_t sent;
593   int c, last;
594   IMAP_MBOX mx;
595   int rc;
596
597   idata = (IMAP_DATA*) ctx->data;
598
599   if (imap_parse_path (ctx->path, &mx))
600     return -1;
601
602   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
603   if (!*mailbox)
604     strfcpy (mailbox, "INBOX", sizeof (mailbox));
605   
606   if ((fp = fopen (msg->path, "r")) == NULL)
607   {
608     mutt_perror (msg->path);
609     goto fail;
610   }
611
612   /* currently we set the \Seen flag on all messages, but probably we
613    * should scan the message Status header for flag info. Since we're
614    * already rereading the whole file for length it isn't any more
615    * expensive (it'd be nice if we had the file size passed in already
616    * by the code that writes the file, but that's a lot of changes.
617    * Ideally we'd have a HEADER structure with flag info here... */
618   for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
619   {
620     if(c == '\n' && last != '\r')
621       len++;
622
623     len++;
624   }
625   rewind (fp);
626
627   mutt_progress_init (&progressbar, _("Uploading message..."),
628                       M_PROGRESS_SIZE, NetInc, len);
629
630   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
631   snprintf (buf, sizeof (buf), "APPEND %s (%s%s%s%s%s) {%lu}", mbox,
632             msg->flags.read    ? "\\Seen"      : "",
633             msg->flags.read && (msg->flags.replied || msg->flags.flagged) ? " " : "",
634             msg->flags.replied ? "\\Answered" : "",
635             msg->flags.replied && msg->flags.flagged ? " " : "",
636             msg->flags.flagged ? "\\Flagged"  : "",
637             (unsigned long) len);
638
639   imap_cmd_start (idata, buf);
640
641   do
642     rc = imap_cmd_step (idata);
643   while (rc == IMAP_CMD_CONTINUE);
644
645   if (rc != IMAP_CMD_RESPOND)
646   {
647     char *pc;
648
649     dprint (1, (debugfile, "imap_append_message(): command failed: %s\n",
650                 idata->buf));
651
652     pc = idata->buf + SEQLEN;
653     SKIPWS (pc);
654     pc = imap_next_word (pc);
655     mutt_error ("%s", pc);
656     mutt_sleep (1);
657     fclose (fp);
658     goto fail;
659   }
660
661   for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
662   {
663     if (c == '\n' && last != '\r')
664       buf[len++] = '\r';
665
666     buf[len++] = c;
667
668     if (len > sizeof(buf) - 3)
669     {
670       sent += len;
671       flush_buffer(buf, &len, idata->conn);
672       mutt_progress_update (&progressbar, sent, -1);
673     }
674   }
675   
676   if (len)
677     flush_buffer(buf, &len, idata->conn);
678
679   mutt_socket_write (idata->conn, "\r\n");
680   fclose (fp);
681
682   do
683     rc = imap_cmd_step (idata);
684   while (rc == IMAP_CMD_CONTINUE);
685
686   if (!imap_code (idata->buf))
687   {
688     char *pc;
689
690     dprint (1, (debugfile, "imap_append_message(): command failed: %s\n",
691                 idata->buf));
692     pc = idata->buf + SEQLEN;
693     SKIPWS (pc);
694     pc = imap_next_word (pc);
695     mutt_error ("%s", pc);
696     mutt_sleep (1);
697     goto fail;
698   }
699
700   FREE (&mx.mbox);
701   return 0;
702
703  fail:
704   FREE (&mx.mbox);
705   return -1;
706 }
707
708 /* imap_copy_messages: use server COPY command to copy messages to another
709  *   folder.
710  *   Return codes:
711  *      -1: error
712  *       0: success
713  *       1: non-fatal error - try fetch/append */
714 int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete)
715 {
716   IMAP_DATA* idata;
717   BUFFER cmd, sync_cmd;
718   char mbox[LONG_STRING];
719   char mmbox[LONG_STRING];
720   char prompt[LONG_STRING];
721   int rc;
722   int n;
723   IMAP_MBOX mx;
724   int err_continue = M_NO;
725   int triedcreate = 0;
726
727   idata = (IMAP_DATA*) ctx->data;
728
729   if (imap_parse_path (dest, &mx))
730   {
731     dprint (1, (debugfile, "imap_copy_messages: bad destination %s\n", dest));
732     return -1;
733   }
734
735   /* check that the save-to folder is in the same account */
736   if (!mutt_account_match (&(CTX_DATA->conn->account), &(mx.account)))
737   {
738     dprint (3, (debugfile, "imap_copy_messages: %s not same server as %s\n",
739       dest, ctx->path));
740     return 1;
741   }
742
743   if (h && h->attach_del)
744   {
745     dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n"));
746     return 1;
747   }
748   
749   imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox));
750   if (!*mbox)
751     strfcpy (mbox, "INBOX", sizeof (mbox));
752   imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox);
753
754   /* loop in case of TRYCREATE */
755   do
756   {
757     memset (&sync_cmd, 0, sizeof (sync_cmd));
758     memset (&cmd, 0, sizeof (cmd));
759
760     /* Null HEADER* means copy tagged messages */
761     if (!h)
762     {
763       /* if any messages have attachments to delete, fall through to FETCH
764        * and APPEND. TODO: Copy what we can with COPY, fall through for the
765        * remainder. */
766       for (n = 0; n < ctx->msgcount; n++)
767       {
768         if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del)
769         {
770           dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n"));
771           return 1;
772         }
773
774         if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active &&
775             ctx->hdrs[n]->changed)
776         {
777           rc = imap_sync_message (idata, ctx->hdrs[n], &sync_cmd, &err_continue);
778           if (rc < 0)
779           {
780             dprint (1, (debugfile, "imap_copy_messages: could not sync\n"));
781             goto fail;
782           }
783         }
784       }
785
786       rc = imap_exec_msgset (idata, "UID COPY", mmbox, M_TAG, 0, 0);
787       if (!rc)
788       {
789         dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n"));
790         goto fail;
791       }
792       else if (rc < 0)
793       {
794         dprint (1, (debugfile, "could not queue copy\n"));
795         goto fail;
796       }
797       else
798         mutt_message (_("Copying %d messages to %s..."), rc, mbox);
799     }
800     else
801     {
802       mutt_message (_("Copying message %d to %s..."), h->index+1, mbox);
803       mutt_buffer_printf (&cmd, "UID COPY %u %s", HEADER_DATA (h)->uid, mmbox);
804
805       if (h->active && h->changed)
806       {
807         rc = imap_sync_message (idata, h, &sync_cmd, &err_continue);
808         if (rc < 0)
809         {
810           dprint (1, (debugfile, "imap_copy_messages: could not sync\n"));
811           goto fail;
812         }
813       }    
814       if ((rc = imap_exec (idata, cmd.data, IMAP_CMD_QUEUE)) < 0)
815       {
816         dprint (1, (debugfile, "could not queue copy\n"));
817         goto fail;
818       }
819     }
820
821     /* let's get it on */
822     rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
823     if (rc == -2)
824     {
825       if (triedcreate)
826       {
827         dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox));
828         break;
829       }
830       /* bail out if command failed for reasons other than nonexistent target */
831       if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11))
832         break;
833       dprint (3, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n"));
834       snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox);
835       if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1)
836       {
837         mutt_clear_error ();
838         break;
839       }
840       if (imap_create_mailbox (idata, mbox) < 0)
841         break;
842       triedcreate = 1;
843     }
844   }
845   while (rc == -2);
846
847   if (rc != 0)
848   {
849     imap_error ("imap_copy_messages", idata->buf);
850     goto fail;
851   }
852
853   /* cleanup */
854   if (delete)
855   {
856     if (!h)
857       for (n = 0; n < ctx->msgcount; n++)
858       {
859         if (ctx->hdrs[n]->tagged)
860         {
861           mutt_set_flag (ctx, ctx->hdrs[n], M_DELETE, 1);
862           if (option (OPTDELETEUNTAG))
863             mutt_set_flag (ctx, ctx->hdrs[n], M_TAG, 0);
864         }
865       }
866     else
867     {
868       mutt_set_flag (ctx, h, M_DELETE, 1);
869       if (option (OPTDELETEUNTAG))
870         mutt_set_flag (ctx, h, M_TAG, 0);
871     }
872   }
873
874   if (cmd.data)
875     FREE (&cmd.data);
876   if (sync_cmd.data)
877     FREE (&sync_cmd.data);
878   FREE (&mx.mbox);
879   return 0;
880
881  fail:
882   if (cmd.data)
883     FREE (&cmd.data);
884   if (sync_cmd.data)
885     FREE (&sync_cmd.data);
886   FREE (&mx.mbox);
887   return -1;
888 }
889
890 static body_cache_t *msg_cache_open (IMAP_DATA *idata)
891 {
892   char mailbox[_POSIX_PATH_MAX];
893
894   if (idata->bcache)
895     return idata->bcache;
896
897   imap_cachepath (idata, idata->mailbox, mailbox, sizeof (mailbox));
898
899   return mutt_bcache_open (&idata->conn->account, mailbox);
900 }
901
902 static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h)
903 {
904   char id[_POSIX_PATH_MAX];
905
906   if (!idata || !h)
907     return NULL;
908
909   idata->bcache = msg_cache_open (idata);
910   snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
911   return mutt_bcache_get (idata->bcache, id);
912 }
913
914 static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h)
915 {
916   char id[_POSIX_PATH_MAX];
917
918   if (!idata || !h)
919     return NULL;
920
921   idata->bcache = msg_cache_open (idata);
922   snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
923   return mutt_bcache_put (idata->bcache, id, 1);
924 }
925
926 static int msg_cache_commit (IMAP_DATA* idata, HEADER* h)
927 {
928   char id[_POSIX_PATH_MAX];
929
930   if (!idata || !h)
931     return -1;
932
933   idata->bcache = msg_cache_open (idata);
934   snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
935
936   return mutt_bcache_commit (idata->bcache, id);
937 }
938
939 int imap_cache_del (IMAP_DATA* idata, HEADER* h)
940 {
941   char id[_POSIX_PATH_MAX];
942
943   if (!idata || !h)
944     return -1;
945
946   idata->bcache = msg_cache_open (idata);
947   snprintf (id, sizeof (id), "%u-%u", idata->uid_validity, HEADER_DATA(h)->uid);
948   return mutt_bcache_del (idata->bcache, id);
949 }
950
951 static int msg_cache_clean_cb (const char* id, body_cache_t* bcache, void* data)
952 {
953   unsigned int uv, uid, n;
954   IMAP_DATA* idata = (IMAP_DATA*)data;
955
956   if (sscanf (id, "%u-%u", &uv, &uid) != 2)
957     return 0;
958
959   /* bad UID */
960   if (uv != idata->uid_validity)
961     mutt_bcache_del (bcache, id);
962
963   /* TODO: presort UIDs, walk in order */
964   for (n = 0; n < idata->ctx->msgcount; n++)
965   {
966     if (uid == HEADER_DATA(idata->ctx->hdrs[n])->uid)
967       return 0;
968   }
969   mutt_bcache_del (bcache, id);
970
971   return 0;
972 }
973
974 int imap_cache_clean (IMAP_DATA* idata)
975 {
976   idata->bcache = msg_cache_open (idata);
977   mutt_bcache_list (idata->bcache, msg_cache_clean_cb, idata);
978
979   return 0;
980 }
981
982 /* imap_add_keywords: concatenate custom IMAP tags to list, if they
983  *   appear in the folder flags list. Why wouldn't they? */
984 void imap_add_keywords (char* s, HEADER* h, LIST* mailbox_flags, size_t slen)
985 {
986   LIST *keywords;
987
988   if (!mailbox_flags || !HEADER_DATA(h) || !HEADER_DATA(h)->keywords)
989     return;
990
991   keywords = HEADER_DATA(h)->keywords->next;
992
993   while (keywords)
994   {
995     if (imap_has_flag (mailbox_flags, keywords->data))
996     {
997       safe_strcat (s, slen, keywords->data);
998       safe_strcat (s, slen, " ");
999     }
1000     keywords = keywords->next;
1001   }
1002 }
1003
1004 /* imap_free_header_data: free IMAP_HEADER structure */
1005 void imap_free_header_data (void** data)
1006 {
1007   /* this should be safe even if the list wasn't used */
1008   mutt_free_list (&(((IMAP_HEADER_DATA*) *data)->keywords));
1009
1010   FREE (data);          /* __FREE_CHECKED__ */
1011 }
1012
1013 /* imap_set_flags: fill out the message header according to the flags from
1014  *   the server. Expects a flags line of the form "FLAGS (flag flag ...)" */
1015 char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s)
1016 {
1017   CONTEXT* ctx = idata->ctx;
1018   IMAP_HEADER newh;
1019   IMAP_HEADER_DATA* hd;
1020   unsigned char readonly;
1021
1022   memset (&newh, 0, sizeof (newh));
1023   hd = h->data;
1024   newh.data = hd;
1025
1026   dprint (2, (debugfile, "imap_fetch_message: parsing FLAGS\n"));
1027   if ((s = msg_parse_flags (&newh, s)) == NULL)
1028     return NULL;
1029   
1030   /* YAUH (yet another ugly hack): temporarily set context to
1031    * read-write even if it's read-only, so *server* updates of
1032    * flags can be processed by mutt_set_flag. ctx->changed must
1033    * be restored afterwards */
1034   readonly = ctx->readonly;
1035   ctx->readonly = 0;
1036             
1037   mutt_set_flag (ctx, h, M_NEW, !(hd->read || hd->old));
1038   mutt_set_flag (ctx, h, M_OLD, hd->old);
1039   mutt_set_flag (ctx, h, M_READ, hd->read);
1040   mutt_set_flag (ctx, h, M_DELETE, hd->deleted);
1041   mutt_set_flag (ctx, h, M_FLAG, hd->flagged);
1042   mutt_set_flag (ctx, h, M_REPLIED, hd->replied);
1043
1044   /* this message is now definitively *not* changed (mutt_set_flag
1045    * marks things changed as a side-effect) */
1046   h->changed = 0;
1047   ctx->changed &= ~readonly;
1048   ctx->readonly = readonly;
1049
1050   return s;
1051 }
1052
1053
1054 /* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER.
1055  *   Expects string beginning with * n FETCH.
1056  *   Returns:
1057  *      0 on success
1058  *     -1 if the string is not a fetch response
1059  *     -2 if the string is a corrupt fetch response */
1060 static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
1061 {
1062   IMAP_DATA* idata;
1063   long bytes;
1064   int rc = -1; /* default now is that string isn't FETCH response*/
1065
1066   idata = (IMAP_DATA*) ctx->data;
1067
1068   if (buf[0] != '*')
1069     return rc;
1070   
1071   /* skip to message number */
1072   buf = imap_next_word (buf);
1073   h->sid = atoi (buf);
1074
1075   /* find FETCH tag */
1076   buf = imap_next_word (buf);
1077   if (ascii_strncasecmp ("FETCH", buf, 5))
1078     return rc;
1079
1080   rc = -2; /* we've got a FETCH response, for better or worse */
1081   if (!(buf = strchr (buf, '(')))
1082     return rc;
1083   buf++;
1084
1085   /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
1086    *   read header lines and call it again. Silly. */
1087   if ((rc = msg_parse_fetch (h, buf)) != -2 || !fp)
1088     return rc;
1089   
1090   if (imap_get_literal_count (buf, &bytes) == 0)
1091   {
1092     imap_read_literal (fp, idata, bytes, NULL);
1093
1094     /* we may have other fields of the FETCH _after_ the literal
1095      * (eg Domino puts FLAGS here). Nothing wrong with that, either.
1096      * This all has to go - we should accept literals and nonliterals
1097      * interchangeably at any time. */
1098     if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1099       return rc;
1100   
1101     if (msg_parse_fetch (h, idata->buf) == -1)
1102       return rc;
1103   }
1104
1105   rc = 0; /* success */
1106   
1107   /* subtract headers from message size - unfortunately only the subset of
1108    * headers we've requested. */
1109   h->content_length -= bytes;
1110
1111   return rc;
1112 }
1113
1114 /* msg_parse_fetch: handle headers returned from header fetch */
1115 static int msg_parse_fetch (IMAP_HEADER *h, char *s)
1116 {
1117   char tmp[SHORT_STRING];
1118   char *ptmp;
1119
1120   if (!s)
1121     return -1;
1122
1123   while (*s)
1124   {
1125     SKIPWS (s);
1126
1127     if (ascii_strncasecmp ("FLAGS", s, 5) == 0)
1128     {
1129       if ((s = msg_parse_flags (h, s)) == NULL)
1130         return -1;
1131     }
1132     else if (ascii_strncasecmp ("UID", s, 3) == 0)
1133     {
1134       s += 3;
1135       SKIPWS (s);
1136       h->data->uid = (unsigned int) atoi (s);
1137
1138       s = imap_next_word (s);
1139     }
1140     else if (ascii_strncasecmp ("INTERNALDATE", s, 12) == 0)
1141     {
1142       s += 12;
1143       SKIPWS (s);
1144       if (*s != '\"')
1145       {
1146         dprint (1, (debugfile, "msg_parse_fetch(): bogus INTERNALDATE entry: %s\n", s));
1147         return -1;
1148       }
1149       s++;
1150       ptmp = tmp;
1151       while (*s && *s != '\"')
1152         *ptmp++ = *s++;
1153       if (*s != '\"')
1154         return -1;
1155       s++; /* skip past the trailing " */
1156       *ptmp = 0;
1157       h->received = imap_parse_date (tmp);
1158     }
1159     else if (ascii_strncasecmp ("RFC822.SIZE", s, 11) == 0)
1160     {
1161       s += 11;
1162       SKIPWS (s);
1163       ptmp = tmp;
1164       while (isdigit ((unsigned char) *s))
1165         *ptmp++ = *s++;
1166       *ptmp = 0;
1167       h->content_length = atoi (tmp);
1168     }
1169     else if (!ascii_strncasecmp ("BODY", s, 4) ||
1170       !ascii_strncasecmp ("RFC822.HEADER", s, 13))
1171     {
1172       /* handle above, in msg_fetch_header */
1173       return -2;
1174     }
1175     else if (*s == ')')
1176       s++; /* end of request */
1177     else if (*s)
1178     {
1179       /* got something i don't understand */
1180       imap_error ("msg_parse_fetch", s);
1181       return -1;
1182     }
1183   }
1184
1185   return 0;
1186 }
1187
1188 /* msg_parse_flags: read a FLAGS token into an IMAP_HEADER */
1189 static char* msg_parse_flags (IMAP_HEADER* h, char* s)
1190 {
1191   IMAP_HEADER_DATA* hd = h->data;
1192
1193   /* sanity-check string */
1194   if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
1195   {
1196     dprint (1, (debugfile, "msg_parse_flags: not a FLAGS response: %s\n",
1197       s));
1198     return NULL;
1199   }
1200   s += 5;
1201   SKIPWS(s);
1202   if (*s != '(')
1203   {
1204     dprint (1, (debugfile, "msg_parse_flags: bogus FLAGS response: %s\n",
1205       s));
1206     return NULL;
1207   }
1208   s++;
1209
1210   mutt_free_list (&hd->keywords);
1211   hd->deleted = hd->flagged = hd->replied = hd->read = hd->old = 0;
1212
1213   /* start parsing */
1214   while (*s && *s != ')')
1215   {
1216     if (ascii_strncasecmp ("\\deleted", s, 8) == 0)
1217     {
1218       s += 8;
1219       hd->deleted = 1;
1220     }
1221     else if (ascii_strncasecmp ("\\flagged", s, 8) == 0)
1222     {
1223       s += 8;
1224       hd->flagged = 1;
1225     }
1226     else if (ascii_strncasecmp ("\\answered", s, 9) == 0)
1227     {
1228       s += 9;
1229       hd->replied = 1;
1230     }
1231     else if (ascii_strncasecmp ("\\seen", s, 5) == 0)
1232     {
1233       s += 5;
1234       hd->read = 1;
1235     }
1236     else if (ascii_strncasecmp ("\\recent", s, 7) == 0)
1237       s += 7;
1238     else if (ascii_strncasecmp ("old", s, 3) == 0)
1239     {
1240       s += 3;
1241       hd->old = 1;
1242     }
1243     else
1244     {
1245       /* store custom flags as well */
1246       char ctmp;
1247       char* flag_word = s;
1248
1249       if (!hd->keywords)
1250         hd->keywords = mutt_new_list ();
1251
1252       while (*s && !ISSPACE (*s) && *s != ')')
1253         s++;
1254       ctmp = *s;
1255       *s = '\0';
1256       mutt_add_list (hd->keywords, flag_word);
1257       *s = ctmp;
1258     }
1259     SKIPWS(s);
1260   }
1261
1262   /* wrap up, or note bad flags response */
1263   if (*s == ')')
1264     s++;
1265   else
1266   {
1267     dprint (1, (debugfile,
1268       "msg_parse_flags: Unterminated FLAGS response: %s\n", s));
1269     return NULL;
1270   }
1271
1272   return s;
1273 }
1274
1275 static void flush_buffer(char *buf, size_t *len, CONNECTION *conn)
1276 {
1277   buf[*len] = '\0';
1278   mutt_socket_write_n(conn, buf, *len);
1279   *len = 0;
1280 }