less warnings
[apps/madmutt.git] / 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'' and ``mmdf'' style mailboxes */
11
12 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include "mutt.h"
17 #include "mx.h"
18 #include "buffy.h"
19 #include "mbox.h"
20 #include "sort.h"
21 #include "thread.h"
22 #include "copy.h"
23
24 #ifdef USE_COMPRESSED
25 #include "compress.h"
26 #endif
27
28 #include "lib/mem.h"
29 #include "lib/intl.h"
30 #include "lib/str.h"
31 #include "lib/debug.h"
32
33 #include <sys/stat.h>
34 #include <dirent.h>
35 #include <string.h>
36 #include <utime.h>
37 #include <sys/file.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 /* struct used by mutt_sync_mailbox() to store new offsets */
43 struct m_update_t {
44   short valid;
45   off_t hdr;
46   off_t body;
47   long lines;
48   off_t length;
49 };
50
51
52 static int mbox_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
53 {
54   msg->fp = dest->fp;
55   return 0;
56 }
57
58 /* prototypes */
59 static int mbox_reopen_mailbox (CONTEXT*, int*);
60
61 /* parameters:
62  * ctx - context to lock
63  * excl - exclusive lock?
64  * retry - should retry if unable to lock?
65  */
66 int mbox_lock_mailbox (CONTEXT * ctx, int excl, int retry)
67 {
68   int r;
69
70   if ((r = mx_lock_file (ctx->path, fileno (ctx->fp), excl, 1, retry)) == 0)
71     ctx->locked = 1;
72   else if (retry && !excl) {
73     ctx->readonly = 1;
74     return 0;
75   }
76
77   return (r);
78 }
79
80 void mbox_unlock_mailbox (CONTEXT * ctx)
81 {
82   if (ctx->locked) {
83     fflush (ctx->fp);
84
85     mx_unlock_file (ctx->path, fileno (ctx->fp), 1);
86     ctx->locked = 0;
87   }
88 }
89
90 static int mmdf_parse_mailbox (CONTEXT * ctx)
91 {
92   char buf[HUGE_STRING];
93   char return_path[LONG_STRING];
94   int count = 0, oldmsgcount = ctx->msgcount;
95   int lines;
96   time_t t, tz;
97   off_t loc, tmploc;
98   HEADER *hdr;
99   struct stat sb;
100
101 #ifdef NFS_ATTRIBUTE_HACK
102   struct utimbuf newtime;
103 #endif
104
105   if (stat (ctx->path, &sb) == -1) {
106     mutt_perror (ctx->path);
107     return (-1);
108   }
109   ctx->mtime = sb.st_mtime;
110   ctx->size = sb.st_size;
111
112 #ifdef NFS_ATTRIBUTE_HACK
113   if (sb.st_mtime > sb.st_atime) {
114     newtime.modtime = sb.st_mtime;
115     newtime.actime = time (NULL);
116     utime (ctx->path, &newtime);
117   }
118 #endif
119
120   /* precompute the local timezone to speed up calculation of the
121      received time */
122   tz = mutt_local_tz (0);
123
124   buf[sizeof (buf) - 1] = 0;
125
126   FOREVER {
127     if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
128       break;
129
130     if (str_cmp (buf, MMDF_SEP) == 0) {
131       loc = ftello (ctx->fp);
132
133       count++;
134       if (!ctx->quiet && ReadInc && ((count % ReadInc == 0) || count == 1))
135         mutt_message (_("Reading %s... %d (%d%%)"), ctx->path, count,
136                       (int) (loc / (ctx->size / 100 + 1)));
137
138
139       if (ctx->msgcount == ctx->hdrmax)
140         mx_alloc_memory (ctx);
141       ctx->hdrs[ctx->msgcount] = hdr = mutt_new_header ();
142       hdr->offset = loc;
143       hdr->index = ctx->msgcount;
144
145       if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL) {
146         /* TODO: memory leak??? */
147         debug_print (1, ("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         if (fseeko (ctx->fp, loc, SEEK_SET) != 0) {
155           debug_print (1, ("fseeko() failed\n"));
156           mutt_error _("Mailbox is corrupt!");
157
158           return (-1);
159         }
160       }
161       else
162         hdr->received = t - tz;
163
164       hdr->env = mutt_read_rfc822_header (ctx->fp, hdr, 0, 0);
165
166       loc = ftello (ctx->fp);
167
168       if (hdr->content->length > 0 && hdr->lines > 0) {
169         tmploc = loc + hdr->content->length;
170
171         if (0 < tmploc && tmploc < ctx->size) {
172           if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
173               fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL ||
174               str_cmp (MMDF_SEP, buf) != 0) {
175             if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
176               debug_print (1, ("fseeko() failed\n"));
177             hdr->content->length = -1;
178           }
179         }
180         else
181           hdr->content->length = -1;
182       }
183       else
184         hdr->content->length = -1;
185
186       if (hdr->content->length < 0) {
187         lines = -1;
188         do {
189           loc = ftello (ctx->fp);
190           if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
191             break;
192           lines++;
193         } while (str_cmp (buf, MMDF_SEP) != 0);
194
195         hdr->lines = lines;
196         hdr->content->length = loc - hdr->content->offset;
197       }
198
199       if (!hdr->env->return_path && return_path[0])
200         hdr->env->return_path =
201           rfc822_parse_adrlist (hdr->env->return_path, return_path);
202
203       if (!hdr->env->from)
204         hdr->env->from = rfc822_cpy_adr (hdr->env->return_path);
205
206       ctx->msgcount++;
207     }
208     else {
209       debug_print (1, ("corrupt mailbox!\n"));
210       mutt_error _("Mailbox is corrupt!");
211
212       return (-1);
213     }
214   }
215
216   if (ctx->msgcount > oldmsgcount)
217     mx_update_context (ctx, ctx->msgcount - oldmsgcount);
218
219   return (0);
220 }
221
222 /* Note that this function is also called when new mail is appended to the
223  * currently open folder, and NOT just when the mailbox is initially read.
224  *
225  * NOTE: it is assumed that the mailbox being read has been locked before
226  * this routine gets called.  Strange things could happen if it's not!
227  */
228 static int mbox_parse_mailbox (CONTEXT * ctx)
229 {
230   struct stat sb;
231   char buf[HUGE_STRING], return_path[STRING];
232   HEADER *curhdr;
233   time_t t, tz;
234   int count = 0, lines = 0;
235   off_t loc;
236
237 #ifdef NFS_ATTRIBUTE_HACK
238   struct utimbuf newtime;
239 #endif
240
241   /* Save information about the folder at the time we opened it. */
242   if (stat (ctx->path, &sb) == -1) {
243     mutt_perror (ctx->path);
244     return (-1);
245   }
246
247   ctx->size = sb.st_size;
248   ctx->mtime = sb.st_mtime;
249
250 #ifdef NFS_ATTRIBUTE_HACK
251   if (sb.st_mtime > sb.st_atime) {
252     newtime.modtime = sb.st_mtime;
253     newtime.actime = time (NULL);
254     utime (ctx->path, &newtime);
255   }
256 #endif
257
258   if (!ctx->readonly)
259     ctx->readonly = access (ctx->path, W_OK) ? 1 : 0;
260
261   /* precompute the local timezone to speed up calculation of the
262      date received */
263   tz = mutt_local_tz (0);
264
265   loc = ftello (ctx->fp);
266   while (fgets (buf, sizeof (buf), ctx->fp) != NULL) {
267     if (is_from (buf, return_path, sizeof (return_path), &t)) {
268       /* Save the Content-Length of the previous message */
269       if (count > 0) {
270 #define PREV ctx->hdrs[ctx->msgcount-1]
271
272         if (PREV->content->length < 0) {
273           PREV->content->length = loc - PREV->content->offset - 1;
274           if (PREV->content->length < 0)
275             PREV->content->length = 0;
276         }
277         if (!PREV->lines)
278           PREV->lines = lines ? lines - 1 : 0;
279       }
280
281       count++;
282
283       if (!ctx->quiet && ReadInc && ((count % ReadInc == 0) || count == 1))
284         mutt_message (_("Reading %s... %d (%d%%)"), ctx->path, count,
285                       (int) (ftello (ctx->fp) / (ctx->size / 100 + 1)));
286
287       if (ctx->msgcount == ctx->hdrmax)
288         mx_alloc_memory (ctx);
289
290       curhdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
291       curhdr->received = t - tz;
292       curhdr->offset = loc;
293       curhdr->index = ctx->msgcount;
294
295       curhdr->env = mutt_read_rfc822_header (ctx->fp, curhdr, 0, 0);
296
297       /* if we know how long this message is, either just skip over the body,
298        * or if we don't know how many lines there are, count them now (this will
299        * save time by not having to search for the next message marker).
300        */
301       if (curhdr->content->length > 0) {
302         off_t tmploc;
303
304         loc = ftello (ctx->fp);
305         tmploc = loc + curhdr->content->length + 1;
306
307         if (0 < tmploc && tmploc < ctx->size) {
308           /*
309            * check to see if the content-length looks valid.  we expect to
310            * to see a valid message separator at this point in the stream
311            */
312           if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
313               fgets (buf, sizeof (buf), ctx->fp) == NULL ||
314               str_ncmp ("From ", buf, 5) != 0) {
315             debug_print (1, ("bad content-length in message %d (cl=%zd)\n",
316                              curhdr->index, curhdr->content->length));
317             debug_print (1, ("LINE: %s\n", buf));
318             if (fseeko (ctx->fp, loc, SEEK_SET) != 0) {  /* nope, return the previous position */
319               debug_print (1, ("fseeko() failed\n"));
320             }
321             curhdr->content->length = -1;
322           }
323         }
324         else if (tmploc != ctx->size) {
325           /* content-length would put us past the end of the file, so it
326            * must be wrong
327            */
328           curhdr->content->length = -1;
329         }
330
331         if (curhdr->content->length != -1) {
332           /* good content-length.  check to see if we know how many lines
333            * are in this message.
334            */
335           if (curhdr->lines == 0) {
336             int cl = curhdr->content->length;
337
338             /* count the number of lines in this message */
339             if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
340               debug_print (1, ("fseeko() failed\n"));
341             while (cl-- > 0) {
342               if (fgetc (ctx->fp) == '\n')
343                 curhdr->lines++;
344             }
345           }
346
347           /* return to the offset of the next message separator */
348           if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0)
349             debug_print (1, ("fseeko() failed\n"));
350         }
351       }
352
353       ctx->msgcount++;
354
355       if (!curhdr->env->return_path && return_path[0])
356         curhdr->env->return_path =
357           rfc822_parse_adrlist (curhdr->env->return_path, return_path);
358
359       if (!curhdr->env->from)
360         curhdr->env->from = rfc822_cpy_adr (curhdr->env->return_path);
361
362       lines = 0;
363     }
364     else
365       lines++;
366
367     loc = ftello (ctx->fp);
368   }
369
370   /*
371    * Only set the content-length of the previous message if we have read more
372    * than one message during _this_ invocation.  If this routine is called
373    * when new mail is received, we need to make sure not to clobber what
374    * previously was the last message since the headers may be sorted.
375    */
376   if (count > 0) {
377     if (PREV->content->length < 0) {
378       PREV->content->length = ftello (ctx->fp) - PREV->content->offset - 1;
379       if (PREV->content->length < 0)
380         PREV->content->length = 0;
381     }
382
383     if (!PREV->lines)
384       PREV->lines = lines ? lines - 1 : 0;
385
386     mx_update_context (ctx, count);
387   }
388
389   return (0);
390 }
391
392 #undef PREV
393
394 /* open a mbox or mmdf style mailbox */
395 static int mbox_open_mailbox (CONTEXT * ctx)
396 {
397   int rc;
398
399   if ((ctx->fp = fopen (ctx->path, "r")) == NULL) {
400     mutt_perror (ctx->path);
401     return (-1);
402   }
403   mutt_block_signals ();
404   if (mbox_lock_mailbox (ctx, 0, 1) == -1) {
405     mutt_unblock_signals ();
406     return (-1);
407   }
408
409   if (ctx->magic == M_MBOX)
410     rc = mbox_parse_mailbox (ctx);
411   else if (ctx->magic == M_MMDF)
412     rc = mmdf_parse_mailbox (ctx);
413   else
414     rc = -1;
415
416   mbox_unlock_mailbox (ctx);
417   mutt_unblock_signals ();
418   return (rc);
419 }
420
421 /* check to see if the mailbox has changed on disk.
422  *
423  * return values:
424  *      M_REOPENED      mailbox has been reopened
425  *      M_NEW_MAIL      new mail has arrived!
426  *      M_LOCKED        couldn't lock the file
427  *      0               no change
428  *      -1              error
429  */
430 static int _mbox_check_mailbox (CONTEXT * ctx, int *index_hint)
431 {
432   struct stat st;
433   char buffer[LONG_STRING];
434   int unlock = 0;
435   int modified = 0;
436
437   if (stat (ctx->path, &st) == 0) {
438     if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
439       return (0);
440
441     if (st.st_size == ctx->size) {
442       /* the file was touched, but it is still the same length, so just exit */
443       ctx->mtime = st.st_mtime;
444       return (0);
445     }
446
447     if (st.st_size > ctx->size) {
448       /* lock the file if it isn't already */
449       if (!ctx->locked) {
450         mutt_block_signals ();
451         if (mbox_lock_mailbox (ctx, 0, 0) == -1) {
452           mutt_unblock_signals ();
453           /* we couldn't lock the mailbox, but nothing serious happened:
454            * probably the new mail arrived: no reason to wait till we can
455            * parse it: we'll get it on the next pass
456            */
457           return (M_LOCKED);
458         }
459         unlock = 1;
460       }
461
462       /*
463        * Check to make sure that the only change to the mailbox is that 
464        * message(s) were appended to this file.  My heuristic is that we should
465        * see the message separator at *exactly* what used to be the end of the
466        * folder.
467        */
468       if (fseeko (ctx->fp, ctx->size, SEEK_SET) != 0)
469         debug_print (1, ("fseeko() failed\n"));
470       if (fgets (buffer, sizeof (buffer), ctx->fp) != NULL) {
471         if ((ctx->magic == M_MBOX && str_ncmp ("From ", buffer, 5) == 0)
472             || (ctx->magic == M_MMDF && str_cmp (MMDF_SEP, buffer) == 0)) {
473           if (fseeko (ctx->fp, ctx->size, SEEK_SET) != 0)
474             debug_print (1, ("fseeko() failed\n"));
475           if (ctx->magic == M_MBOX)
476             mbox_parse_mailbox (ctx);
477           else
478             mmdf_parse_mailbox (ctx);
479
480           /* Only unlock the folder if it was locked inside of this routine.
481            * It may have been locked elsewhere, like in
482            * mutt_checkpoint_mailbox().
483            */
484
485           if (unlock) {
486             mbox_unlock_mailbox (ctx);
487             mutt_unblock_signals ();
488           }
489
490           return (M_NEW_MAIL);  /* signal that new mail arrived */
491         }
492         else
493           modified = 1;
494       }
495       else {
496         debug_print (1, ("fgets returned NULL.\n"));
497         modified = 1;
498       }
499     }
500     else
501       modified = 1;
502   }
503
504   if (modified) {
505     if (mbox_reopen_mailbox (ctx, index_hint) != -1) {
506       if (unlock) {
507         mbox_unlock_mailbox (ctx);
508         mutt_unblock_signals ();
509       }
510       return (M_REOPENED);
511     }
512   }
513
514   /* fatal error */
515
516   mbox_unlock_mailbox (ctx);
517   mx_fastclose_mailbox (ctx);
518   mutt_unblock_signals ();
519   mutt_error _("Mailbox was corrupted!");
520
521   return (-1);
522 }
523
524 static int mbox_check_mailbox (CONTEXT* ctx, int* index_hint, int lock) {
525   int rc = 0;
526
527   if (lock) {
528     mutt_block_signals ();
529     if (mbox_lock_mailbox (ctx, 0, 0) == -1) {
530       mutt_unblock_signals ();
531       return M_LOCKED;
532     }
533   }
534
535   rc = _mbox_check_mailbox (ctx, index_hint);
536
537   if (lock) {
538     mutt_unblock_signals ();
539     mbox_unlock_mailbox (ctx);
540   }
541   return rc;
542 }
543
544 /* return values:
545  *      0       success
546  *      -1      failure
547  */
548 static int _mbox_sync_mailbox (CONTEXT * ctx, int unused, int *index_hint)
549 {
550   char tempfile[_POSIX_PATH_MAX];
551   char buf[32];
552   int i, j, save_sort = SORT_ORDER;
553   int rc = -1;
554   int need_sort = 0;            /* flag to resort mailbox if new mail arrives */
555   int first = -1;               /* first message to be written */
556   off_t offset;                /* location in mailbox to write changed messages */
557   struct stat statbuf;
558   struct utimbuf utimebuf;
559   struct m_update_t *newOffset = NULL;
560   struct m_update_t *oldOffset = NULL;
561   FILE *fp = NULL;
562
563   /* sort message by their position in the mailbox on disk */
564   if (Sort != SORT_ORDER) {
565     save_sort = Sort;
566     Sort = SORT_ORDER;
567     mutt_sort_headers (ctx, 0);
568     Sort = save_sort;
569     need_sort = 1;
570   }
571
572   /* need to open the file for writing in such a way that it does not truncate
573    * the file, so use read-write mode.
574    */
575   if ((ctx->fp = freopen (ctx->path, "r+", ctx->fp)) == NULL) {
576     mx_fastclose_mailbox (ctx);
577     mutt_error _("Fatal error!  Could not reopen mailbox!");
578
579     return (-1);
580   }
581
582   mutt_block_signals ();
583
584   if (mbox_lock_mailbox (ctx, 1, 1) == -1) {
585     mutt_unblock_signals ();
586     mutt_error _("Unable to lock mailbox!");
587
588     goto bail;
589   }
590
591   /* Check to make sure that the file hasn't changed on disk */
592   if ((i = _mbox_check_mailbox (ctx, index_hint)) == M_NEW_MAIL
593       || i == M_REOPENED) {
594     /* new mail arrived, or mailbox reopened */
595     need_sort = i;
596     rc = i;
597     goto bail;
598   }
599   else if (i < 0)
600     /* fatal error */
601     return (-1);
602
603   /* Create a temporary file to write the new version of the mailbox in. */
604   mutt_mktemp (tempfile);
605   if ((i = open (tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1 ||
606       (fp = fdopen (i, "w")) == NULL) {
607     if (-1 != i) {
608       close (i);
609       unlink (tempfile);
610     }
611     mutt_error _("Could not create temporary file!");
612
613     mutt_sleep (5);
614     goto bail;
615   }
616
617   /* find the first deleted/changed message.  we save a lot of time by only
618    * rewriting the mailbox from the point where it has actually changed.
619    */
620   for (i = 0; i < ctx->msgcount && !ctx->hdrs[i]->deleted &&
621        !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++);
622   if (i == ctx->msgcount) {
623     /* this means ctx->changed or ctx->deleted was set, but no
624      * messages were found to be changed or deleted.  This should
625      * never happen, is we presume it is a bug in mutt.
626      */
627     mutt_error
628       _("sync: mbox modified, but no modified messages! (report this bug)");
629     mutt_sleep (5);             /* the mutt_error /will/ get cleared! */
630     debug_print (1, ("no modified messages.\n"));
631     unlink (tempfile);
632     goto bail;
633   }
634
635   /* save the index of the first changed/deleted message */
636   first = i;
637   /* where to start overwriting */
638   offset = ctx->hdrs[i]->offset;
639
640   /* the offset stored in the header does not include the MMDF_SEP, so make
641    * sure we seek to the correct location
642    */
643   if (ctx->magic == M_MMDF)
644     offset -= (sizeof MMDF_SEP - 1);
645
646   /* allocate space for the new offsets */
647   newOffset = mem_calloc (ctx->msgcount - first, sizeof (struct m_update_t));
648   oldOffset = mem_calloc (ctx->msgcount - first, sizeof (struct m_update_t));
649
650   for (i = first, j = 0; i < ctx->msgcount; i++) {
651     /*
652      * back up some information which is needed to restore offsets when
653      * something fails.
654      */
655
656     oldOffset[i - first].valid = 1;
657     oldOffset[i - first].hdr = ctx->hdrs[i]->offset;
658     oldOffset[i - first].body = ctx->hdrs[i]->content->offset;
659     oldOffset[i - first].lines = ctx->hdrs[i]->lines;
660     oldOffset[i - first].length = ctx->hdrs[i]->content->length;
661
662     if (!ctx->hdrs[i]->deleted) {
663       j++;
664       if (!ctx->quiet && WriteInc && ((i % WriteInc) == 0 || j == 1))
665         mutt_message (_("Writing messages... %d (%d%%)"), i,
666                       (int) (ftello (ctx->fp) / (ctx->size / 100 + 1)));
667
668       if (ctx->magic == M_MMDF) {
669         if (fputs (MMDF_SEP, fp) == EOF) {
670           mutt_perror (tempfile);
671           mutt_sleep (5);
672           unlink (tempfile);
673           goto bail;
674         }
675
676       }
677
678       /* save the new offset for this message.  we add `offset' because the
679        * temporary file only contains saved message which are located after
680        * `offset' in the real mailbox
681        */
682       newOffset[i - first].hdr = ftello (fp) + offset;
683
684       if (mutt_copy_message
685           (fp, ctx, ctx->hdrs[i], M_CM_UPDATE,
686            CH_FROM | CH_UPDATE | CH_UPDATE_LEN) == -1) {
687         mutt_perror (tempfile);
688         mutt_sleep (5);
689         unlink (tempfile);
690         goto bail;
691       }
692
693       /* Since messages could have been deleted, the offsets stored in memory
694        * will be wrong, so update what we can, which is the offset of this
695        * message, and the offset of the body.  If this is a multipart message,
696        * we just flush the in memory cache so that the message will be reparsed
697        * if the user accesses it later.
698        */
699       newOffset[i - first].body =
700         ftello (fp) - ctx->hdrs[i]->content->length + offset;
701       mutt_free_body (&ctx->hdrs[i]->content->parts);
702
703       switch (ctx->magic) {
704       case M_MMDF:
705         if (fputs (MMDF_SEP, fp) == EOF) {
706           mutt_perror (tempfile);
707           mutt_sleep (5);
708           unlink (tempfile);
709           goto bail;
710         }
711         break;
712       default:
713         if (fputs ("\n", fp) == EOF) {
714           mutt_perror (tempfile);
715           mutt_sleep (5);
716           unlink (tempfile);
717           goto bail;
718         }
719       }
720     }
721   }
722
723   if (fclose (fp) != 0) {
724     fp = NULL;
725     debug_print (1, ("fclose() returned non-zero.\n"));
726     unlink (tempfile);
727     mutt_perror (tempfile);
728     mutt_sleep (5);
729     goto bail;
730   }
731   fp = NULL;
732
733   /* Save the state of this folder. */
734   if (stat (ctx->path, &statbuf) == -1) {
735     mutt_perror (ctx->path);
736     mutt_sleep (5);
737     unlink (tempfile);
738     goto bail;
739   }
740
741   if ((fp = fopen (tempfile, "r")) == NULL) {
742     mutt_unblock_signals ();
743     mx_fastclose_mailbox (ctx);
744     debug_print (1, ("unable to reopen temp copy of mailbox!\n"));
745     mutt_perror (tempfile);
746     mutt_sleep (5);
747     return (-1);
748   }
749
750   if (fseeko (ctx->fp, offset, SEEK_SET) != 0 || /* seek the append location */
751       /* do a sanity check to make sure the mailbox looks ok */
752       fgets (buf, sizeof (buf), ctx->fp) == NULL ||
753       (ctx->magic == M_MBOX && str_ncmp ("From ", buf, 5) != 0) ||
754       (ctx->magic == M_MMDF && str_cmp (MMDF_SEP, buf) != 0)) {
755     debug_print (1, ("message not in expected position.\n"));
756     debug_print (1, ("LINE: %s\n", buf));
757     i = -1;
758   }
759   else {
760     if (fseeko (ctx->fp, offset, SEEK_SET) != 0) {       /* return to proper offset */
761       i = -1;
762       debug_print (1, ("fseeko() failed\n"));
763     }
764     else {
765       /* copy the temp mailbox back into place starting at the first
766        * change/deleted message
767        */
768       mutt_message _("Committing changes...");
769
770       i = mutt_copy_stream (fp, ctx->fp);
771
772       if (ferror (ctx->fp))
773         i = -1;
774     }
775     if (i == 0) {
776       ctx->size = ftello (ctx->fp);      /* update the size of the mailbox */
777       ftruncate (fileno (ctx->fp), ctx->size);
778     }
779   }
780
781   fclose (fp);
782   fp = NULL;
783   mbox_unlock_mailbox (ctx);
784
785   if (fclose (ctx->fp) != 0 || i == -1) {
786     /* error occured while writing the mailbox back, so keep the temp copy
787      * around
788      */
789
790     char savefile[_POSIX_PATH_MAX];
791
792     snprintf (savefile, sizeof (savefile), "%s/mutt.%s-%s-%u",
793               NONULL (Tempdir), NONULL (Username), NONULL (Hostname),
794               (unsigned int) getpid ());
795     rename (tempfile, savefile);
796     mutt_unblock_signals ();
797     mx_fastclose_mailbox (ctx);
798     mutt_pretty_mailbox (savefile);
799     mutt_error (_("Write failed!  Saved partial mailbox to %s"), savefile);
800     mutt_sleep (5);
801     return (-1);
802   }
803
804   /* Restore the previous access/modification times */
805   utimebuf.actime = statbuf.st_atime;
806   utimebuf.modtime = statbuf.st_mtime;
807   utime (ctx->path, &utimebuf);
808
809   /* reopen the mailbox in read-only mode */
810   if ((ctx->fp = fopen (ctx->path, "r")) == NULL) {
811     unlink (tempfile);
812     mutt_unblock_signals ();
813     mx_fastclose_mailbox (ctx);
814     mutt_error _("Fatal error!  Could not reopen mailbox!");
815     return (-1);
816   }
817
818   /* update the offsets of the rewritten messages */
819   for (i = first, j = first; i < ctx->msgcount; i++) {
820     if (!ctx->hdrs[i]->deleted) {
821       ctx->hdrs[i]->offset = newOffset[i - first].hdr;
822       ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr;
823       ctx->hdrs[i]->content->offset = newOffset[i - first].body;
824       ctx->hdrs[i]->index = j++;
825     }
826   }
827   mem_free (&newOffset);
828   mem_free (&oldOffset);
829   unlink (tempfile);            /* remove partial copy of the mailbox */
830   mutt_unblock_signals ();
831
832   return (0);                   /* signal success */
833
834 bail:                          /* Come here in case of disaster */
835
836   safe_fclose (&fp);
837
838   /* restore offsets, as far as they are valid */
839   if (first >= 0 && oldOffset) {
840     for (i = first; i < ctx->msgcount && oldOffset[i - first].valid; i++) {
841       ctx->hdrs[i]->offset = oldOffset[i - first].hdr;
842       ctx->hdrs[i]->content->hdr_offset = oldOffset[i - first].hdr;
843       ctx->hdrs[i]->content->offset = oldOffset[i - first].body;
844       ctx->hdrs[i]->lines = oldOffset[i - first].lines;
845       ctx->hdrs[i]->content->length = oldOffset[i - first].length;
846     }
847   }
848
849   /* this is ok to call even if we haven't locked anything */
850   mbox_unlock_mailbox (ctx);
851
852   mutt_unblock_signals ();
853   mem_free (&newOffset);
854   mem_free (&oldOffset);
855
856   if ((ctx->fp = freopen (ctx->path, "r", ctx->fp)) == NULL) {
857     mutt_error _("Could not reopen mailbox!");
858
859     mx_fastclose_mailbox (ctx);
860     return (-1);
861   }
862
863   if (need_sort)
864     /* if the mailbox was reopened, the thread tree will be invalid so make
865      * sure to start threading from scratch.  */
866     mutt_sort_headers (ctx, (need_sort == M_REOPENED));
867
868   return rc;
869 }
870
871 static int mbox_sync_mailbox (CONTEXT * ctx, int unused, int *index_hint) {
872 #ifdef BUFFY_SIZE
873   BUFFY* tmp = NULL;
874 #endif
875   int rc = _mbox_sync_mailbox (ctx, unused, index_hint);
876
877 #ifdef BUFFY_SIZE
878   if ((tmp = buffy_find_mailbox (ctx->path)) && tmp->new == 0)
879     buffy_update_mailbox (tmp);
880 #endif
881   return (rc);
882 }
883
884 /* close a mailbox opened in write-mode */
885 int mbox_close_mailbox (CONTEXT * ctx)
886 {
887   mx_unlock_file (ctx->path, fileno (ctx->fp), 1);
888
889 #ifdef USE_COMPRESSED
890   if (ctx->compressinfo)
891     mutt_slow_close_compressed (ctx);
892 #endif
893
894   mutt_unblock_signals ();
895   mx_fastclose_mailbox (ctx);
896   return 0;
897 }
898
899 static int mbox_reopen_mailbox (CONTEXT * ctx, int *index_hint)
900 {
901   int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
902   HEADER **old_hdrs;
903   int old_msgcount;
904   int msg_mod = 0;
905   int index_hint_set;
906   int i, j;
907   int rc = -1;
908
909   /* silent operations */
910   ctx->quiet = 1;
911
912   mutt_message _("Reopening mailbox...");
913
914   /* our heuristics require the old mailbox to be unsorted */
915   if (Sort != SORT_ORDER) {
916     short old_sort;
917
918     old_sort = Sort;
919     Sort = SORT_ORDER;
920     mutt_sort_headers (ctx, 1);
921     Sort = old_sort;
922   }
923
924   old_hdrs = NULL;
925   old_msgcount = 0;
926
927   /* simulate a close */
928   if (ctx->id_hash)
929     hash_destroy (&ctx->id_hash, NULL);
930   if (ctx->subj_hash)
931     hash_destroy (&ctx->subj_hash, NULL);
932   mutt_clear_threads (ctx);
933   mem_free (&ctx->v2r);
934   if (ctx->readonly) {
935     for (i = 0; i < ctx->msgcount; i++)
936       mutt_free_header (&(ctx->hdrs[i]));       /* nothing to do! */
937     mem_free (&ctx->hdrs);
938   }
939   else {
940     /* save the old headers */
941     old_msgcount = ctx->msgcount;
942     old_hdrs = ctx->hdrs;
943     ctx->hdrs = NULL;
944   }
945
946   ctx->hdrmax = 0;              /* force allocation of new headers */
947   ctx->msgcount = 0;
948   ctx->vcount = 0;
949   ctx->tagged = 0;
950   ctx->deleted = 0;
951   ctx->new = 0;
952   ctx->unread = 0;
953   ctx->flagged = 0;
954   ctx->changed = 0;
955   ctx->id_hash = NULL;
956   ctx->subj_hash = NULL;
957
958   switch (ctx->magic) {
959   case M_MBOX:
960   case M_MMDF:
961     if (fseeko (ctx->fp, 0, SEEK_SET) != 0) {
962       debug_print (1, ("fseeko() failed\n"));
963       rc = -1;
964     }
965     else {
966       cmp_headers = mutt_cmp_header;
967       if (ctx->magic == M_MBOX)
968         rc = mbox_parse_mailbox (ctx);
969       else
970         rc = mmdf_parse_mailbox (ctx);
971     }
972     break;
973
974   default:
975     rc = -1;
976     break;
977   }
978
979   if (rc == -1) {
980     /* free the old headers */
981     for (j = 0; j < old_msgcount; j++)
982       mutt_free_header (&(old_hdrs[j]));
983     mem_free (&old_hdrs);
984
985     ctx->quiet = 0;
986     return (-1);
987   }
988
989   /* now try to recover the old flags */
990
991   index_hint_set = (index_hint == NULL);
992
993   if (!ctx->readonly) {
994     for (i = 0; i < ctx->msgcount; i++) {
995       int found = 0;
996
997       /* some messages have been deleted, and new  messages have been
998        * appended at the end; the heuristic is that old messages have then
999        * "advanced" towards the beginning of the folder, so we begin the
1000        * search at index "i"
1001        */
1002       for (j = i; j < old_msgcount; j++) {
1003         if (old_hdrs[j] == NULL)
1004           continue;
1005         if (cmp_headers (ctx->hdrs[i], old_hdrs[j])) {
1006           found = 1;
1007           break;
1008         }
1009       }
1010       if (!found) {
1011         for (j = 0; j < i && j < old_msgcount; j++) {
1012           if (old_hdrs[j] == NULL)
1013             continue;
1014           if (cmp_headers (ctx->hdrs[i], old_hdrs[j])) {
1015             found = 1;
1016             break;
1017           }
1018         }
1019       }
1020
1021       if (found) {
1022         /* this is best done here */
1023         if (!index_hint_set && *index_hint == j)
1024           *index_hint = i;
1025
1026         if (old_hdrs[j]->changed) {
1027           /* Only update the flags if the old header was changed;
1028            * otherwise, the header may have been modified externally,
1029            * and we don't want to lose _those_ changes
1030            */
1031           mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, old_hdrs[j]->flagged);
1032           mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, old_hdrs[j]->replied);
1033           mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, old_hdrs[j]->old);
1034           mutt_set_flag (ctx, ctx->hdrs[i], M_READ, old_hdrs[j]->read);
1035         }
1036         mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, old_hdrs[j]->deleted);
1037         mutt_set_flag (ctx, ctx->hdrs[i], M_TAG, old_hdrs[j]->tagged);
1038
1039         /* we don't need this header any more */
1040         mutt_free_header (&(old_hdrs[j]));
1041       }
1042     }
1043
1044     /* free the remaining old headers */
1045     for (j = 0; j < old_msgcount; j++) {
1046       if (old_hdrs[j]) {
1047         mutt_free_header (&(old_hdrs[j]));
1048         msg_mod = 1;
1049       }
1050     }
1051     mem_free (&old_hdrs);
1052   }
1053
1054   ctx->quiet = 0;
1055
1056   return ((ctx->changed || msg_mod) ? M_REOPENED : M_NEW_MAIL);
1057 }
1058
1059 /*
1060  * Returns:
1061  * 1 if the mailbox is not empty
1062  * 0 if the mailbox is empty
1063  * -1 on error
1064  */
1065 int mbox_check_empty (const char *path)
1066 {
1067   struct stat st;
1068
1069   if (stat (path, &st) == -1)
1070     return -1;
1071
1072   return ((st.st_size == 0));
1073 }
1074
1075 int mbox_is_magic (const char* path, struct stat* st) {
1076   int magic = -1;
1077   FILE* f;
1078   char tmp[_POSIX_PATH_MAX];
1079
1080   if (S_ISDIR(st->st_mode))
1081     return (-1);
1082
1083   if (st->st_size == 0) {
1084     /* hard to tell what zero-length files are, so assume the default magic */
1085     if (DefaultMagic == M_MBOX || DefaultMagic == M_MMDF)
1086       return (DefaultMagic);
1087     else
1088       return (M_MBOX);
1089   }
1090   else if ((f = fopen (path, "r")) != NULL) {
1091 #ifndef BUFFY_SIZE
1092     struct utimbuf times;
1093 #endif
1094     fgets (tmp, sizeof (tmp), f);
1095     if (str_ncmp ("From ", tmp, 5) == 0)
1096       magic = M_MBOX;
1097     else if (str_cmp (MMDF_SEP, tmp) == 0)
1098       magic = M_MMDF;
1099     safe_fclose (&f);
1100 #ifndef BUFFY_SIZE
1101     /* need to restore the times here, the file was not really accessed,
1102      * only the type was accessed.  This is important, because detection
1103      * of "new mail" depends on those times set correctly.
1104      */
1105     times.actime = st->st_atime;
1106     times.modtime = st->st_mtime;
1107     utime (path, &times);
1108 #endif
1109   } else {
1110     mutt_perror (path);
1111     return (-1);         /* fopen failed */
1112   }
1113
1114 #ifdef USE_COMPRESSED
1115   if (magic == -1 && mutt_can_read_compressed (path))
1116     return (M_COMPRESSED);
1117 #endif
1118   return (magic);
1119 }
1120
1121 static int commit_message (MESSAGE* msg, CONTEXT* ctx, int mbox) {
1122   if ((mbox && fputc ('\n', msg->fp) == EOF) ||
1123       (!mbox && fputs (MMDF_SEP, msg->fp) == EOF))
1124     return (-1);
1125   if ((fflush (msg->fp) == EOF || fsync (fileno (msg->fp)) == -1)) {
1126     mutt_perror (_("Can't write message"));
1127     return (-1);
1128   }
1129   return (0);
1130 }
1131
1132 static int mbox_commit_message (MESSAGE* msg, CONTEXT* ctx) {
1133   return (commit_message (msg, ctx, 1));
1134 }
1135
1136 static int mmdf_commit_message (MESSAGE* msg, CONTEXT* ctx) {
1137   return (commit_message (msg, ctx, 0));
1138 }
1139
1140 static mx_t* reg_mx (void) {
1141   mx_t* fmt = mem_calloc (1, sizeof (mx_t));
1142   fmt->local = 1;
1143   fmt->mx_check_empty = mbox_check_empty;
1144   fmt->mx_is_magic = mbox_is_magic;
1145   fmt->mx_access = access;
1146   fmt->mx_open_mailbox = mbox_open_mailbox;
1147   fmt->mx_open_new_message = mbox_open_new_message;
1148   fmt->mx_sync_mailbox = mbox_sync_mailbox;
1149   fmt->mx_check_mailbox = mbox_check_mailbox;
1150   return (fmt);
1151 }
1152
1153 mx_t* mbox_reg_mx (void) {
1154   mx_t* fmt = reg_mx ();
1155   fmt->type = M_MBOX;
1156   fmt->mx_commit_message = mbox_commit_message;
1157   return (fmt);
1158 }
1159 mx_t* mmdf_reg_mx (void) {
1160   mx_t* fmt = reg_mx ();
1161   fmt->type = M_MMDF;
1162   fmt->mx_commit_message = mmdf_commit_message;
1163   return (fmt);
1164 }