mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2002,2010,2013 Michael R. Elkins <me@mutt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19/* This file contains code to parse ``mbox'' and ``mmdf'' style mailboxes */
20
21#if HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include "mutt.h"
26#include "mailbox.h"
27#include "mx.h"
28#include "sort.h"
29#include "copy.h"
30#include "mutt_curses.h"
31
32#include <sys/stat.h>
33#include <dirent.h>
34#include <string.h>
35#include <utime.h>
36#include <sys/file.h>
37#include <errno.h>
38#include <unistd.h>
39#include <fcntl.h>
40
41/* struct used by mutt_sync_mailbox() to store new offsets */
42struct m_update_t
43{
44 short valid;
45 LOFF_T hdr;
46 LOFF_T body;
47 long lines;
48 LOFF_T length;
49};
50
51/* parameters:
52 * ctx - context to lock
53 * excl - exclusive lock?
54 * retry - should retry if unable to lock?
55 */
56int mbox_lock_mailbox (CONTEXT *ctx, int excl, int retry)
57{
58 int r;
59
60 if ((r = mx_lock_file (ctx->path, fileno (ctx->fp), excl, 1, retry)) == 0)
61 ctx->locked = 1;
62 else if (retry && !excl)
63 {
64 ctx->readonly = 1;
65 return 0;
66 }
67
68 return (r);
69}
70
71void mbox_unlock_mailbox (CONTEXT *ctx)
72{
73 if (ctx->locked)
74 {
75 fflush (ctx->fp);
76
77 mx_unlock_file (ctx->path, fileno (ctx->fp), 1);
78 ctx->locked = 0;
79 }
80}
81
82int mmdf_parse_mailbox (CONTEXT *ctx)
83{
84 char buf[HUGE_STRING];
85 char return_path[LONG_STRING];
86 int count = 0, oldmsgcount = ctx->msgcount;
87 int lines;
88 time_t t;
89 LOFF_T loc, tmploc;
90 HEADER *hdr;
91 struct stat sb;
92#ifdef NFS_ATTRIBUTE_HACK
93 struct utimbuf newtime;
94#endif
95 progress_t progress;
96 char msgbuf[STRING];
97
98 if (stat (ctx->path, &sb) == -1)
99 {
100 mutt_perror (ctx->path);
101 return (-1);
102 }
103 ctx->atime = sb.st_atime;
104 ctx->mtime = sb.st_mtime;
105 ctx->size = sb.st_size;
106
107#ifdef NFS_ATTRIBUTE_HACK
108 if (sb.st_mtime > sb.st_atime)
109 {
110 newtime.modtime = sb.st_mtime;
111 newtime.actime = time (NULL);
112 utime (ctx->path, &newtime);
113 }
114#endif
115
116 buf[sizeof (buf) - 1] = 0;
117
118 if (!ctx->quiet)
119 {
120 snprintf (msgbuf, sizeof (msgbuf), _("Reading %s..."), ctx->path);
121 mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, ReadInc, 0);
122 }
123
124 FOREVER
125 {
126 if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
127 break;
128
129 if (mutt_strcmp (buf, MMDF_SEP) == 0)
130 {
131 loc = ftello (ctx->fp);
132
133 count++;
134 if (!ctx->quiet)
135 mutt_progress_update (&progress, count,
136 (int) (loc / (ctx->size / 100 + 1)));
137
138 if (ctx->msgcount == ctx->hdrmax)
139 mx_alloc_memory (ctx);
140 ctx->hdrs[ctx->msgcount] = hdr = mutt_new_header ();
141 hdr->offset = loc;
142 hdr->index = ctx->msgcount;
143
144 if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
145 {
146 /* TODO: memory leak??? */
147 dprint (1, (debugfile, "mmdf_parse_mailbox: unexpected EOF\n"));
148 break;
149 }
150
151 return_path[0] = 0;
152
153 if (!is_from (buf, return_path, sizeof (return_path), &t))
154 {
155 if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
156 {
157 dprint (1, (debugfile, "mmdf_parse_mailbox: fseek() failed\n"));
158 mutt_error _("Mailbox is corrupt!");
159 return (-1);
160 }
161 }
162 else
163 hdr->received = t - mutt_local_tz (t);
164
165 hdr->env = mutt_read_rfc822_header (ctx->fp, hdr, 0, 0);
166
167 loc = ftello (ctx->fp);
168
169 if (hdr->content->length > 0 && hdr->lines > 0)
170 {
171 tmploc = loc + hdr->content->length;
172
173 if (0 < tmploc && tmploc < ctx->size)
174 {
175 if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
176 fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL ||
177 mutt_strcmp (MMDF_SEP, buf) != 0)
178 {
179 if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
180 dprint (1, (debugfile, "mmdf_parse_mailbox: fseek() failed\n"));
181 hdr->content->length = -1;
182 }
183 }
184 else
185 hdr->content->length = -1;
186 }
187 else
188 hdr->content->length = -1;
189
190 if (hdr->content->length < 0)
191 {
192 lines = -1;
193 do {
194 loc = ftello (ctx->fp);
195 if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
196 break;
197 lines++;
198 } while (mutt_strcmp (buf, MMDF_SEP) != 0);
199
200 hdr->lines = lines;
201 hdr->content->length = loc - hdr->content->offset;
202 }
203
204 if (!hdr->env->return_path && return_path[0])
205 hdr->env->return_path = rfc822_parse_adrlist (hdr->env->return_path, return_path);
206
207 if (!hdr->env->from)
208 hdr->env->from = rfc822_cpy_adr (hdr->env->return_path, 0);
209
210 ctx->msgcount++;
211 }
212 else
213 {
214 dprint (1, (debugfile, "mmdf_parse_mailbox: corrupt mailbox!\n"));
215 mutt_error _("Mailbox is corrupt!");
216 return (-1);
217 }
218 }
219
220 if (ctx->msgcount > oldmsgcount)
221 mx_update_context (ctx, ctx->msgcount - oldmsgcount);
222
223 return (0);
224}
225
226/* Note that this function is also called when new mail is appended to the
227 * currently open folder, and NOT just when the mailbox is initially read.
228 *
229 * NOTE: it is assumed that the mailbox being read has been locked before
230 * this routine gets called. Strange things could happen if it's not!
231 */
232int mbox_parse_mailbox (CONTEXT *ctx)
233{
234 struct stat sb;
235 char buf[HUGE_STRING], return_path[STRING];
236 HEADER *curhdr;
237 time_t t;
238 int count = 0, lines = 0;
239 LOFF_T loc;
240#ifdef NFS_ATTRIBUTE_HACK
241 struct utimbuf newtime;
242#endif
243 progress_t progress;
244 char msgbuf[STRING];
245
246 /* Save information about the folder at the time we opened it. */
247 if (stat (ctx->path, &sb) == -1)
248 {
249 mutt_perror (ctx->path);
250 return (-1);
251 }
252
253 ctx->size = sb.st_size;
254 ctx->mtime = sb.st_mtime;
255 ctx->atime = sb.st_atime;
256
257#ifdef NFS_ATTRIBUTE_HACK
258 if (sb.st_mtime > sb.st_atime)
259 {
260 newtime.modtime = sb.st_mtime;
261 newtime.actime = time (NULL);
262 utime (ctx->path, &newtime);
263 }
264#endif
265
266 if (!ctx->readonly)
267 ctx->readonly = access (ctx->path, W_OK) ? 1 : 0;
268
269 if (!ctx->quiet)
270 {
271 snprintf (msgbuf, sizeof (msgbuf), _("Reading %s..."), ctx->path);
272 mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, ReadInc, 0);
273 }
274
275 loc = ftello (ctx->fp);
276 while (fgets (buf, sizeof (buf), ctx->fp) != NULL)
277 {
278 if (is_from (buf, return_path, sizeof (return_path), &t))
279 {
280 /* Save the Content-Length of the previous message */
281 if (count > 0)
282 {
283#define PREV ctx->hdrs[ctx->msgcount-1]
284
285 if (PREV->content->length < 0)
286 {
287 PREV->content->length = loc - PREV->content->offset - 1;
288 if (PREV->content->length < 0)
289 PREV->content->length = 0;
290 }
291 if (!PREV->lines)
292 PREV->lines = lines ? lines - 1 : 0;
293 }
294
295 count++;
296
297 if (!ctx->quiet)
298 mutt_progress_update (&progress, count,
299 (int)(ftello (ctx->fp) / (ctx->size / 100 + 1)));
300
301 if (ctx->msgcount == ctx->hdrmax)
302 mx_alloc_memory (ctx);
303
304 curhdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
305 curhdr->received = t - mutt_local_tz (t);
306 curhdr->offset = loc;
307 curhdr->index = ctx->msgcount;
308
309 curhdr->env = mutt_read_rfc822_header (ctx->fp, curhdr, 0, 0);
310
311 /* if we know how long this message is, either just skip over the body,
312 * or if we don't know how many lines there are, count them now (this will
313 * save time by not having to search for the next message marker).
314 */
315 if (curhdr->content->length > 0)
316 {
317 LOFF_T tmploc;
318
319 loc = ftello (ctx->fp);
320 tmploc = loc + curhdr->content->length + 1;
321
322 if (0 < tmploc && tmploc < ctx->size)
323 {
324 /*
325 * check to see if the content-length looks valid. we expect to
326 * to see a valid message separator at this point in the stream
327 */
328 if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
329 fgets (buf, sizeof (buf), ctx->fp) == NULL ||
330 mutt_strncmp ("From ", buf, 5) != 0)
331 {
332 dprint (1, (debugfile, "mbox_parse_mailbox: bad content-length in message %d (cl=" OFF_T_FMT ")\n", curhdr->index, curhdr->content->length));
333 dprint (1, (debugfile, "\tLINE: %s", buf));
334 if (fseeko (ctx->fp, loc, SEEK_SET) != 0) /* nope, return the previous position */
335 {
336 dprint (1, (debugfile, "mbox_parse_mailbox: fseek() failed\n"));
337 }
338 curhdr->content->length = -1;
339 }
340 }
341 else if (tmploc != ctx->size)
342 {
343 /* content-length would put us past the end of the file, so it
344 * must be wrong
345 */
346 curhdr->content->length = -1;
347 }
348
349 if (curhdr->content->length != -1)
350 {
351 /* good content-length. check to see if we know how many lines
352 * are in this message.
353 */
354 if (curhdr->lines == 0)
355 {
356 int cl = curhdr->content->length;
357
358 /* count the number of lines in this message */
359 if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
360 dprint (1, (debugfile, "mbox_parse_mailbox: fseek() failed\n"));
361 while (cl-- > 0)
362 {
363 if (fgetc (ctx->fp) == '\n')
364 curhdr->lines++;
365 }
366 }
367
368 /* return to the offset of the next message separator */
369 if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0)
370 dprint (1, (debugfile, "mbox_parse_mailbox: fseek() failed\n"));
371 }
372 }
373
374 ctx->msgcount++;
375
376 if (!curhdr->env->return_path && return_path[0])
377 curhdr->env->return_path = rfc822_parse_adrlist (curhdr->env->return_path, return_path);
378
379 if (!curhdr->env->from)
380 curhdr->env->from = rfc822_cpy_adr (curhdr->env->return_path, 0);
381
382 lines = 0;
383 }
384 else
385 lines++;
386
387 loc = ftello (ctx->fp);
388 }
389
390 /*
391 * Only set the content-length of the previous message if we have read more
392 * than one message during _this_ invocation. If this routine is called
393 * when new mail is received, we need to make sure not to clobber what
394 * previously was the last message since the headers may be sorted.
395 */
396 if (count > 0)
397 {
398 if (PREV->content->length < 0)
399 {
400 PREV->content->length = ftello (ctx->fp) - PREV->content->offset - 1;
401 if (PREV->content->length < 0)
402 PREV->content->length = 0;
403 }
404
405 if (!PREV->lines)
406 PREV->lines = lines ? lines - 1 : 0;
407
408 mx_update_context (ctx, count);
409 }
410
411 return (0);
412}
413
414#undef PREV
415
416/* open a mbox or mmdf style mailbox */
417static int mbox_open_mailbox (CONTEXT *ctx)
418{
419 int rc;
420
421 if ((ctx->fp = fopen (ctx->path, "r")) == NULL)
422 {
423 mutt_perror (ctx->path);
424 return (-1);
425 }
426 mutt_block_signals ();
427 if (mbox_lock_mailbox (ctx, 0, 1) == -1)
428 {
429 mutt_unblock_signals ();
430 return (-1);
431 }
432
433 if (ctx->magic == MUTT_MBOX)
434 rc = mbox_parse_mailbox (ctx);
435 else if (ctx->magic == MUTT_MMDF)
436 rc = mmdf_parse_mailbox (ctx);
437 else
438 rc = -1;
439
440 mbox_unlock_mailbox (ctx);
441 mutt_unblock_signals ();
442 return (rc);
443}
444
445static int mbox_open_mailbox_append (CONTEXT *ctx, int flags)
446{
447 ctx->fp = safe_fopen (ctx->path, flags & MUTT_NEWFOLDER ? "w" : "a");
448 if (!ctx->fp)
449 {
450 mutt_perror (ctx->path);
451 return -1;
452 }
453
454 if (mbox_lock_mailbox (ctx, 1, 1) != 0)
455 {
456 mutt_error (_("Couldn't lock %s\n"), ctx->path);
457 safe_fclose (&ctx->fp);
458 return -1;
459 }
460
461 fseek (ctx->fp, 0, 2);
462
463 return 0;
464}
465
466static int mbox_close_mailbox (CONTEXT *ctx)
467{
468 if (ctx->append)
469 {
470 mx_unlock_file (ctx->path, fileno (ctx->fp), 1);
471 mutt_unblock_signals ();
472 }
473
474 safe_fclose (&ctx->fp);
475
476 return 0;
477}
478
479static int mbox_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
480{
481 msg->fp = ctx->fp;
482
483 return 0;
484}
485
486static int mbox_close_message (CONTEXT *ctx, MESSAGE *msg)
487{
488 msg->fp = NULL;
489
490 return 0;
491}
492
493static int mbox_commit_message (CONTEXT *ctx, MESSAGE *msg)
494{
495 if (fputc ('\n', msg->fp) == EOF)
496 return -1;
497
498 if ((fflush (msg->fp) == EOF) ||
499 (fsync (fileno (msg->fp)) == -1))
500 {
501 mutt_perror _("Can't write message");
502 return -1;
503 }
504
505 return 0;
506}
507
508static int mmdf_commit_message (CONTEXT *ctx, MESSAGE *msg)
509{
510 if (fputs (MMDF_SEP, msg->fp) == EOF)
511 return -1;
512
513 if ((fflush (msg->fp) == EOF) ||
514 (fsync (fileno (msg->fp)) == -1))
515 {
516 mutt_perror _("Can't write message");
517 return -1;
518 }
519
520 return 0;
521}
522
523static int mbox_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
524{
525 msg->fp = dest->fp;
526 return 0;
527}
528
529/* return 1 if address lists are strictly identical */
530static int strict_addrcmp (const ADDRESS *a, const ADDRESS *b)
531{
532 while (a && b)
533 {
534 if (mutt_strcmp (a->mailbox, b->mailbox) ||
535 mutt_strcmp (a->personal, b->personal))
536 return (0);
537
538 a = a->next;
539 b = b->next;
540 }
541 if (a || b)
542 return (0);
543
544 return (1);
545}
546
547static int strict_cmp_lists (const LIST *a, const LIST *b)
548{
549 while (a && b)
550 {
551 if (mutt_strcmp (a->data, b->data))
552 return (0);
553
554 a = a->next;
555 b = b->next;
556 }
557 if (a || b)
558 return (0);
559
560 return (1);
561}
562
563static int strict_cmp_envelopes (const ENVELOPE *e1, const ENVELOPE *e2)
564{
565 if (e1 && e2)
566 {
567 if (mutt_strcmp (e1->message_id, e2->message_id) ||
568 mutt_strcmp (e1->subject, e2->subject) ||
569 !strict_cmp_lists (e1->references, e2->references) ||
570 !strict_addrcmp (e1->from, e2->from) ||
571 !strict_addrcmp (e1->sender, e2->sender) ||
572 !strict_addrcmp (e1->reply_to, e2->reply_to) ||
573 !strict_addrcmp (e1->to, e2->to) ||
574 !strict_addrcmp (e1->cc, e2->cc) ||
575 !strict_addrcmp (e1->return_path, e2->return_path))
576 return (0);
577 else
578 return (1);
579 }
580 else
581 {
582 if (e1 == NULL && e2 == NULL)
583 return (1);
584 else
585 return (0);
586 }
587}
588
589static int strict_cmp_parameters (const PARAMETER *p1, const PARAMETER *p2)
590{
591 while (p1 && p2)
592 {
593 if (mutt_strcmp (p1->attribute, p2->attribute) ||
594 mutt_strcmp (p1->value, p2->value))
595 return (0);
596
597 p1 = p1->next;
598 p2 = p2->next;
599 }
600 if (p1 || p2)
601 return (0);
602
603 return (1);
604}
605
606static int strict_cmp_bodies (const BODY *b1, const BODY *b2)
607{
608 if (b1->type != b2->type ||
609 b1->encoding != b2->encoding ||
610 mutt_strcmp (b1->subtype, b2->subtype) ||
611 mutt_strcmp (b1->description, b2->description) ||
612 !strict_cmp_parameters (b1->parameter, b2->parameter) ||
613 b1->length != b2->length)
614 return (0);
615 return (1);
616}
617
618/* return 1 if headers are strictly identical */
619int mbox_strict_cmp_headers (const HEADER *h1, const HEADER *h2)
620{
621 if (h1 && h2)
622 {
623 if (h1->received != h2->received ||
624 h1->date_sent != h2->date_sent ||
625 h1->content->length != h2->content->length ||
626 h1->lines != h2->lines ||
627 h1->zhours != h2->zhours ||
628 h1->zminutes != h2->zminutes ||
629 h1->zoccident != h2->zoccident ||
630 h1->mime != h2->mime ||
631 !strict_cmp_envelopes (h1->env, h2->env) ||
632 !strict_cmp_bodies (h1->content, h2->content))
633 return (0);
634 else
635 return (1);
636 }
637 else
638 {
639 if (h1 == NULL && h2 == NULL)
640 return (1);
641 else
642 return (0);
643 }
644}
645
646/* check to see if the mailbox has changed on disk.
647 *
648 * return values:
649 * MUTT_REOPENED mailbox has been reopened
650 * MUTT_NEW_MAIL new mail has arrived!
651 * MUTT_LOCKED couldn't lock the file
652 * 0 no change
653 * -1 error
654 */
655static int mbox_check_mailbox (CONTEXT *ctx, int *index_hint)
656{
657 struct stat st;
658 char buffer[LONG_STRING];
659 int unlock = 0;
660 int modified = 0;
661
662 if (stat (ctx->path, &st) == 0)
663 {
664 if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
665 return (0);
666
667 if (st.st_size == ctx->size)
668 {
669 /* the file was touched, but it is still the same length, so just exit */
670 ctx->mtime = st.st_mtime;
671 return (0);
672 }
673
674 if (st.st_size > ctx->size)
675 {
676 /* lock the file if it isn't already */
677 if (!ctx->locked)
678 {
679 mutt_block_signals ();
680 if (mbox_lock_mailbox (ctx, 0, 0) == -1)
681 {
682 mutt_unblock_signals ();
683 /* we couldn't lock the mailbox, but nothing serious happened:
684 * probably the new mail arrived: no reason to wait till we can
685 * parse it: we'll get it on the next pass
686 */
687 return (MUTT_LOCKED);
688 }
689 unlock = 1;
690 }
691
692 /*
693 * Check to make sure that the only change to the mailbox is that
694 * message(s) were appended to this file. My heuristic is that we should
695 * see the message separator at *exactly* what used to be the end of the
696 * folder.
697 */
698 if (fseeko (ctx->fp, ctx->size, SEEK_SET) != 0)
699 dprint (1, (debugfile, "mbox_check_mailbox: fseek() failed\n"));
700 if (fgets (buffer, sizeof (buffer), ctx->fp) != NULL)
701 {
702 if ((ctx->magic == MUTT_MBOX && mutt_strncmp ("From ", buffer, 5) == 0) ||
703 (ctx->magic == MUTT_MMDF && mutt_strcmp (MMDF_SEP, buffer) == 0))
704 {
705 if (fseeko (ctx->fp, ctx->size, SEEK_SET) != 0)
706 dprint (1, (debugfile, "mbox_check_mailbox: fseek() failed\n"));
707 if (ctx->magic == MUTT_MBOX)
708 mbox_parse_mailbox (ctx);
709 else
710 mmdf_parse_mailbox (ctx);
711
712 /* Only unlock the folder if it was locked inside of this routine.
713 * It may have been locked elsewhere, like in
714 * mutt_checkpoint_mailbox().
715 */
716
717 if (unlock)
718 {
719 mbox_unlock_mailbox (ctx);
720 mutt_unblock_signals ();
721 }
722
723 return (MUTT_NEW_MAIL); /* signal that new mail arrived */
724 }
725 else
726 modified = 1;
727 }
728 else
729 {
730 dprint (1, (debugfile, "mbox_check_mailbox: fgets returned NULL.\n"));
731 modified = 1;
732 }
733 }
734 else
735 modified = 1;
736 }
737
738 if (modified)
739 {
740 if (mutt_reopen_mailbox (ctx, index_hint) != -1)
741 {
742 if (unlock)
743 {
744 mbox_unlock_mailbox (ctx);
745 mutt_unblock_signals ();
746 }
747 return (MUTT_REOPENED);
748 }
749 }
750
751 /* fatal error */
752
753 mbox_unlock_mailbox (ctx);
754 mx_fastclose_mailbox (ctx);
755 mutt_unblock_signals ();
756 mutt_error _("Mailbox was corrupted!");
757 return (-1);
758}
759
760/*
761 * Returns 1 if the mailbox has at least 1 new messages (not old)
762 * otherwise returns 0.
763 */
764static int mbox_has_new(CONTEXT *ctx)
765{
766 int i;
767
768 for (i = 0; i < ctx->msgcount; i++)
769 if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->read && !ctx->hdrs[i]->old)
770 return 1;
771 return 0;
772}
773
774/* if mailbox has at least 1 new message, sets mtime > atime of mailbox
775 * so buffy check reports new mail */
776void mbox_reset_atime (CONTEXT *ctx, struct stat *st)
777{
778 struct utimbuf utimebuf;
779 struct stat _st;
780
781 if (!st)
782 {
783 if (stat (ctx->path, &_st) < 0)
784 return;
785 st = &_st;
786 }
787
788 utimebuf.actime = st->st_atime;
789 utimebuf.modtime = st->st_mtime;
790
791 /*
792 * When $mbox_check_recent is set, existing new mail is ignored, so do not
793 * reset the atime to mtime-1 to signal new mail.
794 */
795 if (!option(OPTMAILCHECKRECENT) && utimebuf.actime >= utimebuf.modtime && mbox_has_new(ctx))
796 utimebuf.actime = utimebuf.modtime - 1;
797
798 utime (ctx->path, &utimebuf);
799}
800
801/* return values:
802 * 0 success
803 * -1 failure
804 */
805static int mbox_sync_mailbox (CONTEXT *ctx, int *index_hint)
806{
807 char tempfile[_POSIX_PATH_MAX];
808 char buf[32];
809 int i, j, save_sort = SORT_ORDER;
810 int rc = -1;
811 int need_sort = 0; /* flag to resort mailbox if new mail arrives */
812 int first = -1; /* first message to be written */
813 LOFF_T offset; /* location in mailbox to write changed messages */
814 struct stat statbuf;
815 struct m_update_t *newOffset = NULL;
816 struct m_update_t *oldOffset = NULL;
817 FILE *fp = NULL;
818 progress_t progress;
819 char msgbuf[STRING];
820 BUFFY *tmp = NULL;
821
822 /* sort message by their position in the mailbox on disk */
823 if (Sort != SORT_ORDER)
824 {
825 save_sort = Sort;
826 Sort = SORT_ORDER;
827 mutt_sort_headers (ctx, 0);
828 Sort = save_sort;
829 need_sort = 1;
830 }
831
832 /* need to open the file for writing in such a way that it does not truncate
833 * the file, so use read-write mode.
834 */
835 if ((ctx->fp = freopen (ctx->path, "r+", ctx->fp)) == NULL)
836 {
837 mx_fastclose_mailbox (ctx);
838 mutt_error _("Fatal error! Could not reopen mailbox!");
839 return (-1);
840 }
841
842 mutt_block_signals ();
843
844 if (mbox_lock_mailbox (ctx, 1, 1) == -1)
845 {
846 mutt_unblock_signals ();
847 mutt_error _("Unable to lock mailbox!");
848 goto bail;
849 }
850
851 /* Check to make sure that the file hasn't changed on disk */
852 if ((i = mbox_check_mailbox (ctx, index_hint)) == MUTT_NEW_MAIL || i == MUTT_REOPENED)
853 {
854 /* new mail arrived, or mailbox reopened */
855 need_sort = i;
856 rc = i;
857 goto bail;
858 }
859 else if (i < 0)
860 /* fatal error */
861 return (-1);
862
863 /* Create a temporary file to write the new version of the mailbox in. */
864 mutt_mktemp (tempfile, sizeof (tempfile));
865 if ((i = open (tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1 ||
866 (fp = fdopen (i, "w")) == NULL)
867 {
868 if (-1 != i)
869 {
870 close (i);
871 unlink (tempfile);
872 }
873 mutt_error _("Could not create temporary file!");
874 mutt_sleep (5);
875 goto bail;
876 }
877
878 /* find the first deleted/changed message. we save a lot of time by only
879 * rewriting the mailbox from the point where it has actually changed.
880 */
881 for (i = 0 ; i < ctx->msgcount && !ctx->hdrs[i]->deleted &&
882 !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++)
883 ;
884 if (i == ctx->msgcount)
885 {
886 /* this means ctx->changed or ctx->deleted was set, but no
887 * messages were found to be changed or deleted. This should
888 * never happen, is we presume it is a bug in mutt.
889 */
890 mutt_error _("sync: mbox modified, but no modified messages! (report this bug)");
891 mutt_sleep(5); /* the mutt_error /will/ get cleared! */
892 dprint(1, (debugfile, "mbox_sync_mailbox(): no modified messages.\n"));
893 unlink (tempfile);
894 goto bail;
895 }
896
897 /* save the index of the first changed/deleted message */
898 first = i;
899 /* where to start overwriting */
900 offset = ctx->hdrs[i]->offset;
901
902 /* the offset stored in the header does not include the MMDF_SEP, so make
903 * sure we seek to the correct location
904 */
905 if (ctx->magic == MUTT_MMDF)
906 offset -= (sizeof MMDF_SEP - 1);
907
908 /* allocate space for the new offsets */
909 newOffset = safe_calloc (ctx->msgcount - first, sizeof (struct m_update_t));
910 oldOffset = safe_calloc (ctx->msgcount - first, sizeof (struct m_update_t));
911
912 if (!ctx->quiet)
913 {
914 snprintf (msgbuf, sizeof (msgbuf), _("Writing %s..."), ctx->path);
915 mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, WriteInc, ctx->msgcount);
916 }
917
918 for (i = first, j = 0; i < ctx->msgcount; i++)
919 {
920 if (!ctx->quiet)
921 mutt_progress_update (&progress, i, (int)(ftello (ctx->fp) / (ctx->size / 100 + 1)));
922 /*
923 * back up some information which is needed to restore offsets when
924 * something fails.
925 */
926
927 oldOffset[i-first].valid = 1;
928 oldOffset[i-first].hdr = ctx->hdrs[i]->offset;
929 oldOffset[i-first].body = ctx->hdrs[i]->content->offset;
930 oldOffset[i-first].lines = ctx->hdrs[i]->lines;
931 oldOffset[i-first].length = ctx->hdrs[i]->content->length;
932
933 if (! ctx->hdrs[i]->deleted)
934 {
935 j++;
936
937 if (ctx->magic == MUTT_MMDF)
938 {
939 if (fputs (MMDF_SEP, fp) == EOF)
940 {
941 mutt_perror (tempfile);
942 mutt_sleep (5);
943 unlink (tempfile);
944 goto bail;
945 }
946
947 }
948
949 /* save the new offset for this message. we add `offset' because the
950 * temporary file only contains saved message which are located after
951 * `offset' in the real mailbox
952 */
953 newOffset[i - first].hdr = ftello (fp) + offset;
954
955 if (mutt_copy_message (fp, ctx, ctx->hdrs[i], MUTT_CM_UPDATE,
956 CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0)
957 {
958 mutt_perror (tempfile);
959 mutt_sleep (5);
960 unlink (tempfile);
961 goto bail;
962 }
963
964 /* Since messages could have been deleted, the offsets stored in memory
965 * will be wrong, so update what we can, which is the offset of this
966 * message, and the offset of the body. If this is a multipart message,
967 * we just flush the in memory cache so that the message will be reparsed
968 * if the user accesses it later.
969 */
970 newOffset[i - first].body = ftello (fp) - ctx->hdrs[i]->content->length + offset;
971 mutt_free_body (&ctx->hdrs[i]->content->parts);
972
973 switch(ctx->magic)
974 {
975 case MUTT_MMDF:
976 if(fputs(MMDF_SEP, fp) == EOF)
977 {
978 mutt_perror (tempfile);
979 mutt_sleep (5);
980 unlink (tempfile);
981 goto bail;
982 }
983 break;
984 default:
985 if(fputs("\n", fp) == EOF)
986 {
987 mutt_perror (tempfile);
988 mutt_sleep (5);
989 unlink (tempfile);
990 goto bail;
991 }
992 }
993 }
994 }
995
996 if (fclose (fp) != 0)
997 {
998 fp = NULL;
999 dprint(1, (debugfile, "mbox_sync_mailbox: safe_fclose (&) returned non-zero.\n"));
1000 unlink (tempfile);
1001 mutt_perror (tempfile);
1002 mutt_sleep (5);
1003 goto bail;
1004 }
1005 fp = NULL;
1006
1007 /* Save the state of this folder. */
1008 if (stat (ctx->path, &statbuf) == -1)
1009 {
1010 mutt_perror (ctx->path);
1011 mutt_sleep (5);
1012 unlink (tempfile);
1013 goto bail;
1014 }
1015
1016 if ((fp = fopen (tempfile, "r")) == NULL)
1017 {
1018 mutt_unblock_signals ();
1019 mx_fastclose_mailbox (ctx);
1020 dprint (1, (debugfile, "mbox_sync_mailbox: unable to reopen temp copy of mailbox!\n"));
1021 mutt_perror (tempfile);
1022 mutt_sleep (5);
1023 return (-1);
1024 }
1025
1026 if (fseeko (ctx->fp, offset, SEEK_SET) != 0 || /* seek the append location */
1027 /* do a sanity check to make sure the mailbox looks ok */
1028 fgets (buf, sizeof (buf), ctx->fp) == NULL ||
1029 (ctx->magic == MUTT_MBOX && mutt_strncmp ("From ", buf, 5) != 0) ||
1030 (ctx->magic == MUTT_MMDF && mutt_strcmp (MMDF_SEP, buf) != 0))
1031 {
1032 dprint (1, (debugfile, "mbox_sync_mailbox: message not in expected position."));
1033 dprint (1, (debugfile, "\tLINE: %s\n", buf));
1034 i = -1;
1035 }
1036 else
1037 {
1038 if (fseeko (ctx->fp, offset, SEEK_SET) != 0) /* return to proper offset */
1039 {
1040 i = -1;
1041 dprint (1, (debugfile, "mbox_sync_mailbox: fseek() failed\n"));
1042 }
1043 else
1044 {
1045 /* copy the temp mailbox back into place starting at the first
1046 * change/deleted message
1047 */
1048 if (!ctx->quiet)
1049 mutt_message _("Committing changes...");
1050 i = mutt_copy_stream (fp, ctx->fp);
1051
1052 if (ferror (ctx->fp))
1053 i = -1;
1054 }
1055 if (i == 0)
1056 {
1057 ctx->size = ftello (ctx->fp); /* update the size of the mailbox */
1058 if (ftruncate (fileno (ctx->fp), ctx->size) != 0)
1059 {
1060 i = -1;
1061 dprint (1, (debugfile, "mbox_sync_mailbox: ftruncate() failed\n"));
1062 }
1063 }
1064 }
1065
1066 safe_fclose (&fp);
1067 fp = NULL;
1068 mbox_unlock_mailbox (ctx);
1069
1070 if (safe_fclose (&ctx->fp) != 0 || i == -1)
1071 {
1072 /* error occurred while writing the mailbox back, so keep the temp copy
1073 * around
1074 */
1075
1076 char savefile[_POSIX_PATH_MAX];
1077
1078 snprintf (savefile, sizeof (savefile), "%s/mutt.%s-%s-%u",
1079 NONULL (Tempdir), NONULL(Username), NONULL(Hostname), (unsigned int)getpid ());
1080 rename (tempfile, savefile);
1081 mutt_unblock_signals ();
1082 mx_fastclose_mailbox (ctx);
1083 mutt_pretty_mailbox (savefile, sizeof (savefile));
1084 mutt_error (_("Write failed! Saved partial mailbox to %s"), savefile);
1085 mutt_sleep (5);
1086 return (-1);
1087 }
1088
1089 /* Restore the previous access/modification times */
1090 mbox_reset_atime (ctx, &statbuf);
1091
1092 /* reopen the mailbox in read-only mode */
1093 if ((ctx->fp = fopen (ctx->path, "r")) == NULL)
1094 {
1095 unlink (tempfile);
1096 mutt_unblock_signals ();
1097 mx_fastclose_mailbox (ctx);
1098 mutt_error _("Fatal error! Could not reopen mailbox!");
1099 return (-1);
1100 }
1101
1102 /* update the offsets of the rewritten messages */
1103 for (i = first, j = first; i < ctx->msgcount; i++)
1104 {
1105 if (!ctx->hdrs[i]->deleted)
1106 {
1107 ctx->hdrs[i]->offset = newOffset[i - first].hdr;
1108 ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr;
1109 ctx->hdrs[i]->content->offset = newOffset[i - first].body;
1110 ctx->hdrs[i]->index = j++;
1111 }
1112 }
1113 FREE (&newOffset);
1114 FREE (&oldOffset);
1115 unlink (tempfile); /* remove partial copy of the mailbox */
1116 mutt_unblock_signals ();
1117
1118 if (option(OPTCHECKMBOXSIZE))
1119 {
1120 tmp = mutt_find_mailbox (ctx->path);
1121 if (tmp && tmp->new == 0)
1122 mutt_update_mailbox (tmp);
1123 }
1124
1125 return (0); /* signal success */
1126
1127bail: /* Come here in case of disaster */
1128
1129 safe_fclose (&fp);
1130
1131 /* restore offsets, as far as they are valid */
1132 if (first >= 0 && oldOffset)
1133 {
1134 for (i = first; i < ctx->msgcount && oldOffset[i-first].valid; i++)
1135 {
1136 ctx->hdrs[i]->offset = oldOffset[i-first].hdr;
1137 ctx->hdrs[i]->content->hdr_offset = oldOffset[i-first].hdr;
1138 ctx->hdrs[i]->content->offset = oldOffset[i-first].body;
1139 ctx->hdrs[i]->lines = oldOffset[i-first].lines;
1140 ctx->hdrs[i]->content->length = oldOffset[i-first].length;
1141 }
1142 }
1143
1144 /* this is ok to call even if we haven't locked anything */
1145 mbox_unlock_mailbox (ctx);
1146
1147 mutt_unblock_signals ();
1148 FREE (&newOffset);
1149 FREE (&oldOffset);
1150
1151 if ((ctx->fp = freopen (ctx->path, "r", ctx->fp)) == NULL)
1152 {
1153 mutt_error _("Could not reopen mailbox!");
1154 mx_fastclose_mailbox (ctx);
1155 return (-1);
1156 }
1157
1158 if (need_sort)
1159 /* if the mailbox was reopened, the thread tree will be invalid so make
1160 * sure to start threading from scratch. */
1161 mutt_sort_headers (ctx, (need_sort == MUTT_REOPENED));
1162
1163 return rc;
1164}
1165
1166int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
1167{
1168 int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
1169 HEADER **old_hdrs;
1170 int old_msgcount;
1171 int msg_mod = 0;
1172 int index_hint_set;
1173 int i, j;
1174 int rc = -1;
1175
1176 /* silent operations */
1177 ctx->quiet = 1;
1178
1179 if (!ctx->quiet)
1180 mutt_message _("Reopening mailbox...");
1181
1182 /* our heuristics require the old mailbox to be unsorted */
1183 if (Sort != SORT_ORDER)
1184 {
1185 short old_sort;
1186
1187 old_sort = Sort;
1188 Sort = SORT_ORDER;
1189 mutt_sort_headers (ctx, 1);
1190 Sort = old_sort;
1191 }
1192
1193 old_hdrs = NULL;
1194 old_msgcount = 0;
1195
1196 /* simulate a close */
1197 if (ctx->id_hash)
1198 hash_destroy (&ctx->id_hash, NULL);
1199 if (ctx->subj_hash)
1200 hash_destroy (&ctx->subj_hash, NULL);
1201 mutt_clear_threads (ctx);
1202 FREE (&ctx->v2r);
1203 if (ctx->readonly)
1204 {
1205 for (i = 0; i < ctx->msgcount; i++)
1206 mutt_free_header (&(ctx->hdrs[i])); /* nothing to do! */
1207 FREE (&ctx->hdrs);
1208 }
1209 else
1210 {
1211 /* save the old headers */
1212 old_msgcount = ctx->msgcount;
1213 old_hdrs = ctx->hdrs;
1214 ctx->hdrs = NULL;
1215 }
1216
1217 ctx->hdrmax = 0; /* force allocation of new headers */
1218 ctx->msgcount = 0;
1219 ctx->vcount = 0;
1220 ctx->tagged = 0;
1221 ctx->deleted = 0;
1222 ctx->new = 0;
1223 ctx->unread = 0;
1224 ctx->flagged = 0;
1225 ctx->changed = 0;
1226 ctx->id_hash = NULL;
1227 ctx->subj_hash = NULL;
1228
1229 switch (ctx->magic)
1230 {
1231 case MUTT_MBOX:
1232 case MUTT_MMDF:
1233 cmp_headers = mbox_strict_cmp_headers;
1234 safe_fclose (&ctx->fp);
1235 if (!(ctx->fp = safe_fopen (ctx->path, "r")))
1236 rc = -1;
1237 else
1238 rc = ((ctx->magic == MUTT_MBOX) ? mbox_parse_mailbox
1239 : mmdf_parse_mailbox) (ctx);
1240 break;
1241
1242 default:
1243 rc = -1;
1244 break;
1245 }
1246
1247 if (rc == -1)
1248 {
1249 /* free the old headers */
1250 for (j = 0; j < old_msgcount; j++)
1251 mutt_free_header (&(old_hdrs[j]));
1252 FREE (&old_hdrs);
1253
1254 ctx->quiet = 0;
1255 return (-1);
1256 }
1257
1258 /* now try to recover the old flags */
1259
1260 index_hint_set = (index_hint == NULL);
1261
1262 if (!ctx->readonly)
1263 {
1264 for (i = 0; i < ctx->msgcount; i++)
1265 {
1266 int found = 0;
1267
1268 /* some messages have been deleted, and new messages have been
1269 * appended at the end; the heuristic is that old messages have then
1270 * "advanced" towards the beginning of the folder, so we begin the
1271 * search at index "i"
1272 */
1273 for (j = i; j < old_msgcount; j++)
1274 {
1275 if (old_hdrs[j] == NULL)
1276 continue;
1277 if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
1278 {
1279 found = 1;
1280 break;
1281 }
1282 }
1283 if (!found)
1284 {
1285 for (j = 0; j < i && j < old_msgcount; j++)
1286 {
1287 if (old_hdrs[j] == NULL)
1288 continue;
1289 if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
1290 {
1291 found = 1;
1292 break;
1293 }
1294 }
1295 }
1296
1297 if (found)
1298 {
1299 /* this is best done here */
1300 if (!index_hint_set && *index_hint == j)
1301 *index_hint = i;
1302
1303 if (old_hdrs[j]->changed)
1304 {
1305 /* Only update the flags if the old header was changed;
1306 * otherwise, the header may have been modified externally,
1307 * and we don't want to lose _those_ changes
1308 */
1309 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_FLAG, old_hdrs[j]->flagged);
1310 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_REPLIED, old_hdrs[j]->replied);
1311 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_OLD, old_hdrs[j]->old);
1312 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_READ, old_hdrs[j]->read);
1313 }
1314 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_DELETE, old_hdrs[j]->deleted);
1315 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_PURGE, old_hdrs[j]->purge);
1316 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, old_hdrs[j]->tagged);
1317
1318 /* we don't need this header any more */
1319 mutt_free_header (&(old_hdrs[j]));
1320 }
1321 }
1322
1323 /* free the remaining old headers */
1324 for (j = 0; j < old_msgcount; j++)
1325 {
1326 if (old_hdrs[j])
1327 {
1328 mutt_free_header (&(old_hdrs[j]));
1329 msg_mod = 1;
1330 }
1331 }
1332 FREE (&old_hdrs);
1333 }
1334
1335 ctx->quiet = 0;
1336
1337 return ((ctx->changed || msg_mod) ? MUTT_REOPENED : MUTT_NEW_MAIL);
1338}
1339
1340/*
1341 * Returns:
1342 * 1 if the mailbox is not empty
1343 * 0 if the mailbox is empty
1344 * -1 on error
1345 */
1346int mbox_check_empty (const char *path)
1347{
1348 struct stat st;
1349
1350 if (stat (path, &st) == -1)
1351 return -1;
1352
1353 return ((st.st_size == 0));
1354}
1355
1356struct mx_ops mx_mbox_ops = {
1357 .open = mbox_open_mailbox,
1358 .open_append = mbox_open_mailbox_append,
1359 .close = mbox_close_mailbox,
1360 .open_msg = mbox_open_message,
1361 .close_msg = mbox_close_message,
1362 .commit_msg = mbox_commit_message,
1363 .open_new_msg = mbox_open_new_message,
1364 .check = mbox_check_mailbox,
1365 .sync = mbox_sync_mailbox,
1366};
1367
1368struct mx_ops mx_mmdf_ops = {
1369 .open = mbox_open_mailbox,
1370 .open_append = mbox_open_mailbox_append,
1371 .close = mbox_close_mailbox,
1372 .open_msg = mbox_open_message,
1373 .close_msg = mbox_close_message,
1374 .commit_msg = mmdf_commit_message,
1375 .open_new_msg = mbox_open_new_message,
1376 .check = mbox_check_mailbox,
1377 .sync = mbox_sync_mailbox,
1378};