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