simplify count_body_parts drastically.
[apps/madmutt.git] / mx.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #if HAVE_CONFIG_H
12 # include "config.h"
13 #endif
14
15 #include <lib-lib/mem.h>
16 #include <lib-lib/ascii.h>
17 #include <lib-lib/str.h>
18 #include <lib-lib/macros.h>
19 #include <lib-lib/file.h>
20
21 #include <lib-mime/mime.h>
22
23 #include "mutt.h"
24 #include "buffy.h"
25 #include "mx.h"
26 #include "mbox.h"
27 #include "mh.h"
28 #include "sort.h"
29 #include "thread.h"
30 #include "copy.h"
31 #include "keymap.h"
32 #include "url.h"
33 #include "sidebar.h"
34 #include "compress.h"
35
36 #include <imap/imap.h>
37 #include <imap/mx_imap.h>
38
39 #include <pop/pop.h>
40 #include <pop/mx_pop.h>
41
42 #ifdef USE_NNTP
43 #include "nntp/nntp.h"
44 #include "nntp/mx_nntp.h"
45 #endif
46
47 #ifdef USE_DOTLOCK
48 #include "dotlock.h"
49 #endif
50
51 #include <lib-crypt/crypt.h>
52
53 #include "lib/list.h"
54 #include "lib/debug.h"
55
56 #include <dirent.h>
57 #include <fcntl.h>
58 #include <sys/file.h>
59 #include <sys/stat.h>
60 #include <errno.h>
61 #include <unistd.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <ctype.h>
65 #include <utime.h>
66
67 static list2_t* MailboxFormats = NULL;
68 #define MX_COMMAND(idx,cmd) ((mx_t*) MailboxFormats->data[idx])->cmd
69 #define MX_IDX(idx) (idx >= 0 && idx < MailboxFormats->length)
70
71 #define mutt_is_spool(s)  (m_strcmp(Spoolfile, s) == 0)
72
73 #ifdef USE_DOTLOCK
74 /* parameters: 
75  * path - file to lock
76  * retry - should retry if unable to lock?
77  */
78
79 static int invoke_dotlock (const char *path, int dummy, int flags, int retry)
80 {
81   char cmd[LONG_STRING + _POSIX_PATH_MAX];
82   char f[SHORT_STRING + _POSIX_PATH_MAX];
83   char r[SHORT_STRING];
84
85   if (flags & DL_FL_RETRY)
86     snprintf (r, sizeof (r), "-r %d ", retry ? MAXLOCKATTEMPT : 0);
87
88   mutt_quote_filename (f, sizeof (f), path);
89
90   snprintf (cmd, sizeof (cmd),
91             "%s %s%s%s%s%s%s%s",
92             NONULL (MuttDotlock),
93             flags & DL_FL_TRY ? "-t " : "",
94             flags & DL_FL_UNLOCK ? "-u " : "",
95             flags & DL_FL_USEPRIV ? "-p " : "",
96             flags & DL_FL_FORCE ? "-f " : "",
97             flags & DL_FL_UNLINK ? "-d " : "",
98             flags & DL_FL_RETRY ? r : "", f);
99
100   return mutt_system (cmd);
101 }
102
103 static int dotlock_file (const char *path, int fd, int retry)
104 {
105   int r;
106   int flags = DL_FL_USEPRIV | DL_FL_RETRY;
107
108   if (retry)
109     retry = 1;
110
111 retry_lock:
112   if ((r = invoke_dotlock (path, fd, flags, retry)) == DL_EX_EXIST) {
113     if (!option (OPTNOCURSES)) {
114       char msg[LONG_STRING];
115
116       snprintf (msg, sizeof (msg),
117                 _("Lock count exceeded, remove lock for %s?"), path);
118       if (retry && mutt_yesorno (msg, M_YES) == M_YES) {
119         flags |= DL_FL_FORCE;
120         retry--;
121         mutt_clear_error ();
122         goto retry_lock;
123       }
124     }
125     else {
126       mutt_error (_("Can't dotlock %s.\n"), path);
127     }
128   }
129   return (r == DL_EX_OK ? 0 : -1);
130 }
131
132 static int undotlock_file (const char *path, int fd)
133 {
134   return (invoke_dotlock (path, fd, DL_FL_USEPRIV | DL_FL_UNLOCK, 0) ==
135           DL_EX_OK ? 0 : -1);
136 }
137
138 #endif /* USE_DOTLOCK */
139
140 /* looks up index of type for path in MailboxFormats */
141 static int mx_get_idx (const char* path) {
142   int i = 0, t = 0;
143   struct stat st;
144
145   /* first, test all non-local folders to avoid stat() call */
146   for (i = 0; i < MailboxFormats->length; i++) {
147     if (!MX_COMMAND(i,local))
148       t = MX_COMMAND(i,mx_is_magic)(path, NULL);
149     if (t >= 1)
150       return (t-1);
151   }
152   if (stat (path, &st) == 0) {
153     /* if stat() succeeded, keep testing until success and
154      * pass stat() info so that we only need to do it once */
155     for (i = 0; i < MailboxFormats->length; i++) {
156       if (MX_COMMAND(i,local))
157         t = MX_COMMAND(i,mx_is_magic)(path, &st);
158       if (t >= 1)
159         return (t-1);
160     }
161   }
162   return (-1);
163 }
164
165 /* Args:
166  *      excl            if excl != 0, request an exclusive lock
167  *      dot             if dot != 0, try to dotlock the file
168  *      timeout         should retry locking?
169  */
170 int mx_lock_file (const char *path, int fd, int excl, int dot, int timeout)
171 {
172 #if defined (USE_FCNTL) || defined (USE_FLOCK)
173   int count;
174   int attempt;
175   struct stat prev_sb;
176 #endif
177   int r = 0;
178
179 #ifdef USE_FCNTL
180   struct flock lck;
181
182
183   p_clear(&lck, 1);
184   lck.l_type = excl ? F_WRLCK : F_RDLCK;
185   lck.l_whence = SEEK_SET;
186
187   count = 0;
188   attempt = 0;
189   prev_sb.st_size = 0;
190   while (fcntl (fd, F_SETLK, &lck) == -1) {
191     struct stat sb;
192
193     debug_print (1, ("fcntl errno %d.\n", errno));
194     if (errno != EAGAIN && errno != EACCES) {
195       mutt_perror ("fcntl");
196       return (-1);
197     }
198
199     if (fstat (fd, &sb) != 0)
200       sb.st_size = 0;
201
202     if (count == 0)
203       prev_sb = sb;
204
205     /* only unlock file if it is unchanged */
206     if (prev_sb.st_size == sb.st_size
207         && ++count >= (timeout ? MAXLOCKATTEMPT : 0)) {
208       if (timeout)
209         mutt_error _("Timeout exceeded while attempting fcntl lock!");
210
211       return (-1);
212     }
213
214     prev_sb = sb;
215
216     mutt_message (_("Waiting for fcntl lock... %d"), ++attempt);
217     sleep (1);
218   }
219 #endif /* USE_FCNTL */
220
221 #ifdef USE_FLOCK
222   count = 0;
223   attempt = 0;
224   while (flock (fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1) {
225     struct stat sb;
226
227     if (errno != EWOULDBLOCK) {
228       mutt_perror ("flock");
229       r = -1;
230       break;
231     }
232
233     if (fstat (fd, &sb) != 0)
234       sb.st_size = 0;
235
236     if (count == 0)
237       prev_sb = sb;
238
239     /* only unlock file if it is unchanged */
240     if (prev_sb.st_size == sb.st_size
241         && ++count >= (timeout ? MAXLOCKATTEMPT : 0)) {
242       if (timeout)
243         mutt_error _("Timeout exceeded while attempting flock lock!");
244
245       r = -1;
246       break;
247     }
248
249     prev_sb = sb;
250
251     mutt_message (_("Waiting for flock attempt... %d"), ++attempt);
252     sleep (1);
253   }
254 #endif /* USE_FLOCK */
255
256 #ifdef USE_DOTLOCK
257   if (r == 0 && dot)
258     r = dotlock_file (path, fd, timeout);
259 #endif /* USE_DOTLOCK */
260
261   if (r == -1) {
262     /* release any other locks obtained in this routine */
263
264 #ifdef USE_FCNTL
265     lck.l_type = F_UNLCK;
266     fcntl (fd, F_SETLK, &lck);
267 #endif /* USE_FCNTL */
268
269 #ifdef USE_FLOCK
270     flock (fd, LOCK_UN);
271 #endif /* USE_FLOCK */
272
273     return (-1);
274   }
275
276   return 0;
277 }
278
279 int mx_unlock_file (const char *path, int fd, int dot)
280 {
281 #ifdef USE_FCNTL
282   struct flock unlockit;
283
284   p_clear(&unlockit, 1);
285   unlockit.l_type = F_UNLCK;
286   unlockit.l_whence = SEEK_SET;
287   fcntl (fd, F_SETLK, &unlockit);
288 #endif
289
290 #ifdef USE_FLOCK
291   flock (fd, LOCK_UN);
292 #endif
293
294 #ifdef USE_DOTLOCK
295   if (dot)
296     undotlock_file (path, fd);
297 #endif
298
299   return 0;
300 }
301
302 void mx_unlink_empty (const char *path)
303 {
304   int fd;
305
306 #ifndef USE_DOTLOCK
307   struct stat sb;
308 #endif
309
310   if ((fd = open (path, O_RDWR)) == -1)
311     return;
312
313   if (mx_lock_file (path, fd, 1, 0, 1) == -1) {
314     close (fd);
315     return;
316   }
317
318 #ifdef USE_DOTLOCK
319   invoke_dotlock (path, fd, DL_FL_UNLINK, 1);
320 #else
321   if (fstat (fd, &sb) == 0 && sb.st_size == 0)
322     unlink (path);
323 #endif
324
325   mx_unlock_file (path, fd, 0);
326   close (fd);
327 }
328
329 /* try to figure out what type of mailbox ``path'' is */
330 int mx_get_magic (const char *path) {
331   int i = 0;
332
333   if (m_strlen(path) == 0)
334     return (-1);
335   if ((i = mx_get_idx (path)) >= 0)
336     return (MX_COMMAND(i,type));
337   return (-1);
338 }
339
340 int mx_is_local (int m) {
341   if (!MX_IDX(m))
342     return (0);
343   return (MX_COMMAND(m,local));
344 }
345
346 /*
347  * set DefaultMagic to the given value
348  */
349 int mx_set_magic (const char *s)
350 {
351   if (ascii_strcasecmp (s, "mbox") == 0)
352     DefaultMagic = M_MBOX;
353   else if (ascii_strcasecmp (s, "mmdf") == 0)
354     DefaultMagic = M_MMDF;
355   else if (ascii_strcasecmp (s, "mh") == 0)
356     DefaultMagic = M_MH;
357   else if (ascii_strcasecmp (s, "maildir") == 0)
358     DefaultMagic = M_MAILDIR;
359   else
360     return (-1);
361
362   return 0;
363 }
364
365 /* mx_access: Wrapper for access, checks permissions on a given mailbox.
366  *   We may be interested in using ACL-style flags at some point, currently
367  *   we use the normal access() flags. */
368 int mx_access (const char *path, int flags)
369 {
370   int i = 0;
371
372   if ((i = mx_get_idx (path)) >= 0 && MX_COMMAND(i,mx_access))
373     return (MX_COMMAND(i,mx_access)(path,flags));
374   return (0);
375 }
376
377 static int mx_open_mailbox_append (CONTEXT * ctx, int flags)
378 {
379   struct stat sb;
380
381   /* special case for appending to compressed folders -
382    * even if we can not open them for reading */
383   if (mutt_can_append_compressed (ctx->path))
384     mutt_open_append_compressed (ctx);
385
386   ctx->append = 1;
387
388   if (mx_get_magic (ctx->path) == M_IMAP)
389     return imap_open_mailbox_append (ctx);
390
391   if (stat (ctx->path, &sb) == 0) {
392     ctx->magic = mx_get_magic (ctx->path);
393
394     switch (ctx->magic) {
395     case 0:
396       mutt_error (_("%s is not a mailbox."), ctx->path);
397       /* fall through */
398     case -1:
399       return (-1);
400     }
401   }
402   else if (errno == ENOENT) {
403     ctx->magic = DefaultMagic;
404
405     if (ctx->magic == M_MH || ctx->magic == M_MAILDIR) {
406       char tmp[_POSIX_PATH_MAX];
407
408       if (mkdir (ctx->path, S_IRWXU)) {
409         mutt_perror (ctx->path);
410         return (-1);
411       }
412
413       if (ctx->magic == M_MAILDIR) {
414         snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
415         if (mkdir (tmp, S_IRWXU)) {
416           mutt_perror (tmp);
417           rmdir (ctx->path);
418           return (-1);
419         }
420
421         snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
422         if (mkdir (tmp, S_IRWXU)) {
423           mutt_perror (tmp);
424           snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
425           rmdir (tmp);
426           rmdir (ctx->path);
427           return (-1);
428         }
429         snprintf (tmp, sizeof (tmp), "%s/tmp", ctx->path);
430         if (mkdir (tmp, S_IRWXU)) {
431           mutt_perror (tmp);
432           snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
433           rmdir (tmp);
434           snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
435           rmdir (tmp);
436           rmdir (ctx->path);
437           return (-1);
438         }
439       }
440       else {
441         int i;
442
443         snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", ctx->path);
444         if ((i = creat (tmp, S_IRWXU)) == -1) {
445           mutt_perror (tmp);
446           rmdir (ctx->path);
447           return (-1);
448         }
449         close (i);
450       }
451     }
452   }
453   else {
454     mutt_perror (ctx->path);
455     return (-1);
456   }
457
458   switch (ctx->magic) {
459   case M_MBOX:
460   case M_MMDF:
461     if ((ctx->fp =
462          safe_fopen (ctx->path, flags & M_NEWFOLDER ? "w" : "a")) == NULL
463         || mbox_lock_mailbox (ctx, 1, 1) != 0) {
464       if (!ctx->fp)
465         mutt_perror (ctx->path);
466       else {
467         mutt_error (_("Couldn't lock %s\n"), ctx->path);
468         safe_fclose (&ctx->fp);
469       }
470       return (-1);
471     }
472     fseeko (ctx->fp, 0, 2);
473     break;
474
475   case M_MH:
476   case M_MAILDIR:
477     /* nothing to do */
478     break;
479
480   default:
481     return (-1);
482   }
483
484   return 0;
485 }
486
487 /*
488  * open a mailbox and parse it
489  *
490  * Args:
491  *      flags   M_NOSORT        do not sort mailbox
492  *              M_APPEND        open mailbox for appending
493  *              M_READONLY      open mailbox in read-only mode
494  *              M_QUIET         only print error messages
495  *      ctx     if non-null, context struct to use
496  */
497 CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT * pctx)
498 {
499   CONTEXT *ctx = pctx;
500   int rc;
501
502   if (!ctx)
503     ctx = p_new(CONTEXT, 1);
504   p_clear(ctx, 1);
505   ctx->path = m_strdup(path);
506
507   ctx->msgnotreadyet = -1;
508   ctx->collapsed = 0;
509
510   if (flags & M_QUIET)
511     ctx->quiet = 1;
512   if (flags & M_READONLY)
513     ctx->readonly = 1;
514   if (flags & M_COUNT)
515     ctx->counting = 1;
516
517   if (flags & (M_APPEND | M_NEWFOLDER)) {
518     if (mx_open_mailbox_append (ctx, flags) != 0) {
519       mx_fastclose_mailbox (ctx);
520       if (!pctx)
521         p_delete(&ctx);
522       return NULL;
523     }
524     return ctx;
525   }
526
527   if (!MX_IDX(ctx->magic-1))
528     ctx->magic = mx_get_magic (path);
529
530   if (ctx->magic == M_COMPRESSED)
531     mutt_open_read_compressed (ctx);
532
533   if (ctx->magic == 0)
534     mutt_error (_("%s is not a mailbox."), path);
535
536   if (ctx->magic == -1)
537     mutt_perror (path);
538
539   if (ctx->magic <= 0) {
540     mx_fastclose_mailbox (ctx);
541     if (!pctx)
542       p_delete(&ctx);
543     return (NULL);
544   }
545
546   /* if the user has a `push' command in their .muttrc, or in a folder-hook,
547    * it will cause the progress messages not to be displayed because
548    * mutt_refresh() will think we are in the middle of a macro.  so set a
549    * flag to indicate that we should really refresh the screen.
550    */
551   set_option (OPTFORCEREFRESH);
552
553   if (!ctx->quiet)
554     mutt_message (_("Reading %s..."), ctx->path);
555
556   rc = MX_COMMAND(ctx->magic-1,mx_open_mailbox)(ctx);
557
558   if (rc == 0) {
559     if ((flags & M_NOSORT) == 0) {
560       /* avoid unnecessary work since the mailbox is completely unthreaded
561          to begin with */
562       unset_option (OPTSORTSUBTHREADS);
563       unset_option (OPTNEEDRESCORE);
564       mutt_sort_headers (ctx, 1);
565     }
566     if (!ctx->quiet)
567       mutt_clear_error ();
568   }
569   else {
570     mx_fastclose_mailbox (ctx);
571     if (!pctx)
572       p_delete(&ctx);
573   }
574
575   unset_option (OPTFORCEREFRESH);
576   return (ctx);
577 }
578
579 /* free up memory associated with the mailbox context */
580 void mx_fastclose_mailbox (CONTEXT * ctx)
581 {
582   int i;
583
584   if (!ctx)
585     return;
586
587   if (MX_IDX(ctx->magic-1) && MX_COMMAND(ctx->magic-1,mx_fastclose_mailbox))
588     MX_COMMAND(ctx->magic-1,mx_fastclose_mailbox(ctx));
589   if (ctx->subj_hash)
590     hash_destroy (&ctx->subj_hash, NULL);
591   if (ctx->id_hash)
592     hash_destroy (&ctx->id_hash, NULL);
593   mutt_clear_threads (ctx);
594   for (i = 0; i < ctx->msgcount; i++)
595     header_delete(&ctx->hdrs[i]);
596   p_delete(&ctx->hdrs);
597   p_delete(&ctx->v2r);
598
599   if (ctx->compressinfo)
600     mutt_fast_close_compressed (ctx);
601
602   p_delete(&ctx->path);
603   p_delete(&ctx->pattern);
604   if (ctx->limit_pattern)
605     mutt_pattern_free (&ctx->limit_pattern);
606   safe_fclose (&ctx->fp);
607   p_clear(ctx, 1);
608 }
609
610 /* save changes to disk */
611 static int sync_mailbox (CONTEXT * ctx, int *index_hint)
612 {
613   int rc = -1;
614
615   if (!ctx->quiet)
616     mutt_message (_("Writing %s..."), ctx->path);
617
618   if (MX_IDX(ctx->magic-1))
619     /* the 1 is only of interest for IMAP and means EXPUNGE */
620     rc = MX_COMMAND(ctx->magic-1,mx_sync_mailbox(ctx,1,index_hint));
621
622   if (rc == 0 && ctx->compressinfo)
623     return mutt_sync_compressed (ctx);
624
625   return rc;
626 }
627
628 /* move deleted mails to the trash folder */
629 static int trash_append (CONTEXT * ctx)
630 {
631   CONTEXT *ctx_trash;
632   int i = 0;
633   struct stat st, stc;
634
635   if (!TrashPath || !ctx->deleted ||
636       (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH)))
637     return 0;
638
639   for (; i < ctx->msgcount && (!ctx->hdrs[i]->deleted ||
640                                ctx->hdrs[i]->appended); i++);
641   if (i == ctx->msgcount)
642     return 0;                   /* nothing to be done */
643
644   if (mutt_save_confirm (TrashPath, &st) != 0) {
645     mutt_error _("message(s) not deleted");
646
647     return -1;
648   }
649
650   if (lstat (ctx->path, &stc) == 0 && stc.st_ino == st.st_ino
651       && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev)
652     return 0;                   /* we are in the trash folder: simple sync */
653
654   if ((ctx_trash = mx_open_mailbox (TrashPath, M_APPEND, NULL)) != NULL) {
655     for (i = 0; i < ctx->msgcount; i++)
656       if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->appended
657           && !ctx->hdrs[i]->purged
658           && mutt_append_message (ctx_trash, ctx, ctx->hdrs[i], 0, 0) == -1) {
659         mx_close_mailbox (ctx_trash, NULL);
660         return -1;
661       }
662
663     mx_close_mailbox (ctx_trash, NULL);
664   }
665   else {
666     mutt_error _("Can't open trash folder");
667
668     return -1;
669   }
670
671   return 0;
672 }
673
674 /* save changes and close mailbox */
675 static int _mx_close_mailbox (CONTEXT * ctx, int *index_hint)
676 {
677   int i, move_messages = 0, purge = 1, read_msgs = 0;
678   int check;
679   int isSpool = 0;
680   CONTEXT f;
681   char mbox[_POSIX_PATH_MAX];
682   char buf[SHORT_STRING];
683
684   if (!ctx)
685     return 0;
686
687   ctx->closing = 1;
688
689 #ifdef USE_NNTP
690   if (ctx->magic == M_NNTP) {
691     int ret;
692
693     ret = nntp_close_mailbox (ctx);
694     mx_fastclose_mailbox (ctx);
695     return ret;
696   }
697 #endif
698   if (ctx->readonly || ctx->dontwrite) {
699     /* mailbox is readonly or we don't want to write */
700     mx_fastclose_mailbox (ctx);
701     return 0;
702   }
703
704   if (ctx->append) {
705     /* mailbox was opened in write-mode */
706     if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
707       mbox_close_mailbox (ctx);
708     else
709       mx_fastclose_mailbox (ctx);
710     return 0;
711   }
712
713   for (i = 0; i < ctx->msgcount; i++) {
714     if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
715         && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
716       read_msgs++;
717   }
718
719   if (read_msgs && quadoption (OPT_MOVE) != M_NO) {
720     char *p;
721
722     if ((p = mutt_find_hook (M_MBOXHOOK, ctx->path))) {
723       isSpool = 1;
724       m_strcpy(mbox, sizeof(mbox), p);
725     }
726     else {
727       m_strcpy(mbox, sizeof(mbox), NONULL(Inbox));
728       isSpool = mutt_is_spool (ctx->path) && !mutt_is_spool (mbox);
729     }
730     mutt_expand_path (mbox, sizeof (mbox));
731
732     if (isSpool) {
733       snprintf (buf, sizeof (buf), _("Move read messages to %s?"), mbox);
734       if ((move_messages = query_quadoption (OPT_MOVE, buf)) == -1) {
735         ctx->closing = 0;
736         return (-1);
737       }
738     }
739   }
740
741   /* 
742    * There is no point in asking whether or not to purge if we are
743    * just marking messages as "trash".
744    */
745   if (ctx->deleted && !(ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) {
746     snprintf (buf, sizeof (buf), ctx->deleted == 1
747               ? _("Purge %d deleted message?") :
748               _("Purge %d deleted messages?"), ctx->deleted);
749     if ((purge = query_quadoption (OPT_DELETE, buf)) < 0) {
750       ctx->closing = 0;
751       return (-1);
752     }
753   }
754
755   /* IMAP servers manage the OLD flag themselves */
756   if (ctx->magic != M_IMAP)
757     if (option (OPTMARKOLD)) {
758       for (i = 0; i < ctx->msgcount; i++) {
759         if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->old)
760           mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, 1);
761       }
762     }
763
764   if (move_messages) {
765     mutt_message (_("Moving read messages to %s..."), mbox);
766
767     /* try to use server-side copy first */
768     i = 1;
769
770     if (ctx->magic == M_IMAP && imap_is_magic (mbox, NULL) == M_IMAP) {
771       /* tag messages for moving, and clear old tags, if any */
772       for (i = 0; i < ctx->msgcount; i++)
773         if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted
774             && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
775           ctx->hdrs[i]->tagged = 1;
776         else
777           ctx->hdrs[i]->tagged = 0;
778
779       i = imap_copy_messages (ctx, NULL, mbox, 1);
780     }
781
782     if (i == 0)                 /* success */
783       mutt_clear_error ();
784     else if (i == -1) {         /* horrible error, bail */
785       ctx->closing = 0;
786       return -1;
787     }
788     else                        /* use regular append-copy mode */
789     {
790       if (mx_open_mailbox (mbox, M_APPEND, &f) == NULL) {
791         ctx->closing = 0;
792         return -1;
793       }
794
795       for (i = 0; i < ctx->msgcount; i++) {
796         if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted
797             && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) {
798           if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) ==
799               0) {
800             mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, 1);
801             mutt_set_flag (ctx, ctx->hdrs[i], M_APPENDED, 1);
802           }
803           else {
804             mx_close_mailbox (&f, NULL);
805             ctx->closing = 0;
806             return -1;
807           }
808         }
809       }
810
811       mx_close_mailbox (&f, NULL);
812     }
813
814   }
815   else if (!ctx->changed && ctx->deleted == 0) {
816     mutt_message _("Mailbox is unchanged.");
817
818     mx_fastclose_mailbox (ctx);
819     return 0;
820   }
821
822   /* copy mails to the trash before expunging */
823   if (purge && ctx->deleted)
824     if (trash_append (ctx) != 0) {
825       ctx->closing = 0;
826       return -1;
827     }
828
829   /* allow IMAP to preserve the deleted flag across sessions */
830   if (ctx->magic == M_IMAP) {
831     if ((check = imap_sync_mailbox (ctx, purge, index_hint)) != 0) {
832       ctx->closing = 0;
833       return check;
834     }
835   }
836   else
837   {
838     if (!purge) {
839       for (i = 0; i < ctx->msgcount; i++)
840         ctx->hdrs[i]->deleted = 0;
841       ctx->deleted = 0;
842     }
843
844     if (ctx->changed || ctx->deleted) {
845       if ((check = sync_mailbox (ctx, index_hint)) != 0) {
846         ctx->closing = 0;
847         return check;
848       }
849     }
850   }
851
852   if (move_messages)
853     mutt_message (_("%d kept, %d moved, %d deleted."),
854                   ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
855   else
856     mutt_message (_("%d kept, %d deleted."),
857                   ctx->msgcount - ctx->deleted, ctx->deleted);
858
859   if (ctx->msgcount == ctx->deleted &&
860       (ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
861       !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY))
862     mx_unlink_empty (ctx->path);
863
864   if (ctx->compressinfo && mutt_slow_close_compressed (ctx))
865     return (-1);
866
867   mx_fastclose_mailbox (ctx);
868
869   return 0;
870 }
871
872 int mx_close_mailbox (CONTEXT * ctx, int *index_hint) {
873   int ret = 0;
874   if (!ctx)
875     return (0);
876   ret = _mx_close_mailbox (ctx, index_hint);
877   sidebar_set_buffystats (ctx);
878   return (ret);
879 }
880
881 /* update a Context structure's internal tables. */
882
883 void mx_update_tables (CONTEXT * ctx, int committing)
884 {
885   int i, j;
886
887   /* update memory to reflect the new state of the mailbox */
888   ctx->vcount = 0;
889   ctx->vsize = 0;
890   ctx->tagged = 0;
891   ctx->deleted = 0;
892   ctx->new = 0;
893   ctx->unread = 0;
894   ctx->changed = 0;
895   ctx->flagged = 0;
896 #define this_body ctx->hdrs[j]->content
897   for (i = 0, j = 0; i < ctx->msgcount; i++) {
898     if ((committing && (!ctx->hdrs[i]->deleted ||
899                         (ctx->magic == M_MAILDIR
900                          && option (OPTMAILDIRTRASH)))) || (!committing
901                                                             && ctx->hdrs[i]->
902                                                             active)) {
903       if (i != j) {
904         ctx->hdrs[j] = ctx->hdrs[i];
905         ctx->hdrs[i] = NULL;
906       }
907       ctx->hdrs[j]->msgno = j;
908       if (ctx->hdrs[j]->virtual != -1) {
909         ctx->v2r[ctx->vcount] = j;
910         ctx->hdrs[j]->virtual = ctx->vcount++;
911         ctx->vsize += this_body->length + this_body->offset -
912           this_body->hdr_offset;
913       }
914
915       if (committing)
916         ctx->hdrs[j]->changed = 0;
917       else if (ctx->hdrs[j]->changed)
918         ctx->changed++;
919
920       if (!committing
921           || (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) {
922         if (ctx->hdrs[j]->deleted)
923           ctx->deleted++;
924       }
925
926       if (ctx->hdrs[j]->tagged)
927         ctx->tagged++;
928       if (ctx->hdrs[j]->flagged)
929         ctx->flagged++;
930       if (!ctx->hdrs[j]->read) {
931         ctx->unread++;
932         if (!ctx->hdrs[j]->old)
933           ctx->new++;
934       }
935
936       j++;
937     }
938     else {
939       if (ctx->magic == M_MH || ctx->magic == M_MAILDIR)
940         ctx->size -= (ctx->hdrs[i]->content->length +
941                       ctx->hdrs[i]->content->offset -
942                       ctx->hdrs[i]->content->hdr_offset);
943       /* remove message from the hash tables */
944       if (ctx->subj_hash && ctx->hdrs[i]->env->real_subj)
945         hash_delete (ctx->subj_hash, ctx->hdrs[i]->env->real_subj,
946                      ctx->hdrs[i], NULL);
947       if (ctx->id_hash && ctx->hdrs[i]->env->message_id)
948         hash_delete (ctx->id_hash, ctx->hdrs[i]->env->message_id,
949                      ctx->hdrs[i], NULL);
950       header_delete(&ctx->hdrs[i]);
951     }
952   }
953 #undef this_body
954   ctx->msgcount = j;
955
956   /* update sidebar count */
957   sidebar_set_buffystats (ctx);
958 }
959
960
961 /* save changes to mailbox
962  *
963  * return values:
964  *      0               success
965  *      -1              error
966  */
967 static int _mx_sync_mailbox (CONTEXT * ctx, int *index_hint)
968 {
969   int rc, i;
970   int purge = 1;
971   int msgcount, deleted;
972
973   if (ctx->dontwrite) {
974     char buf[STRING], tmp[STRING];
975
976     if (km_expand_key (buf, sizeof (buf),
977                        km_find_func (MENU_MAIN, OP_TOGGLE_WRITE)))
978       snprintf (tmp, sizeof (tmp), _(" Press '%s' to toggle write"), buf);
979     else
980       m_strcpy(tmp, sizeof(tmp), _("Use 'toggle-write' to re-enable write!"));
981
982     mutt_error (_("Mailbox is marked unwritable. %s"), tmp);
983     return -1;
984   }
985   else if (ctx->readonly) {
986     mutt_error _("Mailbox is read-only.");
987
988     return -1;
989   }
990
991   if (!ctx->changed && !ctx->deleted) {
992     mutt_message _("Mailbox is unchanged.");
993
994     return (0);
995   }
996
997   if (ctx->deleted) {
998     char buf[SHORT_STRING];
999
1000     snprintf (buf, sizeof (buf), ctx->deleted == 1
1001               ? _("Purge %d deleted message?") :
1002               _("Purge %d deleted messages?"), ctx->deleted);
1003     if ((purge = query_quadoption (OPT_DELETE, buf)) < 0)
1004       return (-1);
1005     else if (purge == M_NO) {
1006       if (!ctx->changed)
1007         return 0;               /* nothing to do! */
1008       /* let IMAP servers hold on to D flags */
1009       if (ctx->magic != M_IMAP)
1010       {
1011         for (i = 0; i < ctx->msgcount; i++)
1012           ctx->hdrs[i]->deleted = 0;
1013         ctx->deleted = 0;
1014       }
1015     }
1016     else if (ctx->last_tag && ctx->last_tag->deleted)
1017       ctx->last_tag = NULL;     /* reset last tagged msg now useless */
1018   }
1019
1020   /* really only for IMAP - imap_sync_mailbox results in a call to
1021    * mx_update_tables, so ctx->deleted is 0 when it comes back */
1022   msgcount = ctx->msgcount;
1023   deleted = ctx->deleted;
1024
1025   if (purge && ctx->deleted) {
1026     if (trash_append (ctx) == -1)
1027       return -1;
1028   }
1029
1030   if (ctx->magic == M_IMAP)
1031     rc = imap_sync_mailbox (ctx, purge, index_hint);
1032   else
1033     rc = sync_mailbox (ctx, index_hint);
1034   if (rc == 0) {
1035     if (ctx->magic == M_IMAP && !purge)
1036       mutt_message (_("Mailbox checkpointed."));
1037
1038     else
1039       mutt_message (_("%d kept, %d deleted."), msgcount - deleted, deleted);
1040
1041     mutt_sleep (0);
1042
1043     if (ctx->msgcount == ctx->deleted &&
1044         (ctx->magic == M_MBOX || ctx->magic == M_MMDF) &&
1045         !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY)) {
1046       unlink (ctx->path);
1047       mx_fastclose_mailbox (ctx);
1048       return 0;
1049     }
1050
1051     /* if we haven't deleted any messages, we don't need to resort */
1052     /* ... except for certain folder formats which need "unsorted" 
1053      * sort order in order to synchronize folders.
1054      * 
1055      * MH and maildir are safe.  mbox-style seems to need re-sorting,
1056      * at least with the new threading code.
1057      */
1058     if (purge || (ctx->magic != M_MAILDIR && ctx->magic != M_MH)) {
1059       /* IMAP does this automatically after handling EXPUNGE */
1060       if (ctx->magic != M_IMAP)
1061       {
1062         mx_update_tables (ctx, 1);
1063         mutt_sort_headers (ctx, 1);     /* rethread from scratch */
1064       }
1065     }
1066   }
1067
1068   return (rc);
1069 }
1070
1071 int mx_sync_mailbox (CONTEXT* ctx, int* index_hint) {
1072   int ret = _mx_sync_mailbox (ctx, index_hint);
1073   sidebar_set_buffystats (ctx);
1074   return (ret);
1075 }
1076
1077 /* args:
1078  *      dest    destintation mailbox
1079  *      hdr     message being copied (required for maildir support, because
1080  *              the filename depends on the message flags)
1081  */
1082 MESSAGE *mx_open_new_message (CONTEXT * dest, HEADER * hdr, int flags)
1083 {
1084   MESSAGE *msg;
1085   address_t *p = NULL;
1086
1087   if (!MX_IDX(dest->magic-1)) {
1088     debug_print (1, ("function unimplemented for mailbox type %d.\n", dest->magic));
1089     return (NULL);
1090   }
1091
1092   msg = p_new(MESSAGE, 1);
1093   msg->magic = dest->magic;
1094   msg->write = 1;
1095
1096   if (hdr) {
1097     msg->flags.flagged = hdr->flagged;
1098     msg->flags.replied = hdr->replied;
1099     msg->flags.read = hdr->read;
1100     msg->received = hdr->received;
1101   }
1102
1103   if (msg->received == 0)
1104     time (&msg->received);
1105
1106   if (MX_COMMAND(dest->magic-1,mx_open_new_message)(msg, dest, hdr) == 0) {
1107     if (dest->magic == M_MMDF)
1108       fputs (MMDF_SEP, msg->fp);
1109
1110     if ((msg->magic == M_MBOX || msg->magic == M_MMDF) && flags & M_ADD_FROM) {
1111       if (hdr) {
1112         if (hdr->env->return_path)
1113           p = hdr->env->return_path;
1114         else if (hdr->env->sender)
1115           p = hdr->env->sender;
1116         else
1117           p = hdr->env->from;
1118       }
1119
1120       fprintf (msg->fp, "From %s %s", p ? p->mailbox : NONULL (Username),
1121                ctime (&msg->received));
1122     }
1123   }
1124   else
1125     p_delete(&msg);
1126
1127   return msg;
1128 }
1129
1130 /* check for new mail */
1131 int mx_check_mailbox (CONTEXT * ctx, int *index_hint, int lock) {
1132   if (ctx->compressinfo)
1133     return mutt_check_mailbox_compressed (ctx);
1134
1135   if (ctx) {
1136     if (ctx->locked)
1137       lock = 0;
1138     if (MX_IDX(ctx->magic-1) && MX_COMMAND(ctx->magic-1,mx_check_mailbox))
1139       return (MX_COMMAND(ctx->magic-1,mx_check_mailbox)(ctx, index_hint, lock));
1140   }
1141
1142   debug_print (1, ("null or invalid context.\n"));
1143   return (-1);
1144
1145 }
1146
1147 /* return a stream pointer for a message */
1148 MESSAGE *mx_open_message (CONTEXT * ctx, int msgno)
1149 {
1150   MESSAGE *msg;
1151
1152   msg = p_new(MESSAGE, 1);
1153   switch (msg->magic = ctx->magic) {
1154   case M_MBOX:
1155   case M_MMDF:
1156     msg->fp = ctx->fp;
1157     break;
1158
1159   case M_MH:
1160   case M_MAILDIR:
1161     {
1162       HEADER *cur = ctx->hdrs[msgno];
1163       char path[_POSIX_PATH_MAX];
1164
1165       snprintf (path, sizeof (path), "%s/%s", ctx->path, cur->path);
1166
1167       if ((msg->fp = fopen (path, "r")) == NULL && errno == ENOENT &&
1168           ctx->magic == M_MAILDIR)
1169         msg->fp = maildir_open_find_message (ctx->path, cur->path);
1170
1171       if (msg->fp == NULL) {
1172         mutt_perror (path);
1173         debug_print (1, ("fopen: %s: %s (errno %d).\n", path, strerror (errno), errno));
1174         p_delete(&msg);
1175       }
1176     }
1177     break;
1178
1179   case M_IMAP:
1180     {
1181       if (imap_fetch_message (msg, ctx, msgno) != 0)
1182         p_delete(&msg);
1183       break;
1184     }
1185
1186   case M_POP:
1187     {
1188       if (pop_fetch_message (msg, ctx, msgno) != 0)
1189         p_delete(&msg);
1190       break;
1191     }
1192
1193 #ifdef USE_NNTP
1194   case M_NNTP:
1195     {
1196       if (nntp_fetch_message (msg, ctx, msgno) != 0)
1197         p_delete(&msg);
1198       break;
1199     }
1200 #endif /* USE_NNTP */
1201
1202   default:
1203     debug_print (1, ("function not implemented for mailbox type %d.\n", ctx->magic));
1204     p_delete(&msg);
1205     break;
1206   }
1207   return (msg);
1208 }
1209
1210 /* commit a message to a folder */
1211
1212 int mx_commit_message (MESSAGE * msg, CONTEXT * ctx) {
1213   if (!(msg->write && ctx->append)) {
1214     debug_print (1, ("msg->write = %d, ctx->append = %d\n", msg->write, ctx->append));
1215     return -1;
1216   }
1217   if (!ctx || !MX_IDX(ctx->magic-1) || !MX_COMMAND(ctx->magic-1,mx_commit_message))
1218     return (-1);
1219   return (MX_COMMAND(ctx->magic-1,mx_commit_message) (msg, ctx));
1220 }
1221
1222 /* close a pointer to a message */
1223 int mx_close_message (MESSAGE ** msg)
1224 {
1225   int r = 0;
1226
1227   if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR
1228       || (*msg)->magic == M_IMAP
1229       || (*msg)->magic == M_POP
1230 #ifdef USE_NNTP
1231       || (*msg)->magic == M_NNTP
1232 #endif
1233     ) {
1234     r = safe_fclose (&(*msg)->fp);
1235   }
1236   else
1237     (*msg)->fp = NULL;
1238
1239   if ((*msg)->path) {
1240     debug_print (1, ("unlinking %s\n", (*msg)->path));
1241     unlink ((*msg)->path);
1242     p_delete(&(*msg)->path);
1243   }
1244
1245   p_delete(msg);
1246   return (r);
1247 }
1248
1249 void mx_alloc_memory (CONTEXT * ctx)
1250 {
1251   int i;
1252   size_t s = MAX (sizeof (HEADER *), sizeof (int));
1253
1254   if ((ctx->hdrmax + 25) * s < ctx->hdrmax * s) {
1255     mutt_error _("Integer overflow -- can't allocate memory.");
1256
1257     sleep (1);
1258     mutt_exit (1);
1259   }
1260
1261   if (ctx->hdrs) {
1262     p_realloc(&ctx->hdrs, ctx->hdrmax += 25);
1263     p_realloc(&ctx->v2r, ctx->hdrmax);
1264   }
1265   else {
1266     ctx->hdrs = p_new(HEADER *, (ctx->hdrmax += 25));
1267     ctx->v2r = p_new(int, ctx->hdrmax);
1268   }
1269   for (i = ctx->msgcount; i < ctx->hdrmax; i++) {
1270     ctx->hdrs[i] = NULL;
1271     ctx->v2r[i] = -1;
1272   }
1273 }
1274
1275 /* this routine is called to update the counts in the context structure for
1276  * the last message header parsed.
1277  */
1278 void mx_update_context (CONTEXT * ctx, int new_messages)
1279 {
1280   HEADER *h;
1281   int msgno;
1282
1283   for (msgno = ctx->msgcount - new_messages; msgno < ctx->msgcount; msgno++) {
1284     h = ctx->hdrs[msgno];
1285
1286     /* NOTE: this _must_ be done before the check for mailcap! */
1287     h->security = crypt_query (h->content);
1288
1289     if (!ctx->pattern) {
1290       ctx->v2r[ctx->vcount] = msgno;
1291       h->virtual = ctx->vcount++;
1292     }
1293     else
1294       h->virtual = -1;
1295     h->msgno = msgno;
1296
1297     if (h->env->supersedes) {
1298       HEADER *h2;
1299
1300       if (!ctx->id_hash)
1301         ctx->id_hash = mutt_make_id_hash (ctx);
1302
1303       h2 = hash_find (ctx->id_hash, h->env->supersedes);
1304
1305       /* p_delete(&h->env->supersedes); should I ? */
1306       if (h2) {
1307         h2->superseded = 1;
1308         if (!ctx->counting && option (OPTSCORE))
1309           mutt_score_message (ctx, h2, 1);
1310       }
1311     }
1312
1313     /* add this message to the hash tables */
1314     if (ctx->id_hash && h->env->message_id)
1315       hash_insert (ctx->id_hash, h->env->message_id, h, 0);
1316     if (!ctx->counting) {
1317       if (ctx->subj_hash && h->env->real_subj)
1318         hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
1319
1320       if (option (OPTSCORE))
1321         mutt_score_message (ctx, h, 0);
1322     }
1323
1324     if (h->changed)
1325       ctx->changed = 1;
1326     if (h->flagged)
1327       ctx->flagged++;
1328     if (h->deleted)
1329       ctx->deleted++;
1330     if (!h->read) {
1331       ctx->unread++;
1332       if (!h->old)
1333         ctx->new++;
1334     }
1335   }
1336   /* update sidebar count */
1337   sidebar_set_buffystats (ctx);
1338 }
1339
1340 /*
1341  * Return:
1342  * 1 if the specified mailbox contains 0 messages.
1343  * 0 if the mailbox contains messages
1344  * -1 on error
1345  */
1346 int mx_check_empty (const char *path)
1347 {
1348   int i = 0;
1349   if ((i = mx_get_idx (path)) >= 0 && MX_COMMAND(i,mx_check_empty))
1350     return (MX_COMMAND(i,mx_check_empty)(path));
1351   errno = EINVAL;
1352   return (-1);
1353 }
1354
1355 int mx_acl_check (CONTEXT* ctx, int flag) {
1356   if (!ctx || !MX_IDX(ctx->magic-1))
1357     return (0);
1358   /* if no acl_check defined for module, assume permission is granted */
1359   if (!MX_COMMAND(ctx->magic-1,mx_acl_check))
1360     return (1);
1361   return (MX_COMMAND(ctx->magic-1,mx_acl_check)(ctx,flag));
1362 }
1363
1364 void mx_init (void) {
1365 #ifdef DEBUG
1366   int i = 0;
1367 #endif
1368   list_push_back (&MailboxFormats, (void*) mbox_reg_mx ());
1369   list_push_back (&MailboxFormats, (void*) mmdf_reg_mx ());
1370   list_push_back (&MailboxFormats, (void*) mh_reg_mx ());
1371   list_push_back (&MailboxFormats, (void*) maildir_reg_mx ());
1372   list_push_back (&MailboxFormats, (void*) imap_reg_mx ());
1373   list_push_back (&MailboxFormats, (void*) pop_reg_mx ());
1374 #ifdef USE_NNTP
1375   list_push_back (&MailboxFormats, (void*) nntp_reg_mx ());
1376 #endif
1377   list_push_back (&MailboxFormats, (void*) compress_reg_mx ());
1378 #ifdef DEBUG
1379   /* check module registration for completeness with debug versions */
1380 #define EXITWITHERR(m) do { fprintf(stderr, "error: incomplete mx module: %s is missing for type %i\n",m,i);exit(1); } while (0)
1381   for (i = 0; i < MailboxFormats->length; i++) {
1382     if (MX_COMMAND(i,type) < 1)         EXITWITHERR("type");
1383     if (!MX_COMMAND(i,mx_is_magic))     EXITWITHERR("mx_is_magic");
1384     if (!MX_COMMAND(i,mx_open_mailbox)) EXITWITHERR("mx_open_mailbox");
1385 /*    if (!MX_COMMAND(i,mx_sync_mailbox)) EXITWITHERR("mx_sync_mailbox");*/
1386   }
1387 #undef EXITWITHERR
1388 #endif /* DEBUG */
1389 }
1390
1391 int mx_rebuild_cache (void) {
1392 #ifndef USE_HCACHE
1393   mutt_error (_("Support for header caching was not build in."));
1394   return (1);
1395 #else
1396   int i = 0, magic = 0;
1397   CONTEXT* ctx = NULL;
1398   BUFFY* b = NULL;
1399
1400   if (list_empty(Incoming)) {
1401     mutt_error (_("No mailboxes defined."));
1402     return (1);
1403   }
1404
1405   for (i = 0; i < Incoming->length; i++) {
1406     b = (BUFFY*) Incoming->data[i];
1407     magic = mx_get_magic (b->path);
1408     if (magic != M_MAILDIR && magic != M_MH && magic != M_IMAP)
1409       continue;
1410     sidebar_set_current (b->path);
1411     sidebar_draw (CurrentMenu);
1412     if ((ctx = mx_open_mailbox (b->path,
1413                                 M_READONLY | M_NOSORT | M_COUNT,
1414                                 NULL)) != NULL)
1415       mx_close_mailbox (ctx, 0);
1416   }
1417   mutt_clear_error ();
1418
1419   if (Context && Context->path)
1420     sidebar_set_current (Context->path);
1421   sidebar_draw (CurrentMenu);
1422
1423   return (0);
1424 #endif
1425 }
1426
1427 void mutt_parse_mime_message (CONTEXT * ctx, HEADER * cur)
1428 {
1429   MESSAGE *msg;
1430   int flags = 0;
1431
1432   do {
1433     if (cur->content->type != TYPEMESSAGE
1434         && cur->content->type != TYPEMULTIPART)
1435       break;                     /* nothing to do */
1436
1437     if (cur->content->parts)
1438       break;                     /* The message was parsed earlier. */
1439
1440     if ((msg = mx_open_message (ctx, cur->msgno))) {
1441       mutt_parse_part (msg->fp, cur->content);
1442
1443       cur->security = crypt_query (cur->content);
1444
1445       mx_close_message (&msg);
1446     }
1447   } while (0);
1448   mutt_count_body_parts (cur, flags | M_PARTS_RECOUNT);
1449 }