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