05c84416893650793f9ba5595b0a633fde9177d8
[apps/madmutt.git] / curs_main.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * Parts were written/modified by:
6  * Nico Golde <nico@ngolde.de>
7  *
8  * This file is part of mutt-ng, see http://www.muttng.org/.
9  * It's licensed under the GNU General Public License,
10  * please see the file GPL in the top level source directory.
11  */
12
13 #if HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include "mutt.h"
18 #include "mutt_curses.h"
19 #include "mx.h"
20 #include "mutt_menu.h"
21 #include "mapping.h"
22 #include "sort.h"
23 #include "recvattach.h"
24 #include "buffy.h"
25 #include "mx.h"
26 #include "sidebar.h"
27 #include "thread.h"
28 #include "xterm.h"
29
30 #ifdef USE_POP
31 #include "pop.h"
32 #endif
33
34 #ifdef USE_IMAP
35 #include "imap_private.h"
36 #endif
37
38 #include "mutt_crypt.h"
39
40 #ifdef USE_NNTP
41 #include "nntp.h"
42 #endif
43
44 #ifdef USE_SASL
45 #include "mutt_sasl.h"
46 #endif
47
48 #include "lib/mem.h"
49 #include "lib/intl.h"
50 #include "lib/str.h"
51 #include "lib/debug.h"
52
53 #include <ctype.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <sys/wait.h>
57 #include <string.h>
58 #include <sys/stat.h>
59 #include <errno.h>
60
61 static const char *No_mailbox_is_open = N_("No mailbox is open.");
62 static const char *There_are_no_messages = N_("There are no messages.");
63 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
64 static const char *Function_not_permitted_in_attach_message_mode =
65 N_("Function not permitted in attach-message mode.");
66 static const char *No_visible = N_("No visible messages.");
67
68 #define CHECK_IN_MAILBOX if (!Context) \
69         { \
70                 mutt_flushinp (); \
71                 mutt_error (_(No_mailbox_is_open)); \
72                 break; \
73         }
74
75 #define CHECK_MSGCOUNT if (!Context) \
76         { \
77                 mutt_flushinp (); \
78                 mutt_error(_(No_mailbox_is_open)); \
79                 break; \
80         } \
81         else if (!Context->msgcount) \
82         { \
83                   mutt_flushinp (); \
84                 mutt_error(_(There_are_no_messages)); \
85                 break; \
86         }
87
88 #define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
89           {\
90                   mutt_flushinp (); \
91                   mutt_error(_(No_visible)); \
92                   break; \
93         }
94
95
96 #define CHECK_READONLY if (Context->readonly) \
97                         { \
98                                   mutt_flushinp (); \
99                                 mutt_error(_(Mailbox_is_read_only)); \
100                                 break; \
101                         }
102
103 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
104                      {\
105                         mutt_flushinp (); \
106                         mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
107                         break; \
108                      }
109
110 #define CURHDR Context->hdrs[Context->v2r[menu->current]]
111 #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
112 #define UNREAD(h) mutt_thread_contains_unread (Context, h)
113
114 extern const char *ReleaseDate;
115 extern size_t UngetCount;
116
117 void index_make_entry (char *s, size_t l, MUTTMENU * menu, int num)
118 {
119   format_flag flag =
120     M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR | M_FORMAT_INDEX;
121   int edgemsgno, reverse = Sort & SORT_REVERSE;
122   HEADER *h = Context->hdrs[Context->v2r[num]];
123   THREAD *tmp;
124
125   if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) {
126     flag |= M_FORMAT_TREE;      /* display the thread tree */
127     if (h->display_subject)
128       flag |= M_FORMAT_FORCESUBJ;
129     else {
130       if (reverse) {
131         if (menu->top + menu->pagelen > menu->max)
132           edgemsgno = Context->v2r[menu->max - 1];
133         else
134           edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
135       }
136       else
137         edgemsgno = Context->v2r[menu->top];
138
139       for (tmp = h->thread->parent; tmp; tmp = tmp->parent) {
140         if (!tmp->message)
141           continue;
142
143         /* if no ancestor is visible on current screen, provisionally force
144          * subject... */
145         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno <
146             edgemsgno) {
147           flag |= M_FORMAT_FORCESUBJ;
148           break;
149         }
150         else if (tmp->message->virtual >= 0)
151           break;
152       }
153       if (flag & M_FORMAT_FORCESUBJ) {
154         for (tmp = h->thread->prev; tmp; tmp = tmp->prev) {
155           if (!tmp->message)
156             continue;
157
158           /* ...but if a previous sibling is available, don't force it */
159           if (reverse ? tmp->message->msgno >
160               edgemsgno : tmp->message->msgno < edgemsgno)
161             break;
162           else if (tmp->message->virtual >= 0) {
163             flag &= ~M_FORMAT_FORCESUBJ;
164             break;
165           }
166         }
167       }
168     }
169   }
170
171   _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
172 }
173
174 int index_color (int index_no)
175 {
176   HEADER *h = Context->hdrs[Context->v2r[index_no]];
177
178   if (h && h->pair)
179     return h->pair;
180
181   mutt_set_header_color (Context, h);
182   return h->pair;
183 }
184
185 static int ci_next_undeleted (int msgno)
186 {
187   int i;
188
189   for (i = msgno + 1; i < Context->vcount; i++)
190     if (!Context->hdrs[Context->v2r[i]]->deleted)
191       return (i);
192   return (-1);
193 }
194
195 static int ci_previous_undeleted (int msgno)
196 {
197   int i;
198
199   for (i = msgno - 1; i >= 0; i--)
200     if (!Context->hdrs[Context->v2r[i]]->deleted)
201       return (i);
202   return (-1);
203 }
204
205 /* Return the index of the first new message, or failing that, the first
206  * unread message.
207  */
208 static int ci_first_message (void)
209 {
210   int old = -1, i;
211
212   if (Context && Context->msgcount) {
213     for (i = 0; i < Context->vcount; i++) {
214       if (!Context->hdrs[Context->v2r[i]]->read &&
215           !Context->hdrs[Context->v2r[i]]->deleted) {
216         if (!Context->hdrs[Context->v2r[i]]->old)
217           return (i);
218         else if (old == -1)
219           old = i;
220       }
221     }
222     if (old != -1)
223       return (old);
224
225     /* If Sort is reverse and not threaded, the latest message is first.
226      * If Sort is threaded, the latest message is first iff exactly one
227      * of Sort and SortAux are reverse.
228      */
229     if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
230         ((Sort & SORT_MASK) == SORT_THREADS &&
231          ((Sort ^ SortAux) & SORT_REVERSE)))
232       return 0;
233     else
234       return (Context->vcount ? Context->vcount - 1 : 0);
235   }
236   return 0;
237 }
238
239 /* This should be in mx.c, but it only gets used here. */
240 static int mx_toggle_write (CONTEXT * ctx)
241 {
242   if (!ctx)
243     return -1;
244
245   if (ctx->readonly) {
246     mutt_error (_("Cannot toggle write on a readonly mailbox!"));
247
248     return -1;
249   }
250
251   if (ctx->dontwrite) {
252     ctx->dontwrite = 0;
253     mutt_message (_("Changes to folder will be written on folder exit."));
254   }
255   else {
256     ctx->dontwrite = 1;
257     mutt_message (_("Changes to folder will not be written."));
258   }
259
260   return 0;
261 }
262
263 static void update_index (MUTTMENU * menu, CONTEXT * ctx, int check,
264                           int oldcount, int index_hint)
265 {
266   /* store pointers to the newly added messages */
267   HEADER **save_new = NULL;
268   int j;
269
270   /* take note of the current message */
271   if (oldcount) {
272     if (menu->current < Context->vcount)
273       menu->oldcurrent = index_hint;
274     else
275       oldcount = 0;             /* invalid message number! */
276   }
277
278   /* We are in a limited view. Check if the new message(s) satisfy
279    * the limit criteria. If they do, set their virtual msgno so that
280    * they will be visible in the limited view */
281   if (Context->pattern) {
282 #define THIS_BODY Context->hdrs[j]->content
283     for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount; j++) {
284       if (mutt_pattern_exec
285           (Context->limit_pattern, M_MATCH_FULL_ADDRESS, Context,
286             Context->hdrs[j])) {
287         Context->hdrs[j]->virtual = Context->vcount;
288         Context->v2r[Context->vcount] = j;
289         Context->hdrs[j]->limited = 1;
290         Context->vcount++;
291         Context->vsize +=
292           THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
293       }
294     }
295 #undef THIS_BODY
296   }
297
298   /* save the list of new messages */
299   if (oldcount && check != M_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) {
300     save_new =
301       (HEADER **) mem_malloc (sizeof (HEADER *) *
302                                (Context->msgcount - oldcount));
303     for (j = oldcount; j < Context->msgcount; j++)
304       save_new[j - oldcount] = Context->hdrs[j];
305   }
306
307   /* if the mailbox was reopened, need to rethread from scratch */
308   mutt_sort_headers (Context, (check == M_REOPENED));
309
310   /* uncollapse threads with new mail */
311   if ((Sort & SORT_MASK) == SORT_THREADS) {
312     if (check == M_REOPENED) {
313       THREAD *h, *j;
314
315       Context->collapsed = 0;
316
317       for (h = Context->tree; h; h = h->next) {
318         for (j = h; !j->message; j = j->child);
319         mutt_uncollapse_thread (Context, j->message);
320       }
321       mutt_set_virtual (Context);
322     }
323     else if (oldcount) {
324       for (j = 0; j < Context->msgcount - oldcount; j++) {
325         int k;
326
327         for (k = 0; k < Context->msgcount; k++) {
328           HEADER *h = Context->hdrs[k];
329
330           if (h == save_new[j] && (!Context->pattern || h->limited))
331             mutt_uncollapse_thread (Context, h);
332         }
333       }
334       mem_free (&save_new);
335       mutt_set_virtual (Context);
336     }
337   }
338
339   menu->current = -1;
340   if (oldcount) {
341     /* restore the current message to the message it was pointing to */
342     for (j = 0; j < Context->vcount; j++) {
343       if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent) {
344         menu->current = j;
345         break;
346       }
347     }
348   }
349
350   if (menu->current < 0)
351     menu->current = ci_first_message ();
352 }
353
354 static void resort_index (MUTTMENU * menu)
355 {
356   int i;
357   HEADER *current = CURHDR;
358
359   menu->current = -1;
360   mutt_sort_headers (Context, 0);
361   /* Restore the current message */
362
363   for (i = 0; i < Context->vcount; i++) {
364     if (Context->hdrs[Context->v2r[i]] == current) {
365       menu->current = i;
366       break;
367     }
368   }
369
370   if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
371     menu->current = mutt_parent_message (Context, current);
372
373   if (menu->current < 0)
374     menu->current = ci_first_message ();
375
376   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
377 }
378
379 struct mapping_t IndexHelp[] = {
380   {N_("Quit"), OP_QUIT},
381   {N_("Del"), OP_DELETE},
382   {N_("Undel"), OP_UNDELETE},
383   {N_("Save"), OP_SAVE},
384   {N_("Mail"), OP_MAIL},
385   {N_("Reply"), OP_REPLY},
386   {N_("Group"), OP_GROUP_REPLY},
387   {N_("Help"), OP_HELP},
388   {NULL}
389 };
390
391 #ifdef USE_NNTP
392 struct mapping_t IndexNewsHelp[] = {
393   {N_("Quit"), OP_QUIT},
394   {N_("Del"), OP_DELETE},
395   {N_("Undel"), OP_UNDELETE},
396   {N_("Save"), OP_SAVE},
397   {N_("Post"), OP_POST},
398   {N_("Followup"), OP_FOLLOWUP},
399   {N_("Catchup"), OP_CATCHUP},
400   {N_("Help"), OP_HELP},
401   {NULL}
402 };
403 #endif
404
405 /* This function handles the message index window as well as commands returned
406  * from the pager (MENU_PAGER).
407  */
408 int mutt_index_menu (void)
409 {
410   char buf[LONG_STRING], helpstr[SHORT_STRING];
411   int flags;
412   int op = OP_NULL;
413   int done = 0;                 /* controls when to exit the "event" loop */
414   int i = 0, j;
415   int tag = 0;                  /* has the tag-prefix command been pressed? */
416   int newcount = -1;
417   int oldcount = -1;
418   int rc = -1;
419   MUTTMENU *menu;
420   char *cp;                     /* temporary variable. */
421   int index_hint;               /* used to restore cursor position */
422   int do_buffy_notify = 1;
423   int close = 0;                /* did we OP_QUIT or OP_EXIT out of this menu? */
424   int attach_msg = option (OPTATTACHMSG);
425
426   menu = mutt_new_menu ();
427   menu->menu = MENU_MAIN;
428   menu->offset = 1;
429   menu->pagelen = LINES - 3;
430   menu->make_entry = index_make_entry;
431   menu->color = index_color;
432   menu->current = ci_first_message ();
433   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
434 #ifdef USE_NNTP
435                                   (Context
436                                    && (Context->magic ==
437                                        M_NNTP)) ? IndexNewsHelp :
438 #endif
439                                   IndexHelp);
440
441   if (!attach_msg) {
442     buffy_check (1);       /* force the buffy check after we enter the folder */
443     /* record folder we open to place sidebar indicator properly */
444     if (Context && Context->path)
445       sidebar_set_current (Context->path);
446   }
447
448   FOREVER {
449     tag = 0;                    /* clear the tag-prefix */
450
451     menu->max = Context ? Context->vcount : 0;
452     oldcount = Context ? Context->msgcount : 0;
453
454     /* check if we need to resort the index because just about
455      * any 'op' below could do mutt_enter_command(), either here or
456      * from any new menu launched, and change $sort/$sort_aux
457      */
458     if (option (OPTNEEDRESORT) && Context && Context->msgcount)
459       resort_index (menu);
460
461     if (option (OPTREDRAWTREE) && Context && Context->msgcount
462         && (Sort & SORT_MASK) == SORT_THREADS) {
463       mutt_draw_tree (Context);
464       menu->redraw |= REDRAW_STATUS;
465       unset_option (OPTREDRAWTREE);
466     }
467
468     if (Context && !attach_msg) {
469       int check;
470
471       /* check for new mail in the mailbox.  If nonzero, then something has
472        * changed about the file (either we got new mail or the file was
473        * modified underneath us.)
474        */
475
476 #ifdef USE_IMAP
477       imap_allow_reopen (Context);
478 #endif
479
480       index_hint = (Context->vcount && menu->current >= 0
481                     && menu->current < Context->vcount) ? CURHDR->index : 0;
482
483       if ((check = mx_check_mailbox (Context, &index_hint, 0)) < 0) {
484         if (!Context->path) {
485           /* fatal error occurred */
486           mem_free (&Context);
487           menu->redraw = REDRAW_FULL;
488         }
489         set_option (OPTSEARCHINVALID);
490       }
491       else if (check == M_NEW_MAIL || check == M_REOPENED || check == M_FLAGS) {
492         update_index (menu, Context, check, oldcount, index_hint);
493
494         /* notify the user of new mail */
495         if (check == M_REOPENED)
496           mutt_error (_
497                       ("Mailbox was externally modified.  Flags may be wrong."));
498         else if (check == M_NEW_MAIL) {
499           /* on new mail: redraw sidebar */
500           sidebar_draw (CurrentMenu);
501           mutt_message (_("New mail in this mailbox."));
502
503           if (option (OPTBEEPNEW))
504             beep ();
505         }
506         else if (check == M_FLAGS)
507           mutt_message (_("Mailbox was externally modified."));
508
509         /* avoid the message being overwritten by buffy */
510         do_buffy_notify = 0;
511
512         menu->redraw = REDRAW_FULL;
513         menu->max = Context->vcount;
514
515         set_option (OPTSEARCHINVALID);
516       }
517     }
518
519 #ifdef USE_IMAP
520     imap_keepalive ();
521     imap_disallow_reopen (Context);
522 #endif
523
524     if (!attach_msg) {
525       /* check for new mail in the incoming folders */
526       oldcount = newcount;
527       if ((newcount = buffy_check (0)) != oldcount) {
528         menu->redraw |= REDRAW_STATUS;
529         menu->redraw |= REDRAW_SIDEBAR;
530       }
531       if (do_buffy_notify) {
532         if (buffy_notify () && option (OPTBEEPNEW))
533           beep ();
534       }
535       else
536         do_buffy_notify = 1;
537     }
538
539     if (op != -1)
540       mutt_curs_set (0);
541     if (menu->redraw & REDRAW_SIDEBAR)
542       sidebar_draw (menu->menu);
543     if (menu->redraw & REDRAW_FULL) {
544       menu_redraw_full (menu);
545       sidebar_draw (menu->menu);
546       mutt_show_error ();
547     }
548
549     if (menu->menu == MENU_MAIN) {
550       if (Context && Context->hdrs && !(menu->current >= Context->vcount)) {
551         menu_check_recenter (menu);
552
553         if (menu->redraw & REDRAW_INDEX) {
554           menu_redraw_index (menu);
555           menu->redraw |= REDRAW_STATUS;
556         }
557         else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
558           menu_redraw_motion (menu);
559         else if (menu->redraw & REDRAW_CURRENT)
560           menu_redraw_current (menu);
561       }
562
563       if (menu->redraw & REDRAW_STATUS) {
564         DrawFullLine = 1;
565         menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
566         DrawFullLine = 0;
567         CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES - 2);
568         SETCOLOR (MT_COLOR_STATUS);
569         BKGDSET (MT_COLOR_STATUS);
570         mutt_paddstr (COLS, buf);
571         SETCOLOR (MT_COLOR_NORMAL);
572         BKGDSET (MT_COLOR_NORMAL);
573         sidebar_set_buffystats (Context);
574         menu->redraw &= ~REDRAW_STATUS;
575         if (option (OPTXTERMSETTITLES)) {
576           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermTitle));
577           mutt_xterm_set_title (buf);
578           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermIcon));
579           mutt_xterm_set_icon (buf);
580         }
581       }
582
583       menu->redraw = 0;
584       if (menu->current < menu->max)
585         menu->oldcurrent = menu->current;
586       else
587         menu->oldcurrent = -1;
588
589       if (option (OPTARROWCURSOR))
590         move (menu->current - menu->top + menu->offset, 2);
591        else if (option (OPTBRAILLEFRIENDLY))
592          move (menu->current - menu->top + menu->offset, 0);
593       else
594         move (menu->current - menu->top + menu->offset, COLS - 1);
595       mutt_refresh ();
596
597 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
598       if (SigWinch) {
599         mutt_flushinp ();
600         mutt_resize_screen ();
601         menu->redraw = REDRAW_FULL;
602         menu->menu = MENU_MAIN;
603         SigWinch = 0;
604         menu->top = 0;          /* so we scroll the right amount */
605         /*
606          * force a real complete redraw.  clrtobot() doesn't seem to be able
607          * to handle every case without this.
608          */
609         clearok (stdscr, TRUE);
610         continue;
611       }
612 #endif
613
614       op = km_dokey (MENU_MAIN);
615
616       debug_print (4, ("Got op %d\n", op));
617
618       if (op == -1)
619         continue;               /* either user abort or timeout */
620
621       mutt_curs_set (1);
622
623       /* special handling for the tag-prefix function */
624       if (op == OP_TAG_PREFIX) {
625         if (!Context) {
626           mutt_error (_("No mailbox is open."));
627
628           continue;
629         }
630
631         if (!Context->tagged) {
632           mutt_error (_("No tagged messages."));
633
634           continue;
635         }
636         tag = 1;
637
638         /* give visual indication that the next command is a tag- command */
639         mvaddstr (LINES - 1, 0, "tag-");
640         clrtoeol ();
641
642         /* get the real command */
643         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
644           /* abort tag sequence */
645           CLEARLINE (LINES - 1);
646           continue;
647         }
648       }
649       else if (option (OPTAUTOTAG) && Context && Context->tagged)
650         tag = 1;
651
652       if (op == OP_TAG_PREFIX_COND) {
653         if (!Context) {
654           mutt_error (_("No mailbox is open."));
655
656           continue;
657         }
658
659         if (!Context->tagged) {
660           event_t tmp;
661
662           while (UngetCount > 0) {
663             tmp = mutt_getch ();
664             if (tmp.op == OP_END_COND)
665               break;
666           }
667           mutt_message (_("Nothing to do."));
668
669           continue;
670         }
671         tag = 1;
672
673         /* give visual indication that the next command is a tag- command */
674         mvaddstr (LINES - 1, 0, "tag-");
675         clrtoeol ();
676
677         /* get the real command */
678         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
679           /* abort tag sequence */
680           CLEARLINE (LINES - 1);
681           continue;
682         }
683       }
684
685       mutt_clear_error ();
686     }
687     else {
688       if (menu->current < menu->max)
689         menu->oldcurrent = menu->current;
690       else
691         menu->oldcurrent = -1;
692
693       mutt_curs_set (1);        /* fallback from the pager */
694     }
695
696 #ifdef USE_NNTP
697     unset_option (OPTNEWS);     /* for any case */
698 #endif
699
700     switch (op) {
701
702       /* ----------------------------------------------------------------------
703        * movement commands
704        */
705
706     case OP_BOTTOM_PAGE:
707       menu_bottom_page (menu);
708       break;
709     case OP_FIRST_ENTRY:
710       menu_first_entry (menu);
711       break;
712     case OP_MIDDLE_PAGE:
713       menu_middle_page (menu);
714       break;
715     case OP_HALF_UP:
716       menu_half_up (menu);
717       break;
718     case OP_HALF_DOWN:
719       menu_half_down (menu);
720       break;
721     case OP_NEXT_LINE:
722       menu_next_line (menu);
723       break;
724     case OP_PREV_LINE:
725       menu_prev_line (menu);
726       break;
727     case OP_NEXT_PAGE:
728       menu_next_page (menu);
729       break;
730     case OP_PREV_PAGE:
731       menu_prev_page (menu);
732       break;
733     case OP_LAST_ENTRY:
734       menu_last_entry (menu);
735       break;
736     case OP_TOP_PAGE:
737       menu_top_page (menu);
738       break;
739     case OP_CURRENT_TOP:
740       menu_current_top (menu);
741       break;
742     case OP_CURRENT_MIDDLE:
743       menu_current_middle (menu);
744       break;
745     case OP_CURRENT_BOTTOM:
746       menu_current_bottom (menu);
747       break;
748
749 #ifdef USE_NNTP
750     case OP_GET_MESSAGE:
751     case OP_GET_PARENT:
752       CHECK_MSGCOUNT;
753       if (Context->magic == M_NNTP) {
754         HEADER *h;
755
756         if (op == OP_GET_MESSAGE) {
757           buf[0] = 0;
758           if (mutt_get_field (_("Enter Message-ID: "), buf, sizeof (buf), 0)
759               != 0 || !buf[0])
760             break;
761         }
762         else {
763           LIST *ref = CURHDR->env->references;
764
765           if (!ref) {
766             mutt_error (_("Article has no parent reference!"));
767
768             break;
769           }
770           strfcpy (buf, ref->data, sizeof (buf));
771         }
772         if (!Context->id_hash)
773           Context->id_hash = mutt_make_id_hash (Context);
774         if ((h = hash_find (Context->id_hash, buf))) {
775           if (h->virtual != -1) {
776             menu->current = h->virtual;
777             menu->redraw = REDRAW_MOTION_RESYNCH;
778           }
779           else if (h->collapsed) {
780             mutt_uncollapse_thread (Context, h);
781             mutt_set_virtual (Context);
782             menu->current = h->virtual;
783             menu->redraw = REDRAW_MOTION_RESYNCH;
784           }
785           else
786             mutt_error (_("Message not visible in limited view."));
787         }
788         else {
789           if (nntp_check_msgid (Context, buf) == 0) {
790             h = Context->hdrs[Context->msgcount - 1];
791             mutt_sort_headers (Context, 0);
792             menu->current = h->virtual;
793             menu->redraw = REDRAW_FULL;
794           }
795           else
796             mutt_error (_("Article %s not found on server"), buf);
797         }
798       }
799       break;
800
801     case OP_GET_CHILDREN:
802     case OP_RECONSTRUCT_THREAD:
803       CHECK_MSGCOUNT;
804       if (Context->magic == M_NNTP) {
805         HEADER *h;
806         int old = CURHDR->index, i;
807
808         if (!CURHDR->env->message_id) {
809           mutt_error (_("No Message-ID. Unable to perform operation"));
810
811           break;
812         }
813
814         if (!Context->id_hash)
815           Context->id_hash = mutt_make_id_hash (Context);
816         strfcpy (buf, CURHDR->env->message_id, sizeof (buf));
817
818         if (op == OP_RECONSTRUCT_THREAD) {
819           LIST *ref = CURHDR->env->references;
820
821           while (ref) {
822             nntp_check_msgid (Context, ref->data);
823             /* the last msgid in References is the root message */
824             if (!ref->next)
825               strfcpy (buf, ref->data, sizeof (buf));
826             ref = ref->next;
827           }
828         }
829         mutt_message (_("Check for children of message..."));
830
831         if (nntp_check_children (Context, buf) == 0) {
832           mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
833           h = hash_find (Context->id_hash, buf);
834           /* if the root message was retrieved, move to it */
835           if (h)
836             menu->current = h->virtual;
837           else                  /* try to restore old position */
838             for (i = 0; i < Context->msgcount; i++)
839               if (Context->hdrs[i]->index == old) {
840                 menu->current = Context->hdrs[i]->virtual;
841                 /* As an added courtesy, recenter the menu
842                  * with the current entry at the middle of the screen */
843                 menu_check_recenter (menu);
844                 menu_current_middle (menu);
845               }
846         }
847         menu->redraw = REDRAW_FULL;
848         mutt_clear_error ();
849       }
850       break;
851 #endif
852
853     case OP_JUMP:
854
855       CHECK_MSGCOUNT;
856       CHECK_VISIBLE;
857       if (isdigit (LastKey))
858         mutt_ungetch (LastKey, 0);
859       buf[0] = 0;
860       if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
861           || !buf[0])
862         break;
863
864       if (!isdigit ((unsigned char) buf[0])) {
865         mutt_error (_("Argument must be a message number."));
866
867         break;
868       }
869
870       i = atoi (buf);
871       if (i > 0 && i <= Context->msgcount) {
872         for (j = i - 1; j < Context->msgcount; j++) {
873           if (Context->hdrs[j]->virtual != -1)
874             break;
875         }
876         if (j >= Context->msgcount) {
877           for (j = i - 2; j >= 0; j--) {
878             if (Context->hdrs[j]->virtual != -1)
879               break;
880           }
881         }
882
883         if (j >= 0) {
884           menu->current = Context->hdrs[j]->virtual;
885           if (menu->menu == MENU_PAGER) {
886             op = OP_DISPLAY_MESSAGE;
887             continue;
888           }
889           else
890             menu->redraw = REDRAW_MOTION;
891         }
892         else
893           mutt_error (_("That message is not visible."));
894       }
895       else
896         mutt_error (_("Invalid message number."));
897
898       break;
899
900       /* --------------------------------------------------------------------
901        * `index' specific commands
902        */
903
904     case OP_MAIN_DELETE_PATTERN:
905
906       CHECK_MSGCOUNT;
907       CHECK_VISIBLE;
908       CHECK_READONLY;
909
910       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
911
912       CHECK_ATTACH;
913       mutt_pattern_func (M_DELETE, _("Delete messages matching: "));
914       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
915       break;
916
917 #ifdef USE_POP
918     case OP_MAIN_FETCH_MAIL:
919
920       CHECK_ATTACH;
921       pop_fetch_mail ();
922       menu->redraw = REDRAW_FULL;
923       break;
924 #endif /* USE_POP */
925
926     case OP_HELP:
927
928       mutt_help (MENU_MAIN);
929       menu->redraw = REDRAW_FULL;
930       break;
931
932     case OP_MAIN_SHOW_LIMIT:
933       CHECK_IN_MAILBOX;
934       if (!Context->pattern)
935         mutt_message (_("No limit pattern is in effect."));
936
937       else {
938         char buf[STRING];
939
940         /* i18n: ask for a limit to apply */
941         snprintf (buf, sizeof (buf), _("Limit: %s"), Context->pattern);
942         mutt_message ("%s", buf);
943       }
944       break;
945
946     case OP_MAIN_LIMIT:
947     case OP_TOGGLE_READ:
948
949       CHECK_IN_MAILBOX;
950       menu->oldcurrent = (Context->vcount && menu->current >= 0
951                           && menu->current <
952                           Context->vcount) ? CURHDR->index : -1;
953       if (op == OP_TOGGLE_READ) {
954         char buf[LONG_STRING];
955
956         if (!Context->pattern
957             || strncmp (Context->pattern, "!~R!~D~s", 8) != 0) {
958           snprintf (buf, sizeof (buf), "!~R!~D~s%s",
959                     Context->pattern ? Context->pattern : ".*");
960           set_option (OPTHIDEREAD);
961         }
962         else {
963           strfcpy (buf, Context->pattern + 8, sizeof (buf));
964           if (!*buf || strncmp (buf, ".*", 2) == 0)
965             snprintf (buf, sizeof (buf), "~A");
966           unset_option (OPTHIDEREAD);
967         }
968         mem_free (&Context->pattern);
969         Context->pattern = str_dup (buf);
970       }
971       if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
972           mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
973       {
974         if (menu->oldcurrent >= 0) {
975           /* try to find what used to be the current message */
976           menu->current = -1;
977           for (i = 0; i < Context->vcount; i++)
978             if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent) {
979               menu->current = i;
980               break;
981             }
982           if (menu->current < 0)
983             menu->current = 0;
984         }
985         else
986           menu->current = 0;
987         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
988         if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
989           mutt_draw_tree (Context);
990         menu->redraw = REDRAW_FULL;
991       }
992       break;
993
994     case OP_QUIT:
995
996       close = op;
997       if (attach_msg) {
998         done = 1;
999         break;
1000       }
1001
1002       if (query_quadoption (OPT_QUIT, _("Quit Mutt-ng?")) == M_YES) {
1003         int check;
1004
1005         oldcount = Context ? Context->msgcount : 0;
1006
1007         if (!Context
1008             || (check = mx_close_mailbox (Context, &index_hint)) == 0)
1009           done = 1;
1010         else {
1011           if (check == M_NEW_MAIL || check == M_REOPENED)
1012             update_index (menu, Context, check, oldcount, index_hint);
1013
1014           menu->redraw = REDRAW_FULL;   /* new mail arrived? */
1015           set_option (OPTSEARCHINVALID);
1016         }
1017       }
1018       break;
1019
1020     case OP_REDRAW:
1021
1022       clearok (stdscr, TRUE);
1023       menu->redraw = REDRAW_FULL;
1024       break;
1025
1026     case OP_SEARCH:
1027     case OP_SEARCH_REVERSE:
1028     case OP_SEARCH_NEXT:
1029     case OP_SEARCH_OPPOSITE:
1030
1031       CHECK_MSGCOUNT;
1032       CHECK_VISIBLE;
1033       if ((menu->current = mutt_search_command (menu->current, op)) == -1)
1034         menu->current = menu->oldcurrent;
1035       else
1036         menu->redraw = REDRAW_MOTION;
1037       break;
1038
1039     case OP_SORT:
1040     case OP_SORT_REVERSE:
1041
1042       if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0) {
1043         if (Context && Context->msgcount) {
1044           resort_index (menu);
1045           set_option (OPTSEARCHINVALID);
1046         }
1047       }
1048       break;
1049
1050     case OP_TAG:
1051
1052       CHECK_MSGCOUNT;
1053       CHECK_VISIBLE;
1054       if (tag && !option (OPTAUTOTAG)) {
1055         for (j = 0; j < Context->vcount; j++)
1056           mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
1057         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1058       }
1059       else {
1060         mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
1061         Context->last_tag = CURHDR->tagged ? CURHDR :
1062           ((Context->last_tag == CURHDR && !CURHDR->tagged)
1063            ? NULL : Context->last_tag);
1064         menu->redraw = REDRAW_STATUS;
1065         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
1066           menu->current++;
1067           menu->redraw |= REDRAW_MOTION_RESYNCH;
1068         }
1069         else
1070           menu->redraw |= REDRAW_CURRENT;
1071       }
1072       break;
1073
1074     case OP_MAIN_TAG_PATTERN:
1075
1076       CHECK_MSGCOUNT;
1077       CHECK_VISIBLE;
1078       mutt_pattern_func (M_TAG, _("Tag messages matching: "));
1079       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1080       break;
1081
1082     case OP_MAIN_UNDELETE_PATTERN:
1083
1084       CHECK_MSGCOUNT;
1085       CHECK_VISIBLE;
1086       CHECK_READONLY;
1087
1088       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
1089
1090       if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) ==
1091           0)
1092         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1093       break;
1094
1095     case OP_MAIN_UNTAG_PATTERN:
1096
1097       CHECK_MSGCOUNT;
1098       CHECK_VISIBLE;
1099       if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
1100         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1101       break;
1102
1103       /* --------------------------------------------------------------------
1104        * The following operations can be performed inside of the pager.
1105        */
1106
1107 #ifdef USE_IMAP
1108     case OP_MAIN_IMAP_FETCH:
1109       if (Context->magic == M_IMAP)
1110         imap_check_mailbox (Context, &index_hint, 1);
1111       break;
1112 #endif
1113
1114     case OP_MAIN_SYNC_FOLDER:
1115
1116       if (Context && !Context->msgcount)
1117         break;
1118
1119       CHECK_MSGCOUNT;
1120       CHECK_VISIBLE;
1121       CHECK_READONLY;
1122       {
1123         int oldvcount = Context->vcount;
1124         int oldcount = Context->msgcount;
1125         int dcount = 0;
1126         int check;
1127
1128         /* calculate the number of messages _above_ the cursor,
1129          * so we can keep the cursor on the current message
1130          */
1131         for (j = 0; j <= menu->current; j++) {
1132           if (Context->hdrs[Context->v2r[j]]->deleted)
1133             dcount++;
1134         }
1135
1136         if ((check = mx_sync_mailbox (Context, &index_hint)) == 0) {
1137           if (Context->vcount != oldvcount)
1138             menu->current -= dcount;
1139           set_option (OPTSEARCHINVALID);
1140         }
1141         else if (check == M_NEW_MAIL || check == M_REOPENED)
1142           update_index (menu, Context, check, oldcount, index_hint);
1143
1144         /* 
1145          * do a sanity check even if mx_sync_mailbox failed.
1146          */
1147
1148         if (menu->current < 0 || menu->current >= Context->vcount)
1149           menu->current = ci_first_message ();
1150       }
1151
1152       /* check for a fatal error, or all messages deleted */
1153       if (!Context->path)
1154         mem_free (&Context);
1155
1156       /* if we were in the pager, redisplay the message */
1157       if (menu->menu == MENU_PAGER) {
1158         op = OP_DISPLAY_MESSAGE;
1159         continue;
1160       }
1161       else
1162         menu->redraw = REDRAW_FULL;
1163       break;
1164
1165     case OP_SIDEBAR_OPEN:
1166     case OP_MAIN_CHANGE_FOLDER:
1167     case OP_MAIN_CHANGE_FOLDER_READONLY:
1168 #ifdef USE_NNTP
1169     case OP_MAIN_CHANGE_GROUP:
1170     case OP_MAIN_CHANGE_GROUP_READONLY:
1171 #endif
1172       if (attach_msg || option (OPTREADONLY) ||
1173 #ifdef USE_NNTP
1174           op == OP_MAIN_CHANGE_GROUP_READONLY ||
1175 #endif
1176           op == OP_MAIN_CHANGE_FOLDER_READONLY)
1177         flags = M_READONLY;
1178       else
1179         flags = 0;
1180
1181       if (flags)
1182         cp = _("Open mailbox in read-only mode");
1183       else
1184         cp = _("Open mailbox");
1185
1186       buf[0] = '\0';
1187 #ifdef USE_NNTP
1188       unset_option (OPTNEWS);
1189       if (op == OP_MAIN_CHANGE_GROUP || op == OP_MAIN_CHANGE_GROUP_READONLY) {
1190         set_option (OPTNEWS);
1191         if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
1192           break;
1193         if (flags)
1194           cp = _("Open newsgroup in read-only mode");
1195         else
1196           cp = _("Open newsgroup");
1197         nntp_buffy (buf, sizeof (buf));
1198       }
1199       else
1200 #endif
1201       {
1202         if (Context && Context->path)
1203           strncpy (buf, Context->path, sizeof (buf));
1204         if (op != OP_SIDEBAR_OPEN)
1205           buffy_next (buf, sizeof (buf));
1206       }
1207
1208       if (op == OP_SIDEBAR_OPEN) {
1209         strncpy (buf, NONULL(sidebar_get_current ()), sizeof (buf));
1210       }
1211       else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) {
1212         if (menu->menu == MENU_PAGER) {
1213           op = OP_DISPLAY_MESSAGE;
1214           continue;
1215         } else
1216           break;
1217       }
1218       if (!buf[0]) {
1219         CLEARLINE (LINES - 1);
1220         break;
1221       }
1222
1223 #ifdef USE_NNTP
1224       if (option (OPTNEWS)) {
1225         unset_option (OPTNEWS);
1226         nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
1227       }
1228       else
1229 #endif
1230         mutt_expand_path (buf, sizeof (buf));
1231       if (mx_get_magic (buf) <= 0) {
1232         mutt_error (_("%s is not a mailbox."), buf);
1233         break;
1234       }
1235       str_replace (&CurrentFolder, buf);
1236
1237       if (Context) {
1238         int check;
1239
1240 #ifdef USE_COMPRESSED
1241         if (Context->compressinfo && Context->realpath)
1242           str_replace (&LastFolder, Context->realpath);
1243         else
1244 #endif
1245
1246           str_replace (&LastFolder, Context->path);
1247         oldcount = Context ? Context->msgcount : 0;
1248
1249         if ((check = mx_close_mailbox (Context, &index_hint)) != 0) {
1250           if (check == M_NEW_MAIL || check == M_REOPENED)
1251             update_index (menu, Context, check, oldcount, index_hint);
1252
1253           set_option (OPTSEARCHINVALID);
1254           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1255           break;
1256         }
1257         mem_free (&Context);
1258       }
1259
1260       mutt_sleep (0);
1261
1262       /* Set CurrentMenu to MENU_MAIN before executing any folder
1263        * hooks so that all the index menu functions are available to
1264        * the exec command.
1265        */
1266
1267       CurrentMenu = MENU_MAIN;
1268       mutt_folder_hook (buf);
1269
1270       if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) {
1271         menu->current = ci_first_message ();
1272       }
1273       else
1274         menu->current = 0;
1275       sidebar_set_current (buf);
1276
1277 #ifdef USE_NNTP
1278       /* buffy_check() must be done with mail-reader mode! */
1279       menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
1280                                       (Context
1281                                        && (Context->magic ==
1282                                            M_NNTP)) ? IndexNewsHelp :
1283                                       IndexHelp);
1284 #endif
1285       mutt_clear_error ();
1286       buffy_check (1);     /* force the buffy check after we have changed
1287                                    the folder */
1288       menu->redraw = REDRAW_FULL;
1289       set_option (OPTSEARCHINVALID);
1290       break;
1291
1292     case OP_DISPLAY_MESSAGE:
1293     case OP_DISPLAY_HEADERS:   /* don't weed the headers */
1294
1295       CHECK_MSGCOUNT;
1296       CHECK_VISIBLE;
1297       /*
1298        * toggle the weeding of headers so that a user can press the key
1299        * again while reading the message.
1300        */
1301       if (op == OP_DISPLAY_HEADERS)
1302         toggle_option (OPTWEED);
1303
1304       unset_option (OPTNEEDRESORT);
1305
1306       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) {
1307         mutt_uncollapse_thread (Context, CURHDR);
1308         mutt_set_virtual (Context);
1309         if (option (OPTUNCOLLAPSEJUMP))
1310           menu->current = mutt_thread_next_unread (Context, CURHDR);
1311       }
1312
1313       if (option (OPTPGPAUTODEC) && 
1314           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
1315         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1316
1317       if ((op = mutt_display_message (CURHDR)) == -1) {
1318         unset_option (OPTNEEDRESORT);
1319         break;
1320       }
1321
1322       menu->menu = MENU_PAGER;
1323       menu->oldcurrent = menu->current;
1324       continue;
1325
1326     case OP_EXIT:
1327
1328       close = op;
1329       if (menu->menu == MENU_MAIN && attach_msg) {
1330         done = 1;
1331         break;
1332       }
1333
1334       if ((menu->menu == MENU_MAIN)
1335           && (query_quadoption (OPT_QUIT,
1336                                 _("Exit Mutt-ng without saving?")) == M_YES))
1337       {
1338         if (Context) {
1339           mx_fastclose_mailbox (Context);
1340           mem_free (&Context);
1341         }
1342         done = 1;
1343       }
1344       break;
1345
1346     case OP_EDIT_TYPE:
1347
1348       CHECK_MSGCOUNT;
1349       CHECK_VISIBLE;
1350       CHECK_ATTACH;
1351       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1352       /* if we were in the pager, redisplay the message */
1353       if (menu->menu == MENU_PAGER) {
1354         op = OP_DISPLAY_MESSAGE;
1355         continue;
1356       }
1357       else
1358         menu->redraw = REDRAW_CURRENT;
1359       break;
1360
1361     case OP_MAIN_BREAK_THREAD:
1362
1363       CHECK_MSGCOUNT;
1364       CHECK_VISIBLE;
1365       CHECK_READONLY;
1366
1367       if ((Sort & SORT_MASK) != SORT_THREADS)
1368         mutt_error (_("Threading is not enabled."));
1369
1370       else {
1371         {
1372           HEADER *oldcur = CURHDR;
1373
1374           mutt_break_thread (CURHDR);
1375           mutt_sort_headers (Context, 1);
1376           menu->current = oldcur->virtual;
1377         }
1378
1379         Context->changed = 1;
1380         mutt_message _("Thread broken");
1381
1382         if (menu->menu == MENU_PAGER) {
1383           op = OP_DISPLAY_MESSAGE;
1384           continue;
1385         }
1386         else
1387           menu->redraw |= REDRAW_INDEX;
1388       }
1389       break;
1390
1391     case OP_MAIN_LINK_THREADS:
1392
1393       CHECK_MSGCOUNT;
1394       CHECK_VISIBLE;
1395       CHECK_READONLY;
1396
1397       if ((Sort & SORT_MASK) != SORT_THREADS)
1398         mutt_error (_("Threading is not enabled."));
1399
1400       else if (!CURHDR->env->message_id)
1401         mutt_error (_("No Message-ID: header available to link thread"));
1402
1403       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1404         mutt_error (_("First, please tag a message to be linked here"));
1405
1406       else {
1407         HEADER *oldcur = CURHDR;
1408
1409         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1410                                Context)) {
1411           mutt_sort_headers (Context, 1);
1412           menu->current = oldcur->virtual;
1413
1414           Context->changed = 1;
1415           mutt_message _("Threads linked");
1416         }
1417         else
1418           mutt_error (_("No thread linked"));
1419       }
1420
1421       if (menu->menu == MENU_PAGER) {
1422         op = OP_DISPLAY_MESSAGE;
1423         continue;
1424       }
1425       else
1426         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1427       break;
1428
1429     case OP_MAIN_NEXT_UNDELETED:
1430
1431       CHECK_MSGCOUNT;
1432       CHECK_VISIBLE;
1433       if (menu->current >= Context->vcount - 1) {
1434         if (menu->menu == MENU_MAIN)
1435           mutt_error (_("You are on the last message."));
1436
1437         break;
1438       }
1439       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1440         menu->current = menu->oldcurrent;
1441         if (menu->menu == MENU_MAIN)
1442           mutt_error (_("No undeleted messages."));
1443       }
1444       else if (menu->menu == MENU_PAGER) {
1445         op = OP_DISPLAY_MESSAGE;
1446         continue;
1447       }
1448       else
1449         menu->redraw = REDRAW_MOTION;
1450       break;
1451
1452     case OP_NEXT_ENTRY:
1453
1454       CHECK_MSGCOUNT;
1455       CHECK_VISIBLE;
1456       if (menu->current >= Context->vcount - 1) {
1457         if (menu->menu == MENU_MAIN)
1458           mutt_error (_("You are on the last message."));
1459
1460         break;
1461       }
1462       menu->current++;
1463       if (menu->menu == MENU_PAGER) {
1464         op = OP_DISPLAY_MESSAGE;
1465         continue;
1466       }
1467       else
1468         menu->redraw = REDRAW_MOTION;
1469       break;
1470
1471     case OP_MAIN_PREV_UNDELETED:
1472
1473       CHECK_MSGCOUNT;
1474       CHECK_VISIBLE;
1475       if (menu->current < 1) {
1476         mutt_error (_("You are on the first message."));
1477
1478         break;
1479       }
1480       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1481         menu->current = menu->oldcurrent;
1482         if (menu->menu == MENU_MAIN)
1483           mutt_error (_("No undeleted messages."));
1484       }
1485       else if (menu->menu == MENU_PAGER) {
1486         op = OP_DISPLAY_MESSAGE;
1487         continue;
1488       }
1489       else
1490         menu->redraw = REDRAW_MOTION;
1491       break;
1492
1493     case OP_PREV_ENTRY:
1494
1495       CHECK_MSGCOUNT;
1496       CHECK_VISIBLE;
1497       if (menu->current < 1) {
1498         if (menu->menu == MENU_MAIN)
1499           mutt_error (_("You are on the first message."));
1500
1501         break;
1502       }
1503       menu->current--;
1504       if (menu->menu == MENU_PAGER) {
1505         op = OP_DISPLAY_MESSAGE;
1506         continue;
1507       }
1508       else
1509         menu->redraw = REDRAW_MOTION;
1510       break;
1511
1512     case OP_DECRYPT_COPY:
1513     case OP_DECRYPT_SAVE:
1514       if (!WithCrypto)
1515         break;
1516       /* fall thru */
1517     case OP_COPY_MESSAGE:
1518     case OP_SAVE:
1519     case OP_DECODE_COPY:
1520     case OP_DECODE_SAVE:
1521       CHECK_MSGCOUNT;
1522       CHECK_VISIBLE;
1523       if (mutt_save_message (tag ? NULL : CURHDR,
1524                              (op == OP_DECRYPT_SAVE) ||
1525                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1526                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1527                              (op == OP_DECRYPT_SAVE)
1528                              || (op == OP_DECRYPT_COPY)
1529                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1530                                                            || op ==
1531                                                            OP_DECODE_SAVE
1532                                                            || op ==
1533                                                            OP_DECRYPT_SAVE)
1534         ) {
1535         if (tag)
1536           menu->redraw |= REDRAW_INDEX;
1537         else if (option (OPTRESOLVE)) {
1538           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1539             menu->current = menu->oldcurrent;
1540             menu->redraw |= REDRAW_CURRENT;
1541           }
1542           else
1543             menu->redraw |= REDRAW_MOTION_RESYNCH;
1544         }
1545         else
1546           menu->redraw |= REDRAW_CURRENT;
1547       }
1548       break;
1549
1550     case OP_MAIN_NEXT_NEW:
1551     case OP_MAIN_NEXT_UNREAD:
1552     case OP_MAIN_PREV_NEW:
1553     case OP_MAIN_PREV_UNREAD:
1554     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1555     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1556
1557       {
1558         int first_unread = -1;
1559         int first_new = -1;
1560
1561         CHECK_MSGCOUNT;
1562         CHECK_VISIBLE;
1563
1564         i = menu->current;
1565         menu->current = -1;
1566         for (j = 0; j != Context->vcount; j++) {
1567 #define CURHDRi Context->hdrs[Context->v2r[i]]
1568           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1569               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1570             i++;
1571             if (i > Context->vcount - 1) {
1572               mutt_message _("Search wrapped to top.");
1573
1574               i = 0;
1575             }
1576           }
1577           else {
1578             i--;
1579             if (i < 0) {
1580               mutt_message _("Search wrapped to bottom.");
1581
1582               i = Context->vcount - 1;
1583             }
1584           }
1585
1586           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1587             if (UNREAD (CURHDRi) && first_unread == -1)
1588               first_unread = i;
1589             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1590               first_new = i;
1591           }
1592           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1593             if (first_unread == -1)
1594               first_unread = i;
1595             if ((!CURHDRi->old) && first_new == -1)
1596               first_new = i;
1597           }
1598
1599           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1600               first_unread != -1)
1601             break;
1602           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1603                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1604                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1605               && first_new != -1)
1606             break;
1607         }
1608 #undef CURHDRi
1609         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1610              op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1611              || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1612             && first_new != -1)
1613           menu->current = first_new;
1614         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1615                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1616                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1617                  && first_unread != -1)
1618           menu->current = first_unread;
1619
1620         if (menu->current == -1) {
1621           menu->current = menu->oldcurrent;
1622           mutt_error ("%s%s.",
1623                       (op == OP_MAIN_NEXT_NEW
1624                        || op ==
1625                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1626                       _("No unread messages"),
1627                       Context->pattern ? _(" in this limited view") : "");
1628         }
1629         else if (menu->menu == MENU_PAGER) {
1630           op = OP_DISPLAY_MESSAGE;
1631           continue;
1632         }
1633         else
1634           menu->redraw = REDRAW_MOTION;
1635         break;
1636       }
1637     case OP_FLAG_MESSAGE:
1638
1639       CHECK_MSGCOUNT;
1640       CHECK_VISIBLE;
1641       CHECK_READONLY;
1642
1643       CHECK_MX_ACL (Context, ACL_WRITE, _("Flagging"));
1644
1645       if (tag) {
1646         for (j = 0; j < Context->vcount; j++) {
1647           if (Context->hdrs[Context->v2r[j]]->tagged)
1648             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1649                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1650         }
1651
1652         menu->redraw |= REDRAW_INDEX;
1653       }
1654       else {
1655         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1656         if (option (OPTRESOLVE)) {
1657           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1658             menu->current = menu->oldcurrent;
1659             menu->redraw = REDRAW_CURRENT;
1660           }
1661           else
1662             menu->redraw = REDRAW_MOTION_RESYNCH;
1663         }
1664         else
1665           menu->redraw = REDRAW_CURRENT;
1666       }
1667       menu->redraw |= REDRAW_STATUS;
1668       break;
1669
1670     case OP_TOGGLE_NEW:
1671
1672       CHECK_MSGCOUNT;
1673       CHECK_VISIBLE;
1674       CHECK_READONLY;
1675
1676       CHECK_MX_ACL (Context, ACL_SEEN, _("Toggling"));
1677
1678       if (tag) {
1679         for (j = 0; j < Context->vcount; j++) {
1680           if (Context->hdrs[Context->v2r[j]]->tagged) {
1681             if (Context->hdrs[Context->v2r[j]]->read ||
1682                 Context->hdrs[Context->v2r[j]]->old)
1683               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1684                              1);
1685             else
1686               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1687                              1);
1688           }
1689         }
1690         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1691       }
1692       else {
1693         if (CURHDR->read || CURHDR->old)
1694           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1695         else
1696           mutt_set_flag (Context, CURHDR, M_READ, 1);
1697
1698         if (option (OPTRESOLVE)) {
1699           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1700             menu->current = menu->oldcurrent;
1701             menu->redraw = REDRAW_CURRENT;
1702           }
1703           else
1704             menu->redraw = REDRAW_MOTION_RESYNCH;
1705         }
1706         else
1707           menu->redraw = REDRAW_CURRENT;
1708         menu->redraw |= REDRAW_STATUS;
1709       }
1710       break;
1711
1712     case OP_TOGGLE_WRITE:
1713
1714       CHECK_IN_MAILBOX;
1715       if (mx_toggle_write (Context) == 0)
1716         menu->redraw |= REDRAW_STATUS;
1717       break;
1718
1719     case OP_MAIN_NEXT_THREAD:
1720     case OP_MAIN_NEXT_SUBTHREAD:
1721     case OP_MAIN_PREV_THREAD:
1722     case OP_MAIN_PREV_SUBTHREAD:
1723
1724       CHECK_MSGCOUNT;
1725       CHECK_VISIBLE;
1726       switch (op) {
1727       case OP_MAIN_NEXT_THREAD:
1728         menu->current = mutt_next_thread (CURHDR);
1729         break;
1730
1731       case OP_MAIN_NEXT_SUBTHREAD:
1732         menu->current = mutt_next_subthread (CURHDR);
1733         break;
1734
1735       case OP_MAIN_PREV_THREAD:
1736         menu->current = mutt_previous_thread (CURHDR);
1737         break;
1738
1739       case OP_MAIN_PREV_SUBTHREAD:
1740         menu->current = mutt_previous_subthread (CURHDR);
1741         break;
1742       }
1743
1744       if (menu->current < 0) {
1745         menu->current = menu->oldcurrent;
1746         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1747           mutt_error (_("No more threads."));
1748
1749         else
1750           mutt_error (_("You are on the first thread."));
1751       }
1752       else if (menu->menu == MENU_PAGER) {
1753         op = OP_DISPLAY_MESSAGE;
1754         continue;
1755       }
1756       else
1757         menu->redraw = REDRAW_MOTION;
1758       break;
1759
1760     case OP_MAIN_PARENT_MESSAGE:
1761
1762       CHECK_MSGCOUNT;
1763       CHECK_VISIBLE;
1764
1765       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1766         menu->current = menu->oldcurrent;
1767       }
1768       else if (menu->menu == MENU_PAGER) {
1769         op = OP_DISPLAY_MESSAGE;
1770         continue;
1771       }
1772       else
1773         menu->redraw = REDRAW_MOTION;
1774       break;
1775
1776     case OP_MAIN_SET_FLAG:
1777     case OP_MAIN_CLEAR_FLAG:
1778
1779       CHECK_MSGCOUNT;
1780       CHECK_VISIBLE;
1781       CHECK_READONLY;
1782
1783       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) ==
1784           0) {
1785         menu->redraw = REDRAW_STATUS;
1786         if (tag)
1787           menu->redraw |= REDRAW_INDEX;
1788         else if (option (OPTRESOLVE)) {
1789           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1790             menu->current = menu->oldcurrent;
1791             menu->redraw |= REDRAW_CURRENT;
1792           }
1793           else
1794             menu->redraw |= REDRAW_MOTION_RESYNCH;
1795         }
1796         else
1797           menu->redraw |= REDRAW_CURRENT;
1798       }
1799       break;
1800
1801     case OP_MAIN_COLLAPSE_THREAD:
1802       CHECK_MSGCOUNT;
1803       CHECK_VISIBLE;
1804
1805       if ((Sort & SORT_MASK) != SORT_THREADS) {
1806         mutt_error (_("Threading is not enabled."));
1807
1808         break;
1809       }
1810
1811       if (CURHDR->collapsed) {
1812         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1813         mutt_set_virtual (Context);
1814         if (option (OPTUNCOLLAPSEJUMP))
1815           menu->current = mutt_thread_next_unread (Context, CURHDR);
1816       }
1817       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1818         menu->current = mutt_collapse_thread (Context, CURHDR);
1819         mutt_set_virtual (Context);
1820       }
1821       else {
1822         mutt_error (_("Thread contains unread messages."));
1823
1824         break;
1825       }
1826
1827       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1828
1829       break;
1830
1831     case OP_MAIN_COLLAPSE_ALL:
1832       CHECK_MSGCOUNT;
1833       CHECK_VISIBLE;
1834
1835       if ((Sort & SORT_MASK) != SORT_THREADS) {
1836         mutt_error (_("Threading is not enabled."));
1837
1838         break;
1839       }
1840
1841       {
1842         HEADER *h, *base;
1843         THREAD *thread, *top;
1844         int final;
1845
1846         if (CURHDR->collapsed)
1847           final = mutt_uncollapse_thread (Context, CURHDR);
1848         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1849           final = mutt_collapse_thread (Context, CURHDR);
1850         else
1851           final = CURHDR->virtual;
1852
1853         base = Context->hdrs[Context->v2r[final]];
1854
1855         top = Context->tree;
1856         Context->collapsed = !Context->collapsed;
1857         while ((thread = top) != NULL) {
1858           while (!thread->message)
1859             thread = thread->child;
1860           h = thread->message;
1861
1862           if (h->collapsed != Context->collapsed) {
1863             if (h->collapsed)
1864               mutt_uncollapse_thread (Context, h);
1865             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1866               mutt_collapse_thread (Context, h);
1867           }
1868           top = top->next;
1869         }
1870
1871         mutt_set_virtual (Context);
1872         for (j = 0; j < Context->vcount; j++) {
1873           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1874             menu->current = j;
1875             break;
1876           }
1877         }
1878
1879         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1880       }
1881       break;
1882
1883       /* --------------------------------------------------------------------
1884        * These functions are invoked directly from the internal-pager
1885        */
1886
1887     case OP_BOUNCE_MESSAGE:
1888
1889       CHECK_ATTACH;
1890       CHECK_MSGCOUNT;
1891       CHECK_VISIBLE;
1892       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1893       break;
1894
1895     case OP_CREATE_ALIAS:
1896
1897       mutt_create_alias (Context
1898                          && Context->vcount ? CURHDR->env : NULL, NULL);
1899       MAYBE_REDRAW (menu->redraw);
1900       menu->redraw |= REDRAW_CURRENT;
1901       break;
1902
1903     case OP_QUERY:
1904       CHECK_ATTACH;
1905       mutt_query_menu (NULL, 0);
1906       MAYBE_REDRAW (menu->redraw);
1907       break;
1908
1909     case OP_PURGE_MESSAGE:
1910     case OP_DELETE:
1911
1912       CHECK_MSGCOUNT;
1913       CHECK_VISIBLE;
1914       CHECK_READONLY;
1915
1916       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1917
1918       if (tag) {
1919         mutt_tag_set_flag (M_DELETE, 1);
1920         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1921         if (option (OPTDELETEUNTAG))
1922           mutt_tag_set_flag (M_TAG, 0);
1923         menu->redraw = REDRAW_INDEX;
1924       }
1925       else {
1926         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1927         mutt_set_flag (Context, CURHDR, M_PURGED,
1928                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1929         if (option (OPTDELETEUNTAG))
1930           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1931         if (option (OPTRESOLVE)) {
1932           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1933             menu->current = menu->oldcurrent;
1934             menu->redraw = REDRAW_CURRENT;
1935           }
1936           else if (menu->menu == MENU_PAGER) {
1937             op = OP_DISPLAY_MESSAGE;
1938             continue;
1939           }
1940           else
1941             menu->redraw |= REDRAW_MOTION_RESYNCH;
1942         }
1943         else
1944           menu->redraw = REDRAW_CURRENT;
1945       }
1946       menu->redraw |= REDRAW_STATUS;
1947       break;
1948
1949     case OP_DELETE_THREAD:
1950     case OP_DELETE_SUBTHREAD:
1951
1952       CHECK_MSGCOUNT;
1953       CHECK_VISIBLE;
1954       CHECK_READONLY;
1955
1956       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1957
1958       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
1959                                  op == OP_DELETE_THREAD ? 0 : 1);
1960
1961       if (rc != -1) {
1962         if (option (OPTDELETEUNTAG))
1963           mutt_thread_set_flag (CURHDR, M_TAG, 0,
1964                                 op == OP_DELETE_THREAD ? 0 : 1);
1965         if (option (OPTRESOLVE))
1966           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1967             menu->current = menu->oldcurrent;
1968         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1969       }
1970       break;
1971
1972 #ifdef USE_NNTP
1973     case OP_CATCHUP:
1974       if (Context && Context->magic == M_NNTP) {
1975         if (mutt_newsgroup_catchup (CurrentNewsSrv,
1976                                     ((NNTP_DATA *) Context->data)->group))
1977           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1978       }
1979       break;
1980 #endif
1981
1982     case OP_DISPLAY_ADDRESS:
1983
1984       CHECK_MSGCOUNT;
1985       CHECK_VISIBLE;
1986       mutt_display_address (CURHDR->env);
1987       break;
1988
1989     case OP_ENTER_COMMAND:
1990
1991       CurrentMenu = MENU_MAIN;
1992       mutt_enter_command ();
1993       mutt_check_rescore (Context);
1994       if (option (OPTFORCEREDRAWINDEX))
1995         menu->redraw = REDRAW_FULL;
1996       unset_option (OPTFORCEREDRAWINDEX);
1997       unset_option (OPTFORCEREDRAWPAGER);
1998       break;
1999
2000     case OP_EDIT_MESSAGE:
2001
2002       CHECK_MSGCOUNT;
2003       CHECK_VISIBLE;
2004       CHECK_READONLY;
2005       CHECK_ATTACH;
2006
2007       CHECK_MX_ACL (Context, ACL_INSERT, _("Editing"));
2008
2009       if (option (OPTPGPAUTODEC)
2010           && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
2011         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2012       mutt_edit_message (Context, tag ? NULL : CURHDR);
2013       menu->redraw = REDRAW_FULL;
2014
2015       break;
2016
2017     case OP_FORWARD_MESSAGE:
2018
2019       CHECK_MSGCOUNT;
2020       CHECK_VISIBLE;
2021       CHECK_ATTACH;
2022
2023       if (option (OPTPGPAUTODEC) &&
2024           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2025         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2026       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
2027       menu->redraw = REDRAW_FULL;
2028       break;
2029
2030
2031     case OP_FORGET_PASSPHRASE:
2032       crypt_forget_passphrase ();
2033       break;
2034
2035     case OP_GROUP_REPLY:
2036
2037       CHECK_MSGCOUNT;
2038       CHECK_VISIBLE;
2039       CHECK_ATTACH;
2040
2041       if (option (OPTPGPAUTODEC) &&
2042           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2043         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2044
2045       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
2046                        tag ? NULL : CURHDR);
2047       menu->redraw = REDRAW_FULL;
2048       break;
2049
2050     case OP_LIST_REPLY:
2051
2052       CHECK_ATTACH;
2053       CHECK_MSGCOUNT;
2054       CHECK_VISIBLE;
2055
2056       if (option (OPTPGPAUTODEC) &&
2057           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2058         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2059
2060       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
2061                        tag ? NULL : CURHDR);
2062       menu->redraw = REDRAW_FULL;
2063       break;
2064
2065     case OP_MAIL:
2066
2067       CHECK_ATTACH;
2068       ci_send_message (0, NULL, NULL, Context, NULL);
2069       menu->redraw = REDRAW_FULL;
2070       break;
2071
2072     case OP_MAIL_KEY:
2073       if (!(WithCrypto & APPLICATION_PGP))
2074         break;
2075       CHECK_ATTACH;
2076       ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
2077       menu->redraw = REDRAW_FULL;
2078       break;
2079
2080
2081     case OP_EXTRACT_KEYS:
2082       if (!WithCrypto)
2083         break;
2084       CHECK_MSGCOUNT;
2085       CHECK_VISIBLE;
2086       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
2087       menu->redraw = REDRAW_FULL;
2088       break;
2089
2090
2091     case OP_CHECK_TRADITIONAL:
2092       if (!(WithCrypto & APPLICATION_PGP))
2093         break;
2094       CHECK_MSGCOUNT;
2095       CHECK_VISIBLE;
2096       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
2097         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2098
2099       if (menu->menu == MENU_PAGER) {
2100         op = OP_DISPLAY_MESSAGE;
2101         continue;
2102       }
2103       break;
2104
2105     case OP_PIPE:
2106
2107       CHECK_MSGCOUNT;
2108       CHECK_VISIBLE;
2109       mutt_pipe_message (tag ? NULL : CURHDR);
2110 #ifdef USE_IMAP
2111       /* in an IMAP folder index with imap_peek=no, piping could change
2112        * * new or old messages status to read. Redraw what's needed.
2113        */
2114       if (Context->magic == M_IMAP && !option (OPTIMAPPEEK)) {
2115         menu->redraw = (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS;
2116       }
2117 #endif
2118       MAYBE_REDRAW (menu->redraw);
2119       break;
2120
2121     case OP_PRINT:
2122
2123       CHECK_MSGCOUNT;
2124       CHECK_VISIBLE;
2125       mutt_print_message (tag ? NULL : CURHDR);
2126 #ifdef USE_IMAP
2127       /* in an IMAP folder index with imap_peek=no, piping could change
2128        * * new or old messages status to read. Redraw what's needed.
2129        */
2130       if (Context->magic == M_IMAP && !option (OPTIMAPPEEK)) {
2131         menu->redraw = (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS;
2132       }
2133 #endif
2134       break;
2135
2136     case OP_MAIN_READ_THREAD:
2137     case OP_MAIN_READ_SUBTHREAD:
2138
2139       CHECK_MSGCOUNT;
2140       CHECK_VISIBLE;
2141       CHECK_READONLY;
2142
2143       CHECK_MX_ACL (Context, ACL_SEEN, _("Marking as read"));
2144
2145       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2146                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
2147
2148       if (rc != -1) {
2149         if (option (OPTRESOLVE)) {
2150           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
2151                                 mutt_next_thread (CURHDR) :
2152                                 mutt_next_subthread (CURHDR))) == -1)
2153             menu->current = menu->oldcurrent;
2154         }
2155         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2156       }
2157       break;
2158
2159     case OP_RECALL_MESSAGE:
2160
2161       CHECK_ATTACH;
2162       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2163       menu->redraw = REDRAW_FULL;
2164       break;
2165
2166     case OP_RESEND:
2167
2168       CHECK_ATTACH;
2169       CHECK_MSGCOUNT;
2170       CHECK_VISIBLE;
2171
2172       if (tag) {
2173         for (j = 0; j < Context->vcount; j++) {
2174           if (Context->hdrs[Context->v2r[j]]->tagged)
2175             mutt_resend_message (NULL, Context,
2176                                  Context->hdrs[Context->v2r[j]]);
2177         }
2178       }
2179       else
2180         mutt_resend_message (NULL, Context, CURHDR);
2181
2182       menu->redraw = REDRAW_FULL;
2183       break;
2184
2185 #ifdef USE_NNTP
2186     case OP_POST:
2187     case OP_FOLLOWUP:
2188     case OP_FORWARD_TO_GROUP:
2189
2190       CHECK_ATTACH;
2191       if ((op == OP_FOLLOWUP || op == OP_FORWARD_TO_GROUP) &&
2192           Context && Context->msgcount == 0) {
2193         mutt_error (_("There are no messages."));
2194         sleep (2);
2195       }
2196       else if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2197                str_casecmp (CURHDR->env->followup_to, "poster") ||
2198                query_quadoption (OPT_FOLLOWUPTOPOSTER,
2199                                  _("Reply by mail as poster prefers?")) !=
2200                M_YES) {
2201         if (Context && Context->magic == M_NNTP
2202             && !((NNTP_DATA *) Context->data)->allowed
2203             && query_quadoption (OPT_TOMODERATED,
2204                                  _
2205                                  ("Posting to this group not allowed, may be moderated. Continue?"))
2206             != M_YES)
2207           break;
2208         if (op == OP_POST)
2209           ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2210         else {
2211           CHECK_MSGCOUNT;
2212           if (op == OP_FOLLOWUP)
2213             ci_send_message (SENDNEWS | SENDREPLY, NULL, NULL, Context,
2214                              tag ? NULL : CURHDR);
2215           else
2216             ci_send_message (SENDNEWS | SENDFORWARD, NULL, NULL, Context,
2217                              tag ? NULL : CURHDR);
2218         }
2219         menu->redraw = REDRAW_FULL;
2220         break;
2221       }
2222 #endif
2223
2224     case OP_REPLY:
2225
2226       CHECK_ATTACH;
2227       CHECK_MSGCOUNT;
2228       CHECK_VISIBLE;
2229
2230       if (option (OPTPGPAUTODEC) &&
2231           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2232         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2233
2234       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2235       menu->redraw = REDRAW_FULL;
2236       break;
2237
2238     case OP_SHELL_ESCAPE:
2239
2240       mutt_shell_escape ();
2241       MAYBE_REDRAW (menu->redraw);
2242       break;
2243
2244     case OP_TAG_THREAD:
2245     case OP_TAG_SUBTHREAD:
2246
2247       CHECK_MSGCOUNT;
2248       CHECK_VISIBLE;
2249       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2250                                  op == OP_TAG_THREAD ? 0 : 1);
2251
2252       if (rc != -1) {
2253         if (option (OPTRESOLVE)) {
2254           menu->current = mutt_next_thread (CURHDR);
2255
2256           if (menu->current == -1)
2257             menu->current = menu->oldcurrent;
2258         }
2259         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2260       }
2261       break;
2262
2263     case OP_UNDELETE:
2264
2265       CHECK_MSGCOUNT;
2266       CHECK_VISIBLE;
2267       CHECK_READONLY;
2268
2269       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2270
2271       if (tag) {
2272         mutt_tag_set_flag (M_DELETE, 0);
2273         mutt_tag_set_flag (M_PURGED, 0);
2274         menu->redraw = REDRAW_INDEX;
2275       }
2276       else {
2277         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2278         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2279         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
2280           menu->current++;
2281           menu->redraw = REDRAW_MOTION_RESYNCH;
2282         }
2283         else
2284           menu->redraw = REDRAW_CURRENT;
2285       }
2286       menu->redraw |= REDRAW_STATUS;
2287       break;
2288
2289     case OP_UNDELETE_THREAD:
2290     case OP_UNDELETE_SUBTHREAD:
2291
2292       CHECK_MSGCOUNT;
2293       CHECK_VISIBLE;
2294       CHECK_READONLY;
2295
2296       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2297
2298       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2299                                  op == OP_UNDELETE_THREAD ? 0 : 1)
2300         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2301                                 op == OP_UNDELETE_THREAD ? 0 : 1);
2302
2303       if (rc > -1) {
2304         if (option (OPTRESOLVE)) {
2305           if (op == OP_UNDELETE_THREAD)
2306             menu->current = mutt_next_thread (CURHDR);
2307           else
2308             menu->current = mutt_next_subthread (CURHDR);
2309
2310           if (menu->current == -1)
2311             menu->current = menu->oldcurrent;
2312         }
2313         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2314       }
2315       break;
2316
2317     case OP_VERSION:
2318       mutt_version ();
2319       break;
2320
2321     case OP_BUFFY_LIST:
2322       if (option (OPTFORCEBUFFYCHECK))
2323         buffy_check (1);
2324       buffy_list ();
2325       menu->redraw = REDRAW_FULL;
2326       break;
2327
2328     case OP_VIEW_ATTACHMENTS:
2329       CHECK_MSGCOUNT;
2330       CHECK_VISIBLE;
2331       mutt_view_attachments (CURHDR);
2332       if (CURHDR->attach_del)
2333         Context->changed = 1;
2334       menu->redraw = REDRAW_FULL;
2335       break;
2336
2337     case OP_END_COND:
2338       break;
2339
2340     case OP_WHAT_KEY:
2341       mutt_what_key ();
2342       break;
2343
2344     case OP_SIDEBAR_SCROLL_UP:
2345     case OP_SIDEBAR_SCROLL_DOWN:
2346     case OP_SIDEBAR_NEXT:
2347     case OP_SIDEBAR_PREV:
2348     case OP_SIDEBAR_NEXT_NEW:
2349     case OP_SIDEBAR_PREV_NEW:
2350       sidebar_scroll (op, menu->menu);
2351       break;
2352     default:
2353       if (menu->menu == MENU_MAIN)
2354         km_error_key (MENU_MAIN);
2355     }
2356
2357     if (menu->menu == MENU_PAGER) {
2358       menu->menu = MENU_MAIN;
2359       menu->redraw = REDRAW_FULL;
2360 #if 0
2361       set_option (OPTWEED);     /* turn header weeding back on. */
2362 #endif
2363     }
2364
2365     if (done)
2366       break;
2367   }
2368
2369   if (!attach_msg) {
2370 #ifdef USE_IMAP
2371   /* Close all open IMAP connections */
2372     imap_logout_all ();
2373 #endif
2374 #ifdef USE_NNTP
2375   /* Close all open NNTP connections */
2376     nntp_logout_all ();
2377 #endif
2378 #ifdef USE_SASL
2379     mutt_sasl_done ();
2380 #endif
2381   }
2382
2383   mutt_menuDestroy (&menu);
2384   return (close);
2385 }
2386
2387 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2388 {
2389   COLOR_LINE *color;
2390
2391   if (!curhdr)
2392     return;
2393
2394   for (color = ColorIndexList; color; color = color->next)
2395     if (mutt_pattern_exec
2396         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2397       curhdr->pair = color->pair;
2398       return;
2399     }
2400   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2401 }