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