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