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