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