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