9fd7a40ae614d8290b1bea5864bf13b996149b87
[apps/madmutt.git] / lib-mx / mbox.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 /* This file contains code to parse ``mbox'' style mailboxes */
11
12 #include <lib-lib/lib-lib.h>
13
14 #include <lib-ui/lib-ui.h>
15 #include <lib-sys/mutt_signal.h>
16
17 #include "mutt.h"
18 #include "mx.h"
19 #include "buffy.h"
20 #include "mbox.h"
21 #include "sort.h"
22 #include "thread.h"
23 #include "copy.h"
24 #include "compress.h"
25
26 /* struct used by mutt_sync_mailbox() to store new offsets */
27 struct m_update_t {
28     short valid;
29     off_t hdr;
30     off_t body;
31     long lines;
32     off_t length;
33 };
34
35 static int mbox_open_new_message(MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
36 {
37     msg->fp = dest->fp;
38     return 0;
39 }
40
41 /* prototypes */
42 static int mbox_reopen_mailbox (CONTEXT*, int*);
43
44 /* parameters:
45  * ctx - context to lock
46  * excl - exclusive lock?
47  * retry - should retry if unable to lock?
48  */
49 int mbox_lock_mailbox(CONTEXT *ctx, int excl, int retry)
50 {
51   int r;
52
53   if ((r = mx_lock_file(ctx->path, fileno(ctx->fp), excl, retry)) == 0)
54     ctx->locked = 1;
55   else if (retry && !excl) {
56     ctx->readonly = 1;
57     return 0;
58   }
59
60   return (r);
61 }
62
63 static void mbox_unlock_mailbox (CONTEXT * ctx)
64 {
65   if (ctx->locked) {
66     fflush (ctx->fp);
67
68     mx_unlock_file(ctx->path, fileno(ctx->fp));
69     ctx->locked = 0;
70   }
71 }
72
73 /* Note that this function is also called when new mail is appended to the
74  * currently open folder, and NOT just when the mailbox is initially read.
75  *
76  * NOTE: it is assumed that the mailbox being read has been locked before
77  * this routine gets called.  Strange things could happen if it's not!
78  */
79 static int mbox_parse_mailbox (CONTEXT * ctx)
80 {
81   struct stat sb;
82   char buf[HUGE_STRING], return_path[STRING];
83   HEADER *curhdr;
84   time_t t, tz;
85   int count = 0, lines = 0;
86   off_t loc;
87
88   /* Save information about the folder at the time we opened it. */
89   if (stat (ctx->path, &sb) == -1) {
90     mutt_perror (ctx->path);
91     return (-1);
92   }
93
94   ctx->size = sb.st_size;
95   ctx->mtime = sb.st_mtime;
96
97   if (!ctx->readonly)
98     ctx->readonly = access (ctx->path, W_OK) ? 1 : 0;
99
100   /* precompute the local timezone to speed up calculation of the
101      date received */
102   t = time(NULL);
103   tz = localtime(&t)->tm_gmtoff;
104
105   loc = ftello (ctx->fp);
106   while (fgets (buf, sizeof (buf), ctx->fp) != NULL) {
107     if (is_from (buf, return_path, sizeof (return_path), &t)) {
108       /* Save the Content-Length of the previous message */
109       if (count > 0) {
110 #define PREV ctx->hdrs[ctx->msgcount-1]
111
112         if (PREV->content->length < 0) {
113           PREV->content->length = loc - PREV->content->offset - 1;
114           if (PREV->content->length < 0)
115             PREV->content->length = 0;
116         }
117         if (!PREV->lines)
118           PREV->lines = lines ? lines - 1 : 0;
119       }
120
121       count++;
122
123       if (!ctx->quiet && ReadInc && ((count % ReadInc == 0) || count == 1))
124         mutt_message (_("Reading %s... %d (%d%%)"), ctx->path, count,
125                       (int) (ftello (ctx->fp) / (ctx->size / 100 + 1)));
126
127       if (ctx->msgcount == ctx->hdrmax)
128         mx_alloc_memory (ctx);
129
130       curhdr = ctx->hdrs[ctx->msgcount] = header_new();
131       curhdr->received = t - tz;
132       curhdr->offset = loc;
133       curhdr->index = ctx->msgcount;
134
135       curhdr->env = mutt_read_rfc822_header (ctx->fp, curhdr, 0, 0);
136
137       /* if we know how long this message is, either just skip over the body,
138        * or if we don't know how many lines there are, count them now (this will
139        * save time by not having to search for the next message marker).
140        */
141       if (curhdr->content->length > 0) {
142         off_t tmploc;
143
144         loc = ftello (ctx->fp);
145         tmploc = loc + curhdr->content->length + 1;
146
147         if (0 < tmploc && tmploc < ctx->size) {
148           /*
149            * check to see if the content-length looks valid.  we expect to
150            * to see a valid message separator at this point in the stream
151            */
152           if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
153               fgets (buf, sizeof (buf), ctx->fp) == NULL ||
154               m_strncmp("From ", buf, 5) != 0) {
155             fseeko (ctx->fp, loc, SEEK_SET); /* nope, return the previous position */
156             curhdr->content->length = -1;
157           }
158         }
159         else if (tmploc != ctx->size) {
160           /* content-length would put us past the end of the file, so it
161            * must be wrong
162            */
163           curhdr->content->length = -1;
164         }
165
166         if (curhdr->content->length != -1) {
167           /* good content-length.  check to see if we know how many lines
168            * are in this message.
169            */
170           if (curhdr->lines == 0) {
171             int cl = curhdr->content->length;
172
173             /* count the number of lines in this message */
174             fseeko (ctx->fp, loc, SEEK_SET);
175             while (cl-- > 0) {
176               if (fgetc (ctx->fp) == '\n')
177                 curhdr->lines++;
178             }
179           }
180
181           /* return to the offset of the next message separator */
182           fseeko(ctx->fp, tmploc, SEEK_SET);
183         }
184       }
185
186       ctx->msgcount++;
187
188       if (!curhdr->env->return_path && return_path[0])
189         curhdr->env->return_path =
190           rfc822_parse_adrlist (curhdr->env->return_path, return_path);
191
192       if (!curhdr->env->from)
193         curhdr->env->from = address_list_dup (curhdr->env->return_path);
194
195       lines = 0;
196     }
197     else
198       lines++;
199
200     loc = ftello (ctx->fp);
201   }
202
203   /*
204    * Only set the content-length of the previous message if we have read more
205    * than one message during _this_ invocation.  If this routine is called
206    * when new mail is received, we need to make sure not to clobber what
207    * previously was the last message since the headers may be sorted.
208    */
209   if (count > 0) {
210     if (PREV->content->length < 0) {
211       PREV->content->length = ftello (ctx->fp) - PREV->content->offset - 1;
212       if (PREV->content->length < 0)
213         PREV->content->length = 0;
214     }
215
216     if (!PREV->lines)
217       PREV->lines = lines ? lines - 1 : 0;
218
219     mx_update_context (ctx, count);
220   }
221
222   return (0);
223 }
224
225 #undef PREV
226
227 /* open a mbox style mailbox */
228 static int mbox_open_mailbox (CONTEXT * ctx)
229 {
230     int rc;
231
232     if (!(ctx->fp = fopen(ctx->path, "r"))) {
233         mutt_perror(ctx->path);
234         return -1;
235     }
236
237     mutt_block_signals();
238     if (mbox_lock_mailbox(ctx, 0, 1) < 0) {
239         mutt_unblock_signals();
240         return -1;
241     }
242
243     rc = ctx->magic == M_MBOX ? mbox_parse_mailbox(ctx) : -1;
244
245     mbox_unlock_mailbox(ctx);
246     mutt_unblock_signals();
247     return rc;
248 }
249
250 /* check to see if the mailbox has changed on disk.
251  *
252  * return values:
253  *      M_REOPENED      mailbox has been reopened
254  *      M_NEW_MAIL      new mail has arrived!
255  *      M_LOCKED        couldn't lock the file
256  *      0               no change
257  *      -1              error
258  */
259 static int _mbox_check_mailbox (CONTEXT * ctx, int *index_hint)
260 {
261   struct stat st;
262   char buffer[LONG_STRING];
263   int unlock = 0;
264   int modified = 0;
265
266   if (stat (ctx->path, &st) == 0) {
267     if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
268       return (0);
269
270     if (st.st_size == ctx->size) {
271       /* the file was touched, but it is still the same length, so just exit */
272       ctx->mtime = st.st_mtime;
273       return (0);
274     }
275
276     if (st.st_size > ctx->size) {
277       /* lock the file if it isn't already */
278       if (!ctx->locked) {
279         mutt_block_signals ();
280         if (mbox_lock_mailbox (ctx, 0, 0) == -1) {
281           mutt_unblock_signals ();
282           /* we couldn't lock the mailbox, but nothing serious happened:
283            * probably the new mail arrived: no reason to wait till we can
284            * parse it: we'll get it on the next pass
285            */
286           return (M_LOCKED);
287         }
288         unlock = 1;
289       }
290
291       /*
292        * Check to make sure that the only change to the mailbox is that 
293        * message(s) were appended to this file.  My heuristic is that we should
294        * see the message separator at *exactly* what used to be the end of the
295        * folder.
296        */
297       fseeko (ctx->fp, ctx->size, SEEK_SET);
298       if (fgets (buffer, sizeof (buffer), ctx->fp) != NULL) {
299         if (ctx->magic == M_MBOX && m_strncmp("From ", buffer, 5) == 0) {
300           fseeko (ctx->fp, ctx->size, SEEK_SET);
301           mbox_parse_mailbox (ctx);
302
303           /* Only unlock the folder if it was locked inside of this routine.
304            * It may have been locked elsewhere, like in
305            * mutt_checkpoint_mailbox().
306            */
307
308           if (unlock) {
309             mbox_unlock_mailbox (ctx);
310             mutt_unblock_signals ();
311           }
312
313           return (M_NEW_MAIL);  /* signal that new mail arrived */
314         }
315         else
316           modified = 1;
317       } else {
318         modified = 1;
319       }
320     } else {
321       modified = 1;
322     }
323   }
324
325   if (modified) {
326     if (mbox_reopen_mailbox (ctx, index_hint) != -1) {
327       if (unlock) {
328         mbox_unlock_mailbox (ctx);
329         mutt_unblock_signals ();
330       }
331       return (M_REOPENED);
332     }
333   }
334
335   /* fatal error */
336
337   mbox_unlock_mailbox (ctx);
338   mx_fastclose_mailbox (ctx);
339   mutt_unblock_signals ();
340   mutt_error _("Mailbox was corrupted!");
341
342   return (-1);
343 }
344
345 static int mbox_check_mailbox(CONTEXT *ctx, int *index_hint, int lock)
346 {
347     int rc = 0;
348
349     if (lock) {
350         mutt_block_signals();
351         if (mbox_lock_mailbox(ctx, 0, 0) < 0) {
352             mutt_unblock_signals();
353             return M_LOCKED;
354         }
355     }
356
357     rc = _mbox_check_mailbox(ctx, index_hint);
358
359     if (lock) {
360         mutt_unblock_signals ();
361         mbox_unlock_mailbox (ctx);
362     }
363     return rc;
364 }
365
366 /* return values:
367  *      0       success
368  *      -1      failure
369  */
370 static int mbox_sync_mailbox (CONTEXT * ctx, int unused __attribute__ ((unused)), int *index_hint)
371 {
372   char tempfile[_POSIX_PATH_MAX];
373   char buf[32];
374   int i, j, save_sort = SORT_ORDER;
375   int rc = -1;
376   int need_sort = 0;            /* flag to resort mailbox if new mail arrives */
377   int first = -1;               /* first message to be written */
378   off_t offset;                /* location in mailbox to write changed messages */
379   struct stat statbuf;
380   struct utimbuf utimebuf;
381   struct m_update_t *newOffset = NULL;
382   struct m_update_t *oldOffset = NULL;
383   FILE *fp = NULL;
384
385   /* sort message by their position in the mailbox on disk */
386   if (Sort != SORT_ORDER) {
387     save_sort = Sort;
388     Sort = SORT_ORDER;
389     mutt_sort_headers (ctx, 0);
390     Sort = save_sort;
391     need_sort = 1;
392   }
393
394   /* need to open the file for writing in such a way that it does not truncate
395    * the file, so use read-write mode.
396    */
397   if ((ctx->fp = freopen (ctx->path, "r+", ctx->fp)) == NULL) {
398     mx_fastclose_mailbox (ctx);
399     mutt_error _("Fatal error!  Could not reopen mailbox!");
400
401     return (-1);
402   }
403
404   mutt_block_signals ();
405
406   if (mbox_lock_mailbox (ctx, 1, 1) == -1) {
407     mutt_unblock_signals ();
408     mutt_error _("Unable to lock mailbox!");
409
410     goto bail;
411   }
412
413   /* Check to make sure that the file hasn't changed on disk */
414   if ((i = _mbox_check_mailbox (ctx, index_hint)) == M_NEW_MAIL
415       || i == M_REOPENED) {
416     /* new mail arrived, or mailbox reopened */
417     need_sort = i;
418     rc = i;
419     goto bail;
420   }
421   else if (i < 0)
422     /* fatal error */
423     return (-1);
424
425   /* Create a temporary file to write the new version of the mailbox in. */
426   fp = m_tempfile(tempfile, _POSIX_PATH_MAX, NONULL(mod_core.tmpdir), NULL);
427   if (fp == NULL) {
428     mutt_error _("Could not create temporary file!");
429     mutt_sleep (5);
430     goto bail;
431   }
432
433   /* find the first deleted/changed message.  we save a lot of time by only
434    * rewriting the mailbox from the point where it has actually changed.
435    */
436   for (i = 0; i < ctx->msgcount && !ctx->hdrs[i]->deleted &&
437        !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++);
438   if (i == ctx->msgcount) {
439     /* this means ctx->changed or ctx->deleted was set, but no
440      * messages were found to be changed or deleted.  This should
441      * never happen, is we presume it is a bug in mutt.
442      */
443     mutt_error
444       _("sync: mbox modified, but no modified messages! (report this bug)");
445     mutt_sleep (5);             /* the mutt_error /will/ get cleared! */
446     unlink (tempfile);
447     goto bail;
448   }
449
450   /* save the index of the first changed/deleted message */
451   first = i;
452   /* where to start overwriting */
453   offset = ctx->hdrs[i]->offset;
454
455   /* allocate space for the new offsets */
456   newOffset = p_new(struct m_update_t, ctx->msgcount - first);
457   oldOffset = p_new(struct m_update_t, ctx->msgcount - first);
458
459   for (i = first, j = 0; i < ctx->msgcount; i++) {
460     /*
461      * back up some information which is needed to restore offsets when
462      * something fails.
463      */
464
465     oldOffset[i - first].valid = 1;
466     oldOffset[i - first].hdr = ctx->hdrs[i]->offset;
467     oldOffset[i - first].body = ctx->hdrs[i]->content->offset;
468     oldOffset[i - first].lines = ctx->hdrs[i]->lines;
469     oldOffset[i - first].length = ctx->hdrs[i]->content->length;
470
471     if (!ctx->hdrs[i]->deleted) {
472       j++;
473       if (!ctx->quiet && WriteInc && ((i % WriteInc) == 0 || j == 1))
474         mutt_message (_("Writing messages... %d (%d%%)"), i,
475                       (int) (ftello (ctx->fp) / (ctx->size / 100 + 1)));
476
477       /* save the new offset for this message.  we add `offset' because the
478        * temporary file only contains saved message which are located after
479        * `offset' in the real mailbox
480        */
481       newOffset[i - first].hdr = ftello (fp) + offset;
482
483       if (mutt_copy_message
484           (fp, ctx, ctx->hdrs[i], M_CM_UPDATE,
485            CH_FROM | CH_UPDATE | CH_UPDATE_LEN) == -1) {
486         mutt_perror (_("Can't create temporary file"));
487         mutt_sleep (5);
488         unlink (tempfile);
489         goto bail;
490       }
491
492       /* Since messages could have been deleted, the offsets stored in memory
493        * will be wrong, so update what we can, which is the offset of this
494        * message, and the offset of the body.  If this is a multipart message,
495        * we just flush the in memory cache so that the message will be reparsed
496        * if the user accesses it later.
497        */
498       newOffset[i - first].body =
499         ftello (fp) - ctx->hdrs[i]->content->length + offset;
500       body_list_wipe(&ctx->hdrs[i]->content->parts);
501
502       if (fputs ("\n", fp) == EOF) {
503         mutt_perror (_("Can't create temporary file"));
504         mutt_sleep (5);
505         unlink (tempfile);
506         goto bail;
507       }
508     }
509   }
510
511   if (m_fclose(&fp) != 0) {
512     unlink (tempfile);
513     mutt_perror (_("Can't create temporary file"));
514     mutt_sleep (5);
515     goto bail;
516   }
517
518   /* Save the state of this folder. */
519   if (stat (ctx->path, &statbuf) == -1) {
520     mutt_perror (ctx->path);
521     mutt_sleep (5);
522     unlink (tempfile);
523     goto bail;
524   }
525
526   if ((fp = fopen (tempfile, "r")) == NULL) {
527     mutt_unblock_signals ();
528     mx_fastclose_mailbox (ctx);
529     mutt_perror (_("Can't create temporary file"));
530     mutt_sleep (5);
531     return (-1);
532   }
533
534   if (fseeko (ctx->fp, offset, SEEK_SET) != 0 /* seek the append location */
535       /* do a sanity check to make sure the mailbox looks ok */
536   ||  fgets (buf, sizeof (buf), ctx->fp) == NULL
537   ||  (ctx->magic == M_MBOX && m_strncmp("From ", buf, 5) != 0))
538   {
539     i = -1;
540   }
541   else {
542     if (fseeko (ctx->fp, offset, SEEK_SET) != 0) {       /* return to proper offset */
543       i = -1;
544     } else {
545       /* copy the temp mailbox back into place starting at the first
546        * change/deleted message
547        */
548       mutt_message _("Committing changes...");
549
550       i = mutt_copy_stream (fp, ctx->fp);
551
552       if (ferror (ctx->fp))
553         i = -1;
554     }
555     if (i == 0) {
556       ctx->size = ftello (ctx->fp);      /* update the size of the mailbox */
557       ftruncate (fileno (ctx->fp), ctx->size);
558     }
559   }
560
561   m_fclose(&fp);
562   mbox_unlock_mailbox (ctx);
563
564   if (m_fclose(&ctx->fp) != 0 || i == -1) {
565     /* error occured while writing the mailbox back, so keep the temp copy
566      * around
567      */
568
569     char savefile[_POSIX_PATH_MAX];
570
571     snprintf(savefile, sizeof (savefile), "%s/mutt.%s-%u",
572              NONULL(mod_core.tmpdir), NONULL(mod_core.username), (unsigned int)getpid());
573     rename (tempfile, savefile);
574     mutt_unblock_signals ();
575     mx_fastclose_mailbox (ctx);
576     mutt_pretty_mailbox (savefile);
577     mutt_error (_("Write failed!  Saved partial mailbox to %s"), savefile);
578     mutt_sleep (5);
579     return (-1);
580   }
581
582   /* Restore the previous access/modification times */
583   utimebuf.actime = statbuf.st_atime;
584   utimebuf.modtime = statbuf.st_mtime;
585   utime (ctx->path, &utimebuf);
586
587   /* reopen the mailbox in read-only mode */
588   if ((ctx->fp = fopen (ctx->path, "r")) == NULL) {
589     unlink (tempfile);
590     mutt_unblock_signals ();
591     mx_fastclose_mailbox (ctx);
592     mutt_error _("Fatal error!  Could not reopen mailbox!");
593     return (-1);
594   }
595
596   /* update the offsets of the rewritten messages */
597   for (i = first, j = first; i < ctx->msgcount; i++) {
598     if (!ctx->hdrs[i]->deleted) {
599       ctx->hdrs[i]->offset = newOffset[i - first].hdr;
600       ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr;
601       ctx->hdrs[i]->content->offset = newOffset[i - first].body;
602       ctx->hdrs[i]->index = j++;
603     }
604   }
605   p_delete(&newOffset);
606   p_delete(&oldOffset);
607   unlink (tempfile);            /* remove partial copy of the mailbox */
608   mutt_unblock_signals ();
609
610   return (0);                   /* signal success */
611
612 bail:                          /* Come here in case of disaster */
613
614   m_fclose(&fp);
615
616   /* restore offsets, as far as they are valid */
617   if (first >= 0 && oldOffset) {
618     for (i = first; i < ctx->msgcount && oldOffset[i - first].valid; i++) {
619       ctx->hdrs[i]->offset = oldOffset[i - first].hdr;
620       ctx->hdrs[i]->content->hdr_offset = oldOffset[i - first].hdr;
621       ctx->hdrs[i]->content->offset = oldOffset[i - first].body;
622       ctx->hdrs[i]->lines = oldOffset[i - first].lines;
623       ctx->hdrs[i]->content->length = oldOffset[i - first].length;
624     }
625   }
626
627   /* this is ok to call even if we haven't locked anything */
628   mbox_unlock_mailbox (ctx);
629
630   mutt_unblock_signals ();
631   p_delete(&newOffset);
632   p_delete(&oldOffset);
633
634   if ((ctx->fp = freopen (ctx->path, "r", ctx->fp)) == NULL) {
635     mutt_error _("Could not reopen mailbox!");
636
637     mx_fastclose_mailbox (ctx);
638     return (-1);
639   }
640
641   if (need_sort)
642     /* if the mailbox was reopened, the thread tree will be invalid so make
643      * sure to start threading from scratch.  */
644     mutt_sort_headers (ctx, (need_sort == M_REOPENED));
645
646   return rc;
647 }
648
649 /* close a mailbox opened in write-mode */
650 int mbox_close_mailbox (CONTEXT * ctx)
651 {
652   mx_unlock_file(ctx->path, fileno(ctx->fp));
653
654   if (ctx->cinfo)
655     mutt_slow_close_compressed (ctx);
656
657   mutt_unblock_signals ();
658   mx_fastclose_mailbox (ctx);
659   return 0;
660 }
661
662 static int mbox_reopen_mailbox (CONTEXT * ctx, int *index_hint)
663 {
664   int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
665   HEADER **old_hdrs;
666   int old_msgcount;
667   int msg_mod = 0;
668   int index_hint_set;
669   int i, j;
670   int rc = -1;
671
672   /* silent operations */
673   ctx->quiet = 1;
674
675   mutt_message _("Reopening mailbox...");
676
677   /* our heuristics require the old mailbox to be unsorted */
678   if (Sort != SORT_ORDER) {
679     short old_sort;
680
681     old_sort = Sort;
682     Sort = SORT_ORDER;
683     mutt_sort_headers (ctx, 1);
684     Sort = old_sort;
685   }
686
687   old_hdrs = NULL;
688   old_msgcount = 0;
689
690   /* simulate a close */
691   if (ctx->id_hash)
692     hash_delete (&ctx->id_hash, NULL);
693   if (ctx->subj_hash)
694     hash_delete (&ctx->subj_hash, NULL);
695   mutt_clear_threads (ctx);
696   p_delete(&ctx->v2r);
697   if (ctx->readonly) {
698     for (i = 0; i < ctx->msgcount; i++)
699       header_delete(&(ctx->hdrs[i]));       /* nothing to do! */
700     p_delete(&ctx->hdrs);
701   }
702   else {
703     /* save the old headers */
704     old_msgcount = ctx->msgcount;
705     old_hdrs = ctx->hdrs;
706     ctx->hdrs = NULL;
707   }
708
709   ctx->hdrmax = 0;              /* force allocation of new headers */
710   ctx->msgcount = 0;
711   ctx->vcount = 0;
712   ctx->tagged = 0;
713   ctx->deleted = 0;
714   ctx->new = 0;
715   ctx->unread = 0;
716   ctx->flagged = 0;
717   ctx->changed = 0;
718   ctx->id_hash = NULL;
719   ctx->subj_hash = NULL;
720
721   if (ctx->magic == M_MBOX && !fseeko(ctx->fp, 0, SEEK_SET)) {
722     cmp_headers = mutt_cmp_header;
723     rc = mbox_parse_mailbox (ctx);
724   } else {
725     rc = -1;
726   }
727
728   if (rc == -1) {
729     /* free the old headers */
730     for (j = 0; j < old_msgcount; j++)
731       header_delete(&(old_hdrs[j]));
732     p_delete(&old_hdrs);
733
734     ctx->quiet = 0;
735     return (-1);
736   }
737
738   /* now try to recover the old flags */
739
740   index_hint_set = (index_hint == NULL);
741
742   if (!ctx->readonly) {
743     for (i = 0; i < ctx->msgcount; i++) {
744       int found = 0;
745
746       /* some messages have been deleted, and new  messages have been
747        * appended at the end; the heuristic is that old messages have then
748        * "advanced" towards the beginning of the folder, so we begin the
749        * search at index "i"
750        */
751       for (j = i; j < old_msgcount; j++) {
752         if (old_hdrs[j] == NULL)
753           continue;
754         if (cmp_headers (ctx->hdrs[i], old_hdrs[j])) {
755           found = 1;
756           break;
757         }
758       }
759       if (!found) {
760         for (j = 0; j < i && j < old_msgcount; j++) {
761           if (old_hdrs[j] == NULL)
762             continue;
763           if (cmp_headers (ctx->hdrs[i], old_hdrs[j])) {
764             found = 1;
765             break;
766           }
767         }
768       }
769
770       if (found) {
771         /* this is best done here */
772         if (!index_hint_set && *index_hint == j)
773           *index_hint = i;
774
775         if (old_hdrs[j]->changed) {
776           /* Only update the flags if the old header was changed;
777            * otherwise, the header may have been modified externally,
778            * and we don't want to lose _those_ changes
779            */
780           mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, old_hdrs[j]->flagged);
781           mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, old_hdrs[j]->replied);
782           mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, old_hdrs[j]->old);
783           mutt_set_flag (ctx, ctx->hdrs[i], M_READ, old_hdrs[j]->read);
784         }
785         mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, old_hdrs[j]->deleted);
786         mutt_set_flag (ctx, ctx->hdrs[i], M_TAG, old_hdrs[j]->tagged);
787
788         /* we don't need this header any more */
789         header_delete(&(old_hdrs[j]));
790       }
791     }
792
793     /* free the remaining old headers */
794     for (j = 0; j < old_msgcount; j++) {
795       if (old_hdrs[j]) {
796         header_delete(&(old_hdrs[j]));
797         msg_mod = 1;
798       }
799     }
800     p_delete(&old_hdrs);
801   }
802
803   ctx->quiet = 0;
804
805   return ((ctx->changed || msg_mod) ? M_REOPENED : M_NEW_MAIL);
806 }
807
808 /*
809  * Returns:
810  * 1 if the mailbox is not empty
811  * 0 if the mailbox is empty
812  * -1 on error
813  */
814 int mbox_check_empty (const char *path)
815 {
816   struct stat st;
817
818   if (stat (path, &st) == -1)
819     return -1;
820
821   return ((st.st_size == 0));
822 }
823
824 int mbox_is_magic (const char* path, struct stat* st)
825 {
826   int magic = -1;
827   FILE* f;
828   char tmp[_POSIX_PATH_MAX];
829
830   if (S_ISDIR(st->st_mode))
831     return (-1);
832
833   if (st->st_size == 0) {
834     return M_MBOX;
835   }
836   else if ((f = fopen (path, "r")) != NULL) {
837     struct utimbuf times;
838
839     fgets (tmp, sizeof (tmp), f);
840     if (m_strncmp("From ", tmp, 5) == 0)
841       magic = M_MBOX;
842     m_fclose(&f);
843
844     /* need to restore the times here, the file was not really accessed,
845      * only the type was accessed.  This is important, because detection
846      * of "new mail" depends on those times set correctly.
847      */
848     times.actime = st->st_atime;
849     times.modtime = st->st_mtime;
850     utime (path, &times);
851   } else {
852     mutt_perror (path);
853     return (-1);         /* fopen failed */
854   }
855
856   if (magic == -1 && mutt_can_read_compressed (path))
857     return (M_COMPRESSED);
858   return (magic);
859 }
860
861 static int mbox_commit_message (MESSAGE* msg, CONTEXT* ctx) {
862   if (fputc ('\n', msg->fp) == EOF)
863     return -1;
864   if ((fflush (msg->fp) == EOF || fsync (fileno (msg->fp)) == -1)) {
865     mutt_perror (_("Can't write message"));
866     return -1;
867   }
868   return 0;
869 }
870
871 mx_t const mbox_mx = {
872     M_MBOX,
873     1,
874     mbox_is_magic,
875     mbox_check_empty,
876     access,
877     mbox_open_mailbox,
878     mbox_open_new_message,
879     NULL,
880     mbox_check_mailbox,
881     NULL,
882     mbox_sync_mailbox,
883     mbox_commit_message,
884 };