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