less horrible strncpy's
[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 <lib-lib/mem.h>
18 #include <lib-lib/str.h>
19 #include <lib-lib/macros.h>
20
21 #include "mutt.h"
22 #include "mutt_curses.h"
23 #include "mx.h"
24 #include "mutt_menu.h"
25 #include "mapping.h"
26 #include "sort.h"
27 #include "recvattach.h"
28 #include "buffy.h"
29 #include "mx.h"
30 #include "sidebar.h"
31 #include "thread.h"
32 #include "xterm.h"
33
34 #ifdef USE_POP
35 #include "pop.h"
36 #endif
37
38 #ifdef USE_IMAP
39 #include "imap_private.h"
40 #endif
41
42 #include "mutt_crypt.h"
43
44 #ifdef USE_NNTP
45 #include "nntp.h"
46 #endif
47
48 #ifdef USE_SASL
49 #include "mutt_sasl.h"
50 #endif
51
52 #include "lib/debug.h"
53
54 #include <ctype.h>
55 #include <stdlib.h>
56 #include <unistd.h>
57 #include <sys/wait.h>
58 #include <string.h>
59 #include <sys/stat.h>
60 #include <errno.h>
61
62 static const char *No_mailbox_is_open = N_("No mailbox is open.");
63 static const char *There_are_no_messages = N_("There are no messages.");
64 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
65 static const char *Function_not_permitted_in_attach_message_mode =
66 N_("Function not permitted in attach-message mode.");
67 static const char *No_visible = N_("No visible messages.");
68
69 #define CHECK_IN_MAILBOX if (!Context) \
70         { \
71                 mutt_flushinp (); \
72                 mutt_error (_(No_mailbox_is_open)); \
73                 break; \
74         }
75
76 #define CHECK_MSGCOUNT if (!Context) \
77         { \
78                 mutt_flushinp (); \
79                 mutt_error(_(No_mailbox_is_open)); \
80                 break; \
81         } \
82         else if (!Context->msgcount) \
83         { \
84                   mutt_flushinp (); \
85                 mutt_error(_(There_are_no_messages)); \
86                 break; \
87         }
88
89 #define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
90           {\
91                   mutt_flushinp (); \
92                   mutt_error(_(No_visible)); \
93                   break; \
94         }
95
96
97 #define CHECK_READONLY if (Context->readonly) \
98                         { \
99                                   mutt_flushinp (); \
100                                 mutt_error(_(Mailbox_is_read_only)); \
101                                 break; \
102                         }
103
104 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
105                      {\
106                         mutt_flushinp (); \
107                         mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
108                         break; \
109                      }
110
111 #define CURHDR Context->hdrs[Context->v2r[menu->current]]
112 #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
113 #define UNREAD(h) mutt_thread_contains_unread (Context, h)
114 #define SW              (option(OPTMBOXPANE)?SidebarWidth:0)
115
116 extern const char *ReleaseDate;
117 extern size_t UngetCount;
118
119 void index_make_entry (char *s, size_t l, MUTTMENU * menu, int num)
120 {
121   format_flag flag =
122     M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR | M_FORMAT_INDEX;
123   int edgemsgno, reverse = Sort & SORT_REVERSE;
124   HEADER *h = Context->hdrs[Context->v2r[num]];
125   THREAD *tmp;
126
127   if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) {
128     flag |= M_FORMAT_TREE;      /* display the thread tree */
129     if (h->display_subject)
130       flag |= M_FORMAT_FORCESUBJ;
131     else {
132       if (reverse) {
133         if (menu->top + menu->pagelen > menu->max)
134           edgemsgno = Context->v2r[menu->max - 1];
135         else
136           edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
137       }
138       else
139         edgemsgno = Context->v2r[menu->top];
140
141       for (tmp = h->thread->parent; tmp; tmp = tmp->parent) {
142         if (!tmp->message)
143           continue;
144
145         /* if no ancestor is visible on current screen, provisionally force
146          * subject... */
147         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno <
148             edgemsgno) {
149           flag |= M_FORMAT_FORCESUBJ;
150           break;
151         }
152         else if (tmp->message->virtual >= 0)
153           break;
154       }
155       if (flag & M_FORMAT_FORCESUBJ) {
156         for (tmp = h->thread->prev; tmp; tmp = tmp->prev) {
157           if (!tmp->message)
158             continue;
159
160           /* ...but if a previous sibling is available, don't force it */
161           if (reverse ? tmp->message->msgno >
162               edgemsgno : tmp->message->msgno < edgemsgno)
163             break;
164           else if (tmp->message->virtual >= 0) {
165             flag &= ~M_FORMAT_FORCESUBJ;
166             break;
167           }
168         }
169       }
170     }
171   }
172
173   _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
174 }
175
176 int index_color (int index_no)
177 {
178   HEADER *h = Context->hdrs[Context->v2r[index_no]];
179
180   if (h && h->pair)
181     return h->pair;
182
183   mutt_set_header_color (Context, h);
184   return h->pair;
185 }
186
187 static int ci_next_undeleted (int msgno)
188 {
189   int i;
190
191   for (i = msgno + 1; i < Context->vcount; i++)
192     if (!Context->hdrs[Context->v2r[i]]->deleted)
193       return (i);
194   return (-1);
195 }
196
197 static int ci_previous_undeleted (int msgno)
198 {
199   int i;
200
201   for (i = msgno - 1; i >= 0; i--)
202     if (!Context->hdrs[Context->v2r[i]]->deleted)
203       return (i);
204   return (-1);
205 }
206
207 /* Return the index of the first new message, or failing that, the first
208  * unread message.
209  */
210 static int ci_first_message (void)
211 {
212   int old = -1, i;
213
214   if (Context && Context->msgcount) {
215     for (i = 0; i < Context->vcount; i++) {
216       if (!Context->hdrs[Context->v2r[i]]->read &&
217           !Context->hdrs[Context->v2r[i]]->deleted) {
218         if (!Context->hdrs[Context->v2r[i]]->old)
219           return (i);
220         else if (old == -1)
221           old = i;
222       }
223     }
224     if (old != -1)
225       return (old);
226
227     /* If Sort is reverse and not threaded, the latest message is first.
228      * If Sort is threaded, the latest message is first iff exactly one
229      * of Sort and SortAux are reverse.
230      */
231     if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
232         ((Sort & SORT_MASK) == SORT_THREADS &&
233          ((Sort ^ SortAux) & SORT_REVERSE)))
234       return 0;
235     else
236       return (Context->vcount ? Context->vcount - 1 : 0);
237   }
238   return 0;
239 }
240
241 /* This should be in mx.c, but it only gets used here. */
242 static int mx_toggle_write (CONTEXT * ctx)
243 {
244   if (!ctx)
245     return -1;
246
247   if (ctx->readonly) {
248     mutt_error (_("Cannot toggle write on a readonly mailbox!"));
249
250     return -1;
251   }
252
253   if (ctx->dontwrite) {
254     ctx->dontwrite = 0;
255     mutt_message (_("Changes to folder will be written on folder exit."));
256   }
257   else {
258     ctx->dontwrite = 1;
259     mutt_message (_("Changes to folder will not be written."));
260   }
261
262   return 0;
263 }
264
265 static void update_index (MUTTMENU * menu, CONTEXT * ctx, int check,
266                           int oldcount, int index_hint)
267 {
268   /* store pointers to the newly added messages */
269   HEADER **save_new = NULL;
270   int j;
271
272   /* take note of the current message */
273   if (oldcount) {
274     if (menu->current < Context->vcount)
275       menu->oldcurrent = index_hint;
276     else
277       oldcount = 0;             /* invalid message number! */
278   }
279
280   /* We are in a limited view. Check if the new message(s) satisfy
281    * the limit criteria. If they do, set their virtual msgno so that
282    * they will be visible in the limited view */
283   if (Context->pattern) {
284 #define THIS_BODY Context->hdrs[j]->content
285     for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount; j++) {
286       if (mutt_pattern_exec
287           (Context->limit_pattern, M_MATCH_FULL_ADDRESS, Context,
288             Context->hdrs[j])) {
289         Context->hdrs[j]->virtual = Context->vcount;
290         Context->v2r[Context->vcount] = j;
291         Context->hdrs[j]->limited = 1;
292         Context->vcount++;
293         Context->vsize +=
294           THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
295       }
296     }
297 #undef THIS_BODY
298   }
299
300   /* save the list of new messages */
301   if (oldcount && check != M_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) {
302     save_new = p_new(HEADER*, 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       p_delete(&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, OP_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, OP_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   for (;;) {
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           p_delete(&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         menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
565         CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES - 2);
566         sidebar_draw_frames();
567         SETCOLOR (MT_COLOR_STATUS);
568         BKGDSET (MT_COLOR_STATUS);
569         move(option (OPTSTATUSONTOP) ? 0 : LINES - 2,SW);
570         mutt_paddstr (COLS-SW, 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         p_delete(&Context->pattern);
969         Context->pattern = m_strdup(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       if (Context->pattern)
993         mutt_message _("To view all messages, limit to \"all\".");
994       break;
995
996     case OP_QUIT:
997
998       close = op;
999       if (attach_msg) {
1000         done = 1;
1001         break;
1002       }
1003
1004       if (query_quadoption (OPT_QUIT, _("Quit Mutt-ng?")) == M_YES) {
1005         int check;
1006
1007         oldcount = Context ? Context->msgcount : 0;
1008
1009         if (!Context
1010             || (check = mx_close_mailbox (Context, &index_hint)) == 0)
1011           done = 1;
1012         else {
1013           if (check == M_NEW_MAIL || check == M_REOPENED)
1014             update_index (menu, Context, check, oldcount, index_hint);
1015
1016           menu->redraw = REDRAW_FULL;   /* new mail arrived? */
1017           set_option (OPTSEARCHINVALID);
1018         }
1019       }
1020       break;
1021
1022     case OP_REDRAW:
1023
1024       clearok (stdscr, TRUE);
1025       menu->redraw = REDRAW_FULL;
1026       break;
1027
1028     case OP_SEARCH:
1029     case OP_SEARCH_REVERSE:
1030     case OP_SEARCH_NEXT:
1031     case OP_SEARCH_OPPOSITE:
1032
1033       CHECK_MSGCOUNT;
1034       CHECK_VISIBLE;
1035       if ((menu->current = mutt_search_command (menu->current, op)) == -1)
1036         menu->current = menu->oldcurrent;
1037       else
1038         menu->redraw = REDRAW_MOTION;
1039       break;
1040
1041     case OP_SORT:
1042     case OP_SORT_REVERSE:
1043
1044       if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0) {
1045         if (Context && Context->msgcount) {
1046           resort_index (menu);
1047           set_option (OPTSEARCHINVALID);
1048         }
1049       }
1050       break;
1051
1052     case OP_TAG:
1053
1054       CHECK_MSGCOUNT;
1055       CHECK_VISIBLE;
1056       if (tag && !option (OPTAUTOTAG)) {
1057         for (j = 0; j < Context->vcount; j++)
1058           mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
1059         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1060       }
1061       else {
1062         mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
1063         Context->last_tag = CURHDR->tagged ? CURHDR :
1064           ((Context->last_tag == CURHDR && !CURHDR->tagged)
1065            ? NULL : Context->last_tag);
1066         menu->redraw = REDRAW_STATUS;
1067         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
1068           menu->current++;
1069           menu->redraw |= REDRAW_MOTION_RESYNCH;
1070         }
1071         else
1072           menu->redraw |= REDRAW_CURRENT;
1073       }
1074       break;
1075
1076     case OP_MAIN_TAG_PATTERN:
1077
1078       CHECK_MSGCOUNT;
1079       CHECK_VISIBLE;
1080       mutt_pattern_func (M_TAG, _("Tag messages matching: "));
1081       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1082       break;
1083
1084     case OP_MAIN_UNDELETE_PATTERN:
1085
1086       CHECK_MSGCOUNT;
1087       CHECK_VISIBLE;
1088       CHECK_READONLY;
1089
1090       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
1091
1092       if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) ==
1093           0)
1094         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1095       break;
1096
1097     case OP_MAIN_UNTAG_PATTERN:
1098
1099       CHECK_MSGCOUNT;
1100       CHECK_VISIBLE;
1101       if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
1102         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1103       break;
1104
1105       /* --------------------------------------------------------------------
1106        * The following operations can be performed inside of the pager.
1107        */
1108
1109 #ifdef USE_IMAP
1110     case OP_MAIN_IMAP_FETCH:
1111       if (Context->magic == M_IMAP)
1112         imap_check_mailbox (Context, &index_hint, 1);
1113       break;
1114 #endif
1115
1116     case OP_MAIN_SYNC_FOLDER:
1117
1118       if (Context && !Context->msgcount)
1119         break;
1120
1121       CHECK_MSGCOUNT;
1122       CHECK_VISIBLE;
1123       CHECK_READONLY;
1124       {
1125         int oldvcount = Context->vcount;
1126         int oldcount = Context->msgcount;
1127         int dcount = 0;
1128         int check;
1129
1130         /* calculate the number of messages _above_ the cursor,
1131          * so we can keep the cursor on the current message
1132          */
1133         for (j = 0; j <= menu->current; j++) {
1134           if (Context->hdrs[Context->v2r[j]]->deleted)
1135             dcount++;
1136         }
1137
1138         if ((check = mx_sync_mailbox (Context, &index_hint)) == 0) {
1139           if (Context->vcount != oldvcount)
1140             menu->current -= dcount;
1141           set_option (OPTSEARCHINVALID);
1142         }
1143         else if (check == M_NEW_MAIL || check == M_REOPENED)
1144           update_index (menu, Context, check, oldcount, index_hint);
1145
1146         /* 
1147          * do a sanity check even if mx_sync_mailbox failed.
1148          */
1149
1150         if (menu->current < 0 || menu->current >= Context->vcount)
1151           menu->current = ci_first_message ();
1152       }
1153
1154       /* check for a fatal error, or all messages deleted */
1155       if (!Context->path)
1156         p_delete(&Context);
1157
1158       /* if we were in the pager, redisplay the message */
1159       if (menu->menu == MENU_PAGER) {
1160         op = OP_DISPLAY_MESSAGE;
1161         continue;
1162       }
1163       else
1164         menu->redraw = REDRAW_FULL;
1165       break;
1166
1167     case OP_SIDEBAR_OPEN:
1168     case OP_MAIN_CHANGE_FOLDER:
1169     case OP_MAIN_CHANGE_FOLDER_READONLY:
1170 #ifdef USE_NNTP
1171     case OP_MAIN_CHANGE_GROUP:
1172     case OP_MAIN_CHANGE_GROUP_READONLY:
1173 #endif
1174       if (attach_msg || option (OPTREADONLY) ||
1175 #ifdef USE_NNTP
1176           op == OP_MAIN_CHANGE_GROUP_READONLY ||
1177 #endif
1178           op == OP_MAIN_CHANGE_FOLDER_READONLY)
1179         flags = M_READONLY;
1180       else
1181         flags = 0;
1182
1183       if (flags)
1184         cp = _("Open mailbox in read-only mode");
1185       else
1186         cp = _("Open mailbox");
1187
1188       buf[0] = '\0';
1189 #ifdef USE_NNTP
1190       unset_option (OPTNEWS);
1191       if (op == OP_MAIN_CHANGE_GROUP || op == OP_MAIN_CHANGE_GROUP_READONLY) {
1192         set_option (OPTNEWS);
1193         if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
1194           break;
1195         if (flags)
1196           cp = _("Open newsgroup in read-only mode");
1197         else
1198           cp = _("Open newsgroup");
1199         nntp_buffy (buf, sizeof (buf));
1200       }
1201       else
1202 #endif
1203       {
1204         if (Context && Context->path)
1205           m_strcpy(buf, sizeof(buf), Context->path);
1206         if (op != OP_SIDEBAR_OPEN)
1207           buffy_next (buf, sizeof (buf));
1208       }
1209
1210       if (op == OP_SIDEBAR_OPEN) {
1211         m_strcpy(buf, sizeof(buf), sidebar_get_current());
1212       }
1213       else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) {
1214         if (menu->menu == MENU_PAGER) {
1215           op = OP_DISPLAY_MESSAGE;
1216           continue;
1217         } else
1218           break;
1219       }
1220       if (!buf[0]) {
1221         CLEARLINE (LINES - 1);
1222         break;
1223       }
1224
1225 #ifdef USE_NNTP
1226       if (option (OPTNEWS)) {
1227         unset_option (OPTNEWS);
1228         nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
1229       }
1230       else
1231 #endif
1232         mutt_expand_path (buf, sizeof (buf));
1233       if (mx_get_magic (buf) <= 0) {
1234         mutt_error (_("%s is not a mailbox."), buf);
1235         break;
1236       }
1237       str_replace (&CurrentFolder, buf);
1238
1239       if (Context) {
1240         int check;
1241
1242 #ifdef USE_COMPRESSED
1243         if (Context->compressinfo && Context->realpath)
1244           str_replace (&LastFolder, Context->realpath);
1245         else
1246 #endif
1247
1248           str_replace (&LastFolder, Context->path);
1249         oldcount = Context ? Context->msgcount : 0;
1250
1251         if ((check = mx_close_mailbox (Context, &index_hint)) != 0) {
1252           if (check == M_NEW_MAIL || check == M_REOPENED)
1253             update_index (menu, Context, check, oldcount, index_hint);
1254
1255           set_option (OPTSEARCHINVALID);
1256           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1257           break;
1258         }
1259         p_delete(&Context);
1260       }
1261
1262       mutt_sleep (0);
1263
1264       /* Set CurrentMenu to MENU_MAIN before executing any folder
1265        * hooks so that all the index menu functions are available to
1266        * the exec command.
1267        */
1268
1269       CurrentMenu = MENU_MAIN;
1270       mutt_folder_hook (buf);
1271
1272       if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) {
1273         menu->current = ci_first_message ();
1274       }
1275       else
1276         menu->current = 0;
1277       sidebar_set_current (buf);
1278
1279 #ifdef USE_NNTP
1280       /* buffy_check() must be done with mail-reader mode! */
1281       menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
1282                                       (Context
1283                                        && (Context->magic ==
1284                                            M_NNTP)) ? IndexNewsHelp :
1285                                       IndexHelp);
1286 #endif
1287       mutt_clear_error ();
1288       buffy_check (1);     /* force the buffy check after we have changed
1289                                    the folder */
1290       menu->redraw = REDRAW_FULL;
1291       set_option (OPTSEARCHINVALID);
1292       break;
1293
1294     case OP_DISPLAY_MESSAGE:
1295     case OP_DISPLAY_HEADERS:   /* don't weed the headers */
1296
1297       CHECK_MSGCOUNT;
1298       CHECK_VISIBLE;
1299       /*
1300        * toggle the weeding of headers so that a user can press the key
1301        * again while reading the message.
1302        */
1303       if (op == OP_DISPLAY_HEADERS)
1304         toggle_option (OPTWEED);
1305
1306       unset_option (OPTNEEDRESORT);
1307
1308       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) {
1309         mutt_uncollapse_thread (Context, CURHDR);
1310         mutt_set_virtual (Context);
1311         if (option (OPTUNCOLLAPSEJUMP))
1312           menu->current = mutt_thread_next_unread (Context, CURHDR);
1313       }
1314
1315       if (option (OPTPGPAUTODEC) && 
1316           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
1317         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1318
1319       if ((op = mutt_display_message (CURHDR)) == -1) {
1320         unset_option (OPTNEEDRESORT);
1321         break;
1322       }
1323
1324       menu->menu = MENU_PAGER;
1325       menu->oldcurrent = menu->current;
1326       continue;
1327
1328     case OP_EXIT:
1329
1330       close = op;
1331       if (menu->menu == MENU_MAIN && attach_msg) {
1332         done = 1;
1333         break;
1334       }
1335
1336       if ((menu->menu == MENU_MAIN)
1337           && (query_quadoption (OPT_QUIT,
1338                                 _("Exit Mutt-ng without saving?")) == M_YES))
1339       {
1340         if (Context) {
1341           mx_fastclose_mailbox (Context);
1342           p_delete(&Context);
1343         }
1344         done = 1;
1345       }
1346       break;
1347
1348     case OP_EDIT_TYPE:
1349
1350       CHECK_MSGCOUNT;
1351       CHECK_VISIBLE;
1352       CHECK_ATTACH;
1353       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1354       /* if we were in the pager, redisplay the message */
1355       if (menu->menu == MENU_PAGER) {
1356         op = OP_DISPLAY_MESSAGE;
1357         continue;
1358       }
1359       else
1360         menu->redraw = REDRAW_CURRENT;
1361       break;
1362
1363     case OP_MAIN_BREAK_THREAD:
1364
1365       CHECK_MSGCOUNT;
1366       CHECK_VISIBLE;
1367       CHECK_READONLY;
1368
1369       if ((Sort & SORT_MASK) != SORT_THREADS)
1370         mutt_error (_("Threading is not enabled."));
1371
1372       else {
1373         {
1374           HEADER *oldcur = CURHDR;
1375
1376           mutt_break_thread (CURHDR);
1377           mutt_sort_headers (Context, 1);
1378           menu->current = oldcur->virtual;
1379         }
1380
1381         Context->changed = 1;
1382         mutt_message _("Thread broken");
1383
1384         if (menu->menu == MENU_PAGER) {
1385           op = OP_DISPLAY_MESSAGE;
1386           continue;
1387         }
1388         else
1389           menu->redraw |= REDRAW_INDEX;
1390       }
1391       break;
1392
1393     case OP_MAIN_LINK_THREADS:
1394
1395       CHECK_MSGCOUNT;
1396       CHECK_VISIBLE;
1397       CHECK_READONLY;
1398
1399       if ((Sort & SORT_MASK) != SORT_THREADS)
1400         mutt_error (_("Threading is not enabled."));
1401
1402       else if (!CURHDR->env->message_id)
1403         mutt_error (_("No Message-ID: header available to link thread"));
1404
1405       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1406         mutt_error (_("First, please tag a message to be linked here"));
1407
1408       else {
1409         HEADER *oldcur = CURHDR;
1410
1411         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1412                                Context)) {
1413           mutt_sort_headers (Context, 1);
1414           menu->current = oldcur->virtual;
1415
1416           Context->changed = 1;
1417           mutt_message _("Threads linked");
1418         }
1419         else
1420           mutt_error (_("No thread linked"));
1421       }
1422
1423       if (menu->menu == MENU_PAGER) {
1424         op = OP_DISPLAY_MESSAGE;
1425         continue;
1426       }
1427       else
1428         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1429       break;
1430
1431     case OP_MAIN_NEXT_UNDELETED:
1432
1433       CHECK_MSGCOUNT;
1434       CHECK_VISIBLE;
1435       if (menu->current >= Context->vcount - 1) {
1436         if (menu->menu == MENU_MAIN)
1437           mutt_error (_("You are on the last message."));
1438
1439         break;
1440       }
1441       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1442         menu->current = menu->oldcurrent;
1443         if (menu->menu == MENU_MAIN)
1444           mutt_error (_("No undeleted messages."));
1445       }
1446       else if (menu->menu == MENU_PAGER) {
1447         op = OP_DISPLAY_MESSAGE;
1448         continue;
1449       }
1450       else
1451         menu->redraw = REDRAW_MOTION;
1452       break;
1453
1454     case OP_NEXT_ENTRY:
1455
1456       CHECK_MSGCOUNT;
1457       CHECK_VISIBLE;
1458       if (menu->current >= Context->vcount - 1) {
1459         if (menu->menu == MENU_MAIN)
1460           mutt_error (_("You are on the last message."));
1461
1462         break;
1463       }
1464       menu->current++;
1465       if (menu->menu == MENU_PAGER) {
1466         op = OP_DISPLAY_MESSAGE;
1467         continue;
1468       }
1469       else
1470         menu->redraw = REDRAW_MOTION;
1471       break;
1472
1473     case OP_MAIN_PREV_UNDELETED:
1474
1475       CHECK_MSGCOUNT;
1476       CHECK_VISIBLE;
1477       if (menu->current < 1) {
1478         mutt_error (_("You are on the first message."));
1479
1480         break;
1481       }
1482       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1483         menu->current = menu->oldcurrent;
1484         if (menu->menu == MENU_MAIN)
1485           mutt_error (_("No undeleted messages."));
1486       }
1487       else if (menu->menu == MENU_PAGER) {
1488         op = OP_DISPLAY_MESSAGE;
1489         continue;
1490       }
1491       else
1492         menu->redraw = REDRAW_MOTION;
1493       break;
1494
1495     case OP_PREV_ENTRY:
1496
1497       CHECK_MSGCOUNT;
1498       CHECK_VISIBLE;
1499       if (menu->current < 1) {
1500         if (menu->menu == MENU_MAIN)
1501           mutt_error (_("You are on the first message."));
1502
1503         break;
1504       }
1505       menu->current--;
1506       if (menu->menu == MENU_PAGER) {
1507         op = OP_DISPLAY_MESSAGE;
1508         continue;
1509       }
1510       else
1511         menu->redraw = REDRAW_MOTION;
1512       break;
1513
1514     case OP_DECRYPT_COPY:
1515     case OP_DECRYPT_SAVE:
1516       if (!WithCrypto)
1517         break;
1518       /* fall thru */
1519     case OP_COPY_MESSAGE:
1520     case OP_SAVE:
1521     case OP_DECODE_COPY:
1522     case OP_DECODE_SAVE:
1523       CHECK_MSGCOUNT;
1524       CHECK_VISIBLE;
1525       if (mutt_save_message (tag ? NULL : CURHDR,
1526                              (op == OP_DECRYPT_SAVE) ||
1527                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1528                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1529                              (op == OP_DECRYPT_SAVE)
1530                              || (op == OP_DECRYPT_COPY)
1531                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1532                                                            || op ==
1533                                                            OP_DECODE_SAVE
1534                                                            || op ==
1535                                                            OP_DECRYPT_SAVE)
1536         ) {
1537         if (tag)
1538           menu->redraw |= REDRAW_INDEX;
1539         else if (option (OPTRESOLVE)) {
1540           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1541             menu->current = menu->oldcurrent;
1542             menu->redraw |= REDRAW_CURRENT;
1543           }
1544           else
1545             menu->redraw |= REDRAW_MOTION_RESYNCH;
1546         }
1547         else
1548           menu->redraw |= REDRAW_CURRENT;
1549       }
1550       break;
1551
1552     case OP_MAIN_NEXT_NEW:
1553     case OP_MAIN_NEXT_UNREAD:
1554     case OP_MAIN_PREV_NEW:
1555     case OP_MAIN_PREV_UNREAD:
1556     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1557     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1558
1559       {
1560         int first_unread = -1;
1561         int first_new = -1;
1562
1563         CHECK_MSGCOUNT;
1564         CHECK_VISIBLE;
1565
1566         i = menu->current;
1567         menu->current = -1;
1568         for (j = 0; j != Context->vcount; j++) {
1569 #define CURHDRi Context->hdrs[Context->v2r[i]]
1570           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1571               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1572             i++;
1573             if (i > Context->vcount - 1) {
1574               mutt_message _("Search wrapped to top.");
1575
1576               i = 0;
1577             }
1578           }
1579           else {
1580             i--;
1581             if (i < 0) {
1582               mutt_message _("Search wrapped to bottom.");
1583
1584               i = Context->vcount - 1;
1585             }
1586           }
1587
1588           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1589             if (UNREAD (CURHDRi) && first_unread == -1)
1590               first_unread = i;
1591             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1592               first_new = i;
1593           }
1594           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1595             if (first_unread == -1)
1596               first_unread = i;
1597             if ((!CURHDRi->old) && first_new == -1)
1598               first_new = i;
1599           }
1600
1601           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1602               first_unread != -1)
1603             break;
1604           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1605                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1606                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1607               && first_new != -1)
1608             break;
1609         }
1610 #undef CURHDRi
1611         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1612              op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1613              || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1614             && first_new != -1)
1615           menu->current = first_new;
1616         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1617                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1618                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1619                  && first_unread != -1)
1620           menu->current = first_unread;
1621
1622         if (menu->current == -1) {
1623           menu->current = menu->oldcurrent;
1624           mutt_error ("%s%s.",
1625                       (op == OP_MAIN_NEXT_NEW
1626                        || op ==
1627                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1628                       _("No unread messages"),
1629                       Context->pattern ? _(" in this limited view") : "");
1630         }
1631         else if (menu->menu == MENU_PAGER) {
1632           op = OP_DISPLAY_MESSAGE;
1633           continue;
1634         }
1635         else
1636           menu->redraw = REDRAW_MOTION;
1637         break;
1638       }
1639     case OP_FLAG_MESSAGE:
1640
1641       CHECK_MSGCOUNT;
1642       CHECK_VISIBLE;
1643       CHECK_READONLY;
1644
1645       CHECK_MX_ACL (Context, ACL_WRITE, _("Flagging"));
1646
1647       if (tag) {
1648         for (j = 0; j < Context->vcount; j++) {
1649           if (Context->hdrs[Context->v2r[j]]->tagged)
1650             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1651                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1652         }
1653
1654         menu->redraw |= REDRAW_INDEX;
1655       }
1656       else {
1657         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1658         if (option (OPTRESOLVE)) {
1659           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1660             menu->current = menu->oldcurrent;
1661             menu->redraw = REDRAW_CURRENT;
1662           }
1663           else
1664             menu->redraw = REDRAW_MOTION_RESYNCH;
1665         }
1666         else
1667           menu->redraw = REDRAW_CURRENT;
1668       }
1669       menu->redraw |= REDRAW_STATUS;
1670       break;
1671
1672     case OP_TOGGLE_NEW:
1673
1674       CHECK_MSGCOUNT;
1675       CHECK_VISIBLE;
1676       CHECK_READONLY;
1677
1678       CHECK_MX_ACL (Context, ACL_SEEN, _("Toggling"));
1679
1680       if (tag) {
1681         for (j = 0; j < Context->vcount; j++) {
1682           if (Context->hdrs[Context->v2r[j]]->tagged) {
1683             if (Context->hdrs[Context->v2r[j]]->read ||
1684                 Context->hdrs[Context->v2r[j]]->old)
1685               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1686                              1);
1687             else
1688               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1689                              1);
1690           }
1691         }
1692         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1693       }
1694       else {
1695         if (CURHDR->read || CURHDR->old)
1696           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1697         else
1698           mutt_set_flag (Context, CURHDR, M_READ, 1);
1699
1700         if (option (OPTRESOLVE)) {
1701           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1702             menu->current = menu->oldcurrent;
1703             menu->redraw = REDRAW_CURRENT;
1704           }
1705           else
1706             menu->redraw = REDRAW_MOTION_RESYNCH;
1707         }
1708         else
1709           menu->redraw = REDRAW_CURRENT;
1710         menu->redraw |= REDRAW_STATUS;
1711       }
1712       break;
1713
1714     case OP_TOGGLE_WRITE:
1715
1716       CHECK_IN_MAILBOX;
1717       if (mx_toggle_write (Context) == 0)
1718         menu->redraw |= REDRAW_STATUS;
1719       break;
1720
1721     case OP_MAIN_NEXT_THREAD:
1722     case OP_MAIN_NEXT_SUBTHREAD:
1723     case OP_MAIN_PREV_THREAD:
1724     case OP_MAIN_PREV_SUBTHREAD:
1725
1726       CHECK_MSGCOUNT;
1727       CHECK_VISIBLE;
1728       switch (op) {
1729       case OP_MAIN_NEXT_THREAD:
1730         menu->current = mutt_next_thread (CURHDR);
1731         break;
1732
1733       case OP_MAIN_NEXT_SUBTHREAD:
1734         menu->current = mutt_next_subthread (CURHDR);
1735         break;
1736
1737       case OP_MAIN_PREV_THREAD:
1738         menu->current = mutt_previous_thread (CURHDR);
1739         break;
1740
1741       case OP_MAIN_PREV_SUBTHREAD:
1742         menu->current = mutt_previous_subthread (CURHDR);
1743         break;
1744       }
1745
1746       if (menu->current < 0) {
1747         menu->current = menu->oldcurrent;
1748         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1749           mutt_error (_("No more threads."));
1750
1751         else
1752           mutt_error (_("You are on the first thread."));
1753       }
1754       else if (menu->menu == MENU_PAGER) {
1755         op = OP_DISPLAY_MESSAGE;
1756         continue;
1757       }
1758       else
1759         menu->redraw = REDRAW_MOTION;
1760       break;
1761
1762     case OP_MAIN_PARENT_MESSAGE:
1763
1764       CHECK_MSGCOUNT;
1765       CHECK_VISIBLE;
1766
1767       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1768         menu->current = menu->oldcurrent;
1769       }
1770       else if (menu->menu == MENU_PAGER) {
1771         op = OP_DISPLAY_MESSAGE;
1772         continue;
1773       }
1774       else
1775         menu->redraw = REDRAW_MOTION;
1776       break;
1777
1778     case OP_MAIN_SET_FLAG:
1779     case OP_MAIN_CLEAR_FLAG:
1780
1781       CHECK_MSGCOUNT;
1782       CHECK_VISIBLE;
1783       CHECK_READONLY;
1784
1785       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) ==
1786           0) {
1787         menu->redraw = REDRAW_STATUS;
1788         if (tag)
1789           menu->redraw |= REDRAW_INDEX;
1790         else if (option (OPTRESOLVE)) {
1791           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1792             menu->current = menu->oldcurrent;
1793             menu->redraw |= REDRAW_CURRENT;
1794           }
1795           else
1796             menu->redraw |= REDRAW_MOTION_RESYNCH;
1797         }
1798         else
1799           menu->redraw |= REDRAW_CURRENT;
1800       }
1801       break;
1802
1803     case OP_MAIN_COLLAPSE_THREAD:
1804       CHECK_MSGCOUNT;
1805       CHECK_VISIBLE;
1806
1807       if ((Sort & SORT_MASK) != SORT_THREADS) {
1808         mutt_error (_("Threading is not enabled."));
1809
1810         break;
1811       }
1812
1813       if (CURHDR->collapsed) {
1814         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1815         mutt_set_virtual (Context);
1816         if (option (OPTUNCOLLAPSEJUMP))
1817           menu->current = mutt_thread_next_unread (Context, CURHDR);
1818       }
1819       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1820         menu->current = mutt_collapse_thread (Context, CURHDR);
1821         mutt_set_virtual (Context);
1822       }
1823       else {
1824         mutt_error (_("Thread contains unread messages."));
1825
1826         break;
1827       }
1828
1829       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1830
1831       break;
1832
1833     case OP_MAIN_COLLAPSE_ALL:
1834       CHECK_MSGCOUNT;
1835       CHECK_VISIBLE;
1836
1837       if ((Sort & SORT_MASK) != SORT_THREADS) {
1838         mutt_error (_("Threading is not enabled."));
1839
1840         break;
1841       }
1842
1843       {
1844         HEADER *h, *base;
1845         THREAD *thread, *top;
1846         int final;
1847
1848         if (CURHDR->collapsed)
1849           final = mutt_uncollapse_thread (Context, CURHDR);
1850         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1851           final = mutt_collapse_thread (Context, CURHDR);
1852         else
1853           final = CURHDR->virtual;
1854
1855         base = Context->hdrs[Context->v2r[final]];
1856
1857         top = Context->tree;
1858         Context->collapsed = !Context->collapsed;
1859         while ((thread = top) != NULL) {
1860           while (!thread->message)
1861             thread = thread->child;
1862           h = thread->message;
1863
1864           if (h->collapsed != Context->collapsed) {
1865             if (h->collapsed)
1866               mutt_uncollapse_thread (Context, h);
1867             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1868               mutt_collapse_thread (Context, h);
1869           }
1870           top = top->next;
1871         }
1872
1873         mutt_set_virtual (Context);
1874         for (j = 0; j < Context->vcount; j++) {
1875           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1876             menu->current = j;
1877             break;
1878           }
1879         }
1880
1881         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1882       }
1883       break;
1884
1885       /* --------------------------------------------------------------------
1886        * These functions are invoked directly from the internal-pager
1887        */
1888
1889     case OP_BOUNCE_MESSAGE:
1890
1891       CHECK_ATTACH;
1892       CHECK_MSGCOUNT;
1893       CHECK_VISIBLE;
1894       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1895       break;
1896
1897     case OP_CREATE_ALIAS:
1898
1899       mutt_create_alias (Context
1900                          && Context->vcount ? CURHDR->env : NULL, NULL);
1901       MAYBE_REDRAW (menu->redraw);
1902       menu->redraw |= REDRAW_CURRENT;
1903       break;
1904
1905     case OP_QUERY:
1906       CHECK_ATTACH;
1907       mutt_query_menu (NULL, 0);
1908       MAYBE_REDRAW (menu->redraw);
1909       break;
1910
1911     case OP_PURGE_MESSAGE:
1912     case OP_DELETE:
1913
1914       CHECK_MSGCOUNT;
1915       CHECK_VISIBLE;
1916       CHECK_READONLY;
1917
1918       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1919
1920       if (tag) {
1921         mutt_tag_set_flag (M_DELETE, 1);
1922         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1923         if (option (OPTDELETEUNTAG))
1924           mutt_tag_set_flag (M_TAG, 0);
1925         menu->redraw = REDRAW_INDEX;
1926       }
1927       else {
1928         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1929         mutt_set_flag (Context, CURHDR, M_PURGED,
1930                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1931         if (option (OPTDELETEUNTAG))
1932           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1933         if (option (OPTRESOLVE)) {
1934           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1935             menu->current = menu->oldcurrent;
1936             menu->redraw = REDRAW_CURRENT;
1937           }
1938           else if (menu->menu == MENU_PAGER) {
1939             op = OP_DISPLAY_MESSAGE;
1940             continue;
1941           }
1942           else
1943             menu->redraw |= REDRAW_MOTION_RESYNCH;
1944         }
1945         else
1946           menu->redraw = REDRAW_CURRENT;
1947       }
1948       menu->redraw |= REDRAW_STATUS;
1949       break;
1950
1951     case OP_DELETE_THREAD:
1952     case OP_DELETE_SUBTHREAD:
1953
1954       CHECK_MSGCOUNT;
1955       CHECK_VISIBLE;
1956       CHECK_READONLY;
1957
1958       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1959
1960       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
1961                                  op == OP_DELETE_THREAD ? 0 : 1);
1962
1963       if (rc != -1) {
1964         if (option (OPTDELETEUNTAG))
1965           mutt_thread_set_flag (CURHDR, M_TAG, 0,
1966                                 op == OP_DELETE_THREAD ? 0 : 1);
1967         if (option (OPTRESOLVE))
1968           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1969             menu->current = menu->oldcurrent;
1970         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1971       }
1972       break;
1973
1974 #ifdef USE_NNTP
1975     case OP_CATCHUP:
1976       if (Context && Context->magic == M_NNTP) {
1977         if (mutt_newsgroup_catchup (CurrentNewsSrv,
1978                                     ((NNTP_DATA *) Context->data)->group))
1979           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1980       }
1981       break;
1982 #endif
1983
1984     case OP_DISPLAY_ADDRESS:
1985
1986       CHECK_MSGCOUNT;
1987       CHECK_VISIBLE;
1988       mutt_display_address (CURHDR->env);
1989       break;
1990
1991     case OP_ENTER_COMMAND:
1992
1993       CurrentMenu = MENU_MAIN;
1994       mutt_enter_command ();
1995       mutt_check_rescore (Context);
1996       if (option (OPTFORCEREDRAWINDEX))
1997         menu->redraw = REDRAW_FULL;
1998       unset_option (OPTFORCEREDRAWINDEX);
1999       unset_option (OPTFORCEREDRAWPAGER);
2000       break;
2001
2002     case OP_EDIT_MESSAGE:
2003
2004       CHECK_MSGCOUNT;
2005       CHECK_VISIBLE;
2006       CHECK_READONLY;
2007       CHECK_ATTACH;
2008
2009       CHECK_MX_ACL (Context, ACL_INSERT, _("Editing"));
2010
2011       if (option (OPTPGPAUTODEC)
2012           && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
2013         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2014       mutt_edit_message (Context, tag ? NULL : CURHDR);
2015       menu->redraw = REDRAW_FULL;
2016
2017       break;
2018
2019     case OP_FORWARD_MESSAGE:
2020
2021       CHECK_MSGCOUNT;
2022       CHECK_VISIBLE;
2023       CHECK_ATTACH;
2024
2025       if (option (OPTPGPAUTODEC) &&
2026           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2027         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2028       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
2029       menu->redraw = REDRAW_FULL;
2030       break;
2031
2032
2033     case OP_FORGET_PASSPHRASE:
2034       crypt_forget_passphrase ();
2035       break;
2036
2037     case OP_GROUP_REPLY:
2038
2039       CHECK_MSGCOUNT;
2040       CHECK_VISIBLE;
2041       CHECK_ATTACH;
2042
2043       if (option (OPTPGPAUTODEC) &&
2044           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2045         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2046
2047       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
2048                        tag ? NULL : CURHDR);
2049       menu->redraw = REDRAW_FULL;
2050       break;
2051
2052     case OP_LIST_REPLY:
2053
2054       CHECK_ATTACH;
2055       CHECK_MSGCOUNT;
2056       CHECK_VISIBLE;
2057
2058       if (option (OPTPGPAUTODEC) &&
2059           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2060         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2061
2062       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
2063                        tag ? NULL : CURHDR);
2064       menu->redraw = REDRAW_FULL;
2065       break;
2066
2067     case OP_MAIL:
2068
2069       CHECK_ATTACH;
2070       ci_send_message (0, NULL, NULL, Context, NULL);
2071       menu->redraw = REDRAW_FULL;
2072       break;
2073
2074     case OP_MAIL_KEY:
2075       if (!(WithCrypto & APPLICATION_PGP))
2076         break;
2077       CHECK_ATTACH;
2078       ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
2079       menu->redraw = REDRAW_FULL;
2080       break;
2081
2082
2083     case OP_EXTRACT_KEYS:
2084       if (!WithCrypto)
2085         break;
2086       CHECK_MSGCOUNT;
2087       CHECK_VISIBLE;
2088       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
2089       menu->redraw = REDRAW_FULL;
2090       break;
2091
2092
2093     case OP_CHECK_TRADITIONAL:
2094       if (!(WithCrypto & APPLICATION_PGP))
2095         break;
2096       CHECK_MSGCOUNT;
2097       CHECK_VISIBLE;
2098       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
2099         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2100
2101       if (menu->menu == MENU_PAGER) {
2102         op = OP_DISPLAY_MESSAGE;
2103         continue;
2104       }
2105       break;
2106
2107     case OP_PIPE:
2108
2109       CHECK_MSGCOUNT;
2110       CHECK_VISIBLE;
2111       mutt_pipe_message (tag ? NULL : CURHDR);
2112 #ifdef USE_IMAP
2113       /* in an IMAP folder index with imap_peek=no, piping could change
2114        * * new or old messages status to read. Redraw what's needed.
2115        */
2116       if (Context->magic == M_IMAP && !option (OPTIMAPPEEK)) {
2117         menu->redraw = (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS;
2118       }
2119 #endif
2120       MAYBE_REDRAW (menu->redraw);
2121       break;
2122
2123     case OP_PRINT:
2124
2125       CHECK_MSGCOUNT;
2126       CHECK_VISIBLE;
2127       mutt_print_message (tag ? NULL : CURHDR);
2128 #ifdef USE_IMAP
2129       /* in an IMAP folder index with imap_peek=no, piping could change
2130        * * new or old messages status to read. Redraw what's needed.
2131        */
2132       if (Context->magic == M_IMAP && !option (OPTIMAPPEEK)) {
2133         menu->redraw = (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS;
2134       }
2135 #endif
2136       break;
2137
2138     case OP_MAIN_READ_THREAD:
2139     case OP_MAIN_READ_SUBTHREAD:
2140
2141       CHECK_MSGCOUNT;
2142       CHECK_VISIBLE;
2143       CHECK_READONLY;
2144
2145       CHECK_MX_ACL (Context, ACL_SEEN, _("Marking as read"));
2146
2147       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2148                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
2149
2150       if (rc != -1) {
2151         if (option (OPTRESOLVE)) {
2152           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
2153                                 mutt_next_thread (CURHDR) :
2154                                 mutt_next_subthread (CURHDR))) == -1)
2155             menu->current = menu->oldcurrent;
2156         }
2157         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2158       }
2159       break;
2160
2161     case OP_RECALL_MESSAGE:
2162
2163       CHECK_ATTACH;
2164       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2165       menu->redraw = REDRAW_FULL;
2166       break;
2167
2168     case OP_RESEND:
2169
2170       CHECK_ATTACH;
2171       CHECK_MSGCOUNT;
2172       CHECK_VISIBLE;
2173
2174       if (tag) {
2175         for (j = 0; j < Context->vcount; j++) {
2176           if (Context->hdrs[Context->v2r[j]]->tagged)
2177             mutt_resend_message (NULL, Context,
2178                                  Context->hdrs[Context->v2r[j]]);
2179         }
2180       }
2181       else
2182         mutt_resend_message (NULL, Context, CURHDR);
2183
2184       menu->redraw = REDRAW_FULL;
2185       break;
2186
2187 #ifdef USE_NNTP
2188     case OP_POST:
2189     case OP_FOLLOWUP:
2190     case OP_FORWARD_TO_GROUP:
2191
2192       CHECK_ATTACH;
2193       if ((op == OP_FOLLOWUP || op == OP_FORWARD_TO_GROUP) &&
2194           Context && Context->msgcount == 0) {
2195         mutt_error (_("There are no messages."));
2196         sleep (2);
2197       }
2198       else if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2199                str_casecmp (CURHDR->env->followup_to, "poster") ||
2200                query_quadoption (OPT_FOLLOWUPTOPOSTER,
2201                                  _("Reply by mail as poster prefers?")) !=
2202                M_YES) {
2203         if (Context && Context->magic == M_NNTP
2204             && !((NNTP_DATA *) Context->data)->allowed
2205             && query_quadoption (OPT_TOMODERATED,
2206                                  _
2207                                  ("Posting to this group not allowed, may be moderated. Continue?"))
2208             != M_YES)
2209           break;
2210         if (op == OP_POST)
2211           ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2212         else {
2213           CHECK_MSGCOUNT;
2214           if (op == OP_FOLLOWUP)
2215             ci_send_message (SENDNEWS | SENDREPLY, NULL, NULL, Context,
2216                              tag ? NULL : CURHDR);
2217           else
2218             ci_send_message (SENDNEWS | SENDFORWARD, NULL, NULL, Context,
2219                              tag ? NULL : CURHDR);
2220         }
2221         menu->redraw = REDRAW_FULL;
2222         break;
2223       }
2224 #endif
2225
2226     case OP_REPLY:
2227
2228       CHECK_ATTACH;
2229       CHECK_MSGCOUNT;
2230       CHECK_VISIBLE;
2231
2232       if (option (OPTPGPAUTODEC) &&
2233           (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) 
2234         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2235
2236       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2237       menu->redraw = REDRAW_FULL;
2238       break;
2239
2240     case OP_SHELL_ESCAPE:
2241
2242       mutt_shell_escape ();
2243       MAYBE_REDRAW (menu->redraw);
2244       break;
2245
2246     case OP_TAG_THREAD:
2247     case OP_TAG_SUBTHREAD:
2248
2249       CHECK_MSGCOUNT;
2250       CHECK_VISIBLE;
2251       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2252                                  op == OP_TAG_THREAD ? 0 : 1);
2253
2254       if (rc != -1) {
2255         if (option (OPTRESOLVE)) {
2256           menu->current = mutt_next_thread (CURHDR);
2257
2258           if (menu->current == -1)
2259             menu->current = menu->oldcurrent;
2260         }
2261         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2262       }
2263       break;
2264
2265     case OP_UNDELETE:
2266
2267       CHECK_MSGCOUNT;
2268       CHECK_VISIBLE;
2269       CHECK_READONLY;
2270
2271       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2272
2273       if (tag) {
2274         mutt_tag_set_flag (M_DELETE, 0);
2275         mutt_tag_set_flag (M_PURGED, 0);
2276         menu->redraw = REDRAW_INDEX;
2277       }
2278       else {
2279         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2280         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2281         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
2282           menu->current++;
2283           menu->redraw = REDRAW_MOTION_RESYNCH;
2284         }
2285         else
2286           menu->redraw = REDRAW_CURRENT;
2287       }
2288       menu->redraw |= REDRAW_STATUS;
2289       break;
2290
2291     case OP_UNDELETE_THREAD:
2292     case OP_UNDELETE_SUBTHREAD:
2293
2294       CHECK_MSGCOUNT;
2295       CHECK_VISIBLE;
2296       CHECK_READONLY;
2297
2298       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2299
2300       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2301                                  op == OP_UNDELETE_THREAD ? 0 : 1)
2302         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2303                                 op == OP_UNDELETE_THREAD ? 0 : 1);
2304
2305       if (rc > -1) {
2306         if (option (OPTRESOLVE)) {
2307           if (op == OP_UNDELETE_THREAD)
2308             menu->current = mutt_next_thread (CURHDR);
2309           else
2310             menu->current = mutt_next_subthread (CURHDR);
2311
2312           if (menu->current == -1)
2313             menu->current = menu->oldcurrent;
2314         }
2315         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2316       }
2317       break;
2318
2319     case OP_VERSION:
2320       mutt_version ();
2321       break;
2322
2323     case OP_BUFFY_LIST:
2324       if (option (OPTFORCEBUFFYCHECK))
2325         buffy_check (1);
2326       buffy_list ();
2327       menu->redraw = REDRAW_FULL;
2328       break;
2329
2330     case OP_VIEW_ATTACHMENTS:
2331       CHECK_MSGCOUNT;
2332       CHECK_VISIBLE;
2333       mutt_view_attachments (CURHDR);
2334       if (CURHDR->attach_del)
2335         Context->changed = 1;
2336       menu->redraw = REDRAW_FULL;
2337       break;
2338
2339     case OP_END_COND:
2340       break;
2341
2342     case OP_WHAT_KEY:
2343       mutt_what_key ();
2344       break;
2345
2346     case OP_REBUILD_CACHE:
2347       mx_rebuild_cache ();
2348       break;
2349
2350     case OP_SIDEBAR_SCROLL_UP:
2351     case OP_SIDEBAR_SCROLL_DOWN:
2352     case OP_SIDEBAR_NEXT:
2353     case OP_SIDEBAR_PREV:
2354     case OP_SIDEBAR_NEXT_NEW:
2355     case OP_SIDEBAR_PREV_NEW:
2356       sidebar_scroll (op, menu->menu);
2357       break;
2358     default:
2359       if (menu->menu == MENU_MAIN)
2360         km_error_key (MENU_MAIN);
2361     }
2362
2363     if (menu->menu == MENU_PAGER) {
2364       menu->menu = MENU_MAIN;
2365       menu->redraw = REDRAW_FULL;
2366 #if 0
2367       set_option (OPTWEED);     /* turn header weeding back on. */
2368 #endif
2369     }
2370
2371     if (done)
2372       break;
2373   }
2374
2375   if (!attach_msg) {
2376 #ifdef USE_IMAP
2377   /* Close all open IMAP connections */
2378     imap_logout_all ();
2379 #endif
2380 #ifdef USE_NNTP
2381   /* Close all open NNTP connections */
2382     nntp_logout_all ();
2383 #endif
2384 #ifdef USE_SASL
2385     mutt_sasl_done ();
2386 #endif
2387   }
2388
2389   mutt_menuDestroy (&menu);
2390   return (close);
2391 }
2392
2393 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2394 {
2395   COLOR_LINE *color;
2396
2397   if (!curhdr)
2398     return;
2399
2400   for (color = ColorIndexList; color; color = color->next)
2401     if (mutt_pattern_exec
2402         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2403       curhdr->pair = color->pair;
2404       return;
2405     }
2406   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2407 }