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