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