/src/neomutt/pager/message.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Process a message for display in the pager |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2021 Richard Russon <rich@flatcap.org> |
7 | | * |
8 | | * @copyright |
9 | | * This program is free software: you can redistribute it and/or modify it under |
10 | | * the terms of the GNU General Public License as published by the Free Software |
11 | | * Foundation, either version 2 of the License, or (at your option) any later |
12 | | * version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
17 | | * details. |
18 | | * |
19 | | * You should have received a copy of the GNU General Public License along with |
20 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | /** |
24 | | * @page pager_message Process a message for display in the pager |
25 | | * |
26 | | * Process a message for display in the pager |
27 | | */ |
28 | | |
29 | | #include "config.h" |
30 | | #include <errno.h> |
31 | | #include <stdbool.h> |
32 | | #include <stdio.h> |
33 | | #include <unistd.h> |
34 | | #include "mutt/lib.h" |
35 | | #include "config/lib.h" |
36 | | #include "email/lib.h" |
37 | | #include "core/lib.h" |
38 | | #include "gui/lib.h" |
39 | | #include "mutt.h" |
40 | | #include "attach/lib.h" |
41 | | #include "index/lib.h" |
42 | | #include "menu/lib.h" |
43 | | #include "ncrypt/lib.h" |
44 | | #include "pager/lib.h" |
45 | | #include "question/lib.h" |
46 | | #include "copy.h" |
47 | | #include "format_flags.h" |
48 | | #include "globals.h" // IWYU pragma: keep |
49 | | #include "hdrline.h" |
50 | | #include "hook.h" |
51 | | #include "keymap.h" |
52 | | #include "mview.h" |
53 | | #include "mx.h" |
54 | | #include "protos.h" |
55 | | #ifdef USE_AUTOCRYPT |
56 | | #include "autocrypt/lib.h" |
57 | | #endif |
58 | | |
59 | | /// Status bar message when entire message is visible in the Pager |
60 | | static const char *ExtPagerProgress = N_("all"); |
61 | | |
62 | | /** |
63 | | * process_protected_headers - Get the protected header and update the index |
64 | | * @param m Mailbox |
65 | | * @param e Email to update |
66 | | */ |
67 | | static void process_protected_headers(struct Mailbox *m, struct Email *e) |
68 | 0 | { |
69 | 0 | struct Envelope *prot_headers = NULL; |
70 | 0 | regmatch_t pmatch[1]; |
71 | |
|
72 | 0 | const bool c_crypt_protected_headers_read = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read"); |
73 | | #ifdef USE_AUTOCRYPT |
74 | | const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt"); |
75 | | if (!c_crypt_protected_headers_read && !c_autocrypt) |
76 | | return; |
77 | | #else |
78 | 0 | if (!c_crypt_protected_headers_read) |
79 | 0 | return; |
80 | 0 | #endif |
81 | | |
82 | | /* Grab protected headers to update in the index */ |
83 | 0 | if (e->security & SEC_SIGN) |
84 | 0 | { |
85 | | /* Don't update on a bad signature. |
86 | | * |
87 | | * This is a simplification. It's possible the headers are in the |
88 | | * encrypted part of a nested encrypt/signed. But properly handling that |
89 | | * case would require more complexity in the decryption handlers, which |
90 | | * I'm not sure is worth it. */ |
91 | 0 | if (!(e->security & SEC_GOODSIGN)) |
92 | 0 | return; |
93 | | |
94 | 0 | if (mutt_is_multipart_signed(e->body) && e->body->parts) |
95 | 0 | { |
96 | 0 | prot_headers = e->body->parts->mime_headers; |
97 | 0 | } |
98 | 0 | else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(e->body)) |
99 | 0 | { |
100 | 0 | prot_headers = e->body->mime_headers; |
101 | 0 | } |
102 | 0 | } |
103 | 0 | if (!prot_headers && (e->security & SEC_ENCRYPT)) |
104 | 0 | { |
105 | 0 | if (((WithCrypto & APPLICATION_PGP) != 0) && |
106 | 0 | (mutt_is_valid_multipart_pgp_encrypted(e->body) || |
107 | 0 | mutt_is_malformed_multipart_pgp_encrypted(e->body))) |
108 | 0 | { |
109 | 0 | prot_headers = e->body->mime_headers; |
110 | 0 | } |
111 | 0 | else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(e->body)) |
112 | 0 | { |
113 | 0 | prot_headers = e->body->mime_headers; |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | | /* Update protected headers in the index and header cache. */ |
118 | 0 | if (c_crypt_protected_headers_read && prot_headers && prot_headers->subject && |
119 | 0 | !mutt_str_equal(e->env->subject, prot_headers->subject)) |
120 | 0 | { |
121 | 0 | if (m->subj_hash && e->env->real_subj) |
122 | 0 | mutt_hash_delete(m->subj_hash, e->env->real_subj, e); |
123 | |
|
124 | 0 | mutt_str_replace(&e->env->subject, prot_headers->subject); |
125 | 0 | FREE(&e->env->disp_subj); |
126 | 0 | const struct Regex *c_reply_regex = cs_subset_regex(NeoMutt->sub, "reply_regex"); |
127 | 0 | if (mutt_regex_capture(c_reply_regex, e->env->subject, 1, pmatch)) |
128 | 0 | { |
129 | 0 | e->env->real_subj = e->env->subject + pmatch[0].rm_eo; |
130 | 0 | if (e->env->real_subj[0] == '\0') |
131 | 0 | e->env->real_subj = NULL; |
132 | 0 | } |
133 | 0 | else |
134 | 0 | { |
135 | 0 | e->env->real_subj = e->env->subject; |
136 | 0 | } |
137 | |
|
138 | 0 | if (m->subj_hash) |
139 | 0 | mutt_hash_insert(m->subj_hash, e->env->real_subj, e); |
140 | |
|
141 | 0 | mx_save_hcache(m, e); |
142 | | |
143 | | /* Also persist back to the message headers if this is set */ |
144 | 0 | const bool c_crypt_protected_headers_save = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_save"); |
145 | 0 | if (c_crypt_protected_headers_save) |
146 | 0 | { |
147 | 0 | e->env->changed |= MUTT_ENV_CHANGED_SUBJECT; |
148 | 0 | e->changed = true; |
149 | 0 | m->changed = true; |
150 | 0 | } |
151 | 0 | } |
152 | |
|
153 | | #ifdef USE_AUTOCRYPT |
154 | | if (c_autocrypt && (e->security & SEC_ENCRYPT) && prot_headers && prot_headers->autocrypt_gossip) |
155 | | { |
156 | | mutt_autocrypt_process_gossip_header(e, prot_headers); |
157 | | } |
158 | | #endif |
159 | 0 | } |
160 | | |
161 | | /** |
162 | | * email_to_file - Decrypt, decode and weed an Email into a file |
163 | | * @param msg Raw Email |
164 | | * @param tempfile Temporary filename for result |
165 | | * @param m Mailbox |
166 | | * @param e Email to display |
167 | | * @param header Header to prefix output (OPTIONAL) |
168 | | * @param wrap_len Width to wrap lines |
169 | | * @param cmflags Message flags, e.g. #MUTT_CM_DECODE |
170 | | * @retval 0 Success |
171 | | * @retval -1 Error |
172 | | * |
173 | | * @note Flags may be added to @a cmflags |
174 | | */ |
175 | | static int email_to_file(struct Message *msg, struct Buffer *tempfile, |
176 | | struct Mailbox *m, struct Email *e, const char *header, |
177 | | int wrap_len, CopyMessageFlags *cmflags) |
178 | 0 | { |
179 | 0 | int rc = 0; |
180 | 0 | pid_t filterpid = -1; |
181 | |
|
182 | 0 | mutt_parse_mime_message(e, msg->fp); |
183 | 0 | mutt_message_hook(m, e, MUTT_MESSAGE_HOOK); |
184 | |
|
185 | 0 | char columns[16] = { 0 }; |
186 | | // win_pager might not be visible and have a size yet, so use win_index |
187 | 0 | snprintf(columns, sizeof(columns), "%d", wrap_len); |
188 | 0 | envlist_set(&EnvList, "COLUMNS", columns, true); |
189 | | |
190 | | /* see if crypto is needed for this message. if so, we should exit curses */ |
191 | 0 | if ((WithCrypto != 0) && e->security) |
192 | 0 | { |
193 | 0 | if (e->security & SEC_ENCRYPT) |
194 | 0 | { |
195 | 0 | if (e->security & APPLICATION_SMIME) |
196 | 0 | crypt_smime_getkeys(e->env); |
197 | 0 | if (!crypt_valid_passphrase(e->security)) |
198 | 0 | goto cleanup; |
199 | | |
200 | 0 | *cmflags |= MUTT_CM_VERIFY; |
201 | 0 | } |
202 | 0 | else if (e->security & SEC_SIGN) |
203 | 0 | { |
204 | | /* find out whether or not the verify signature */ |
205 | | /* L10N: Used for the $crypt_verify_sig prompt */ |
206 | 0 | const enum QuadOption c_crypt_verify_sig = cs_subset_quad(NeoMutt->sub, "crypt_verify_sig"); |
207 | 0 | if (query_quadoption(c_crypt_verify_sig, _("Verify signature?")) == MUTT_YES) |
208 | 0 | { |
209 | 0 | *cmflags |= MUTT_CM_VERIFY; |
210 | 0 | } |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | 0 | if (*cmflags & MUTT_CM_VERIFY || e->security & SEC_ENCRYPT) |
215 | 0 | { |
216 | 0 | if (e->security & APPLICATION_PGP) |
217 | 0 | { |
218 | 0 | if (!TAILQ_EMPTY(&e->env->from)) |
219 | 0 | crypt_pgp_invoke_getkeys(TAILQ_FIRST(&e->env->from)); |
220 | |
|
221 | 0 | crypt_invoke_message(APPLICATION_PGP); |
222 | 0 | } |
223 | |
|
224 | 0 | if (e->security & APPLICATION_SMIME) |
225 | 0 | crypt_invoke_message(APPLICATION_SMIME); |
226 | 0 | } |
227 | |
|
228 | 0 | FILE *fp_filter_out = NULL; |
229 | 0 | buf_mktemp(tempfile); |
230 | 0 | FILE *fp_out = mutt_file_fopen(buf_string(tempfile), "w"); |
231 | 0 | if (!fp_out) |
232 | 0 | { |
233 | 0 | mutt_error(_("Could not create temporary file")); |
234 | 0 | goto cleanup; |
235 | 0 | } |
236 | | |
237 | 0 | const char *const c_display_filter = cs_subset_string(NeoMutt->sub, "display_filter"); |
238 | 0 | if (c_display_filter) |
239 | 0 | { |
240 | 0 | fp_filter_out = fp_out; |
241 | 0 | fp_out = NULL; |
242 | 0 | filterpid = filter_create_fd(c_display_filter, &fp_out, NULL, NULL, -1, |
243 | 0 | fileno(fp_filter_out), -1); |
244 | 0 | if (filterpid < 0) |
245 | 0 | { |
246 | 0 | mutt_error(_("Can't create display filter")); |
247 | 0 | mutt_file_fclose(&fp_filter_out); |
248 | 0 | unlink(buf_string(tempfile)); |
249 | 0 | goto cleanup; |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | 0 | if (header) |
254 | 0 | { |
255 | 0 | fputs(header, fp_out); |
256 | 0 | fputs("\n\n", fp_out); |
257 | 0 | } |
258 | |
|
259 | 0 | const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed"); |
260 | 0 | CopyHeaderFlags chflags = (c_weed ? (CH_WEED | CH_REORDER) : CH_NO_FLAGS) | |
261 | 0 | CH_DECODE | CH_FROM | CH_DISPLAY; |
262 | | #ifdef USE_NOTMUCH |
263 | | if (m->type == MUTT_NOTMUCH) |
264 | | chflags |= CH_VIRTUAL; |
265 | | #endif |
266 | 0 | rc = mutt_copy_message(fp_out, e, msg, *cmflags, chflags, wrap_len); |
267 | |
|
268 | 0 | if (((mutt_file_fclose(&fp_out) != 0) && (errno != EPIPE)) || (rc < 0)) |
269 | 0 | { |
270 | 0 | mutt_error(_("Could not copy message")); |
271 | 0 | if (fp_filter_out) |
272 | 0 | { |
273 | 0 | filter_wait(filterpid); |
274 | 0 | mutt_file_fclose(&fp_filter_out); |
275 | 0 | } |
276 | 0 | mutt_file_unlink(buf_string(tempfile)); |
277 | 0 | goto cleanup; |
278 | 0 | } |
279 | | |
280 | 0 | if (fp_filter_out && (filter_wait(filterpid) != 0)) |
281 | 0 | mutt_any_key_to_continue(NULL); |
282 | |
|
283 | 0 | mutt_file_fclose(&fp_filter_out); /* XXX - check result? */ |
284 | |
|
285 | 0 | if (WithCrypto) |
286 | 0 | { |
287 | | /* update crypto information for this message */ |
288 | 0 | e->security &= ~(SEC_GOODSIGN | SEC_BADSIGN); |
289 | 0 | e->security |= crypt_query(e->body); |
290 | | |
291 | | /* Remove color cache for this message, in case there |
292 | | * are color patterns for both ~g and ~V */ |
293 | 0 | e->attr_color = NULL; |
294 | | |
295 | | /* Process protected headers and autocrypt gossip headers */ |
296 | 0 | process_protected_headers(m, e); |
297 | 0 | } |
298 | |
|
299 | 0 | cleanup: |
300 | 0 | envlist_unset(&EnvList, "COLUMNS"); |
301 | 0 | return rc; |
302 | 0 | } |
303 | | |
304 | | /** |
305 | | * external_pager - Display a message in an external program |
306 | | * @param mv Mailbox view |
307 | | * @param e Email to display |
308 | | * @param command External command to run |
309 | | * @retval 0 Success |
310 | | * @retval -1 Error |
311 | | */ |
312 | | int external_pager(struct MailboxView *mv, struct Email *e, const char *command) |
313 | 0 | { |
314 | 0 | if (!mv || !mv->mailbox) |
315 | 0 | return -1; |
316 | | |
317 | 0 | struct Mailbox *m = mv->mailbox; |
318 | 0 | struct Message *msg = mx_msg_open(m, e); |
319 | 0 | if (!msg) |
320 | 0 | return -1; |
321 | | |
322 | 0 | char buf[1024] = { 0 }; |
323 | 0 | const char *const c_pager_format = cs_subset_string(NeoMutt->sub, "pager_format"); |
324 | 0 | const int screen_width = RootWindow->state.cols; |
325 | 0 | mutt_make_string(buf, sizeof(buf), screen_width, NONULL(c_pager_format), m, |
326 | 0 | -1, e, MUTT_FORMAT_NO_FLAGS, _(ExtPagerProgress)); |
327 | |
|
328 | 0 | struct Buffer *tempfile = buf_pool_get(); |
329 | |
|
330 | 0 | CopyMessageFlags cmflags = MUTT_CM_DECODE | MUTT_CM_DISPLAY | MUTT_CM_CHARCONV; |
331 | 0 | int rc = email_to_file(msg, tempfile, m, e, buf, screen_width, &cmflags); |
332 | 0 | if (rc < 0) |
333 | 0 | goto cleanup; |
334 | | |
335 | 0 | mutt_endwin(); |
336 | |
|
337 | 0 | struct Buffer *cmd = buf_pool_get(); |
338 | 0 | buf_printf(cmd, "%s %s", command, buf_string(tempfile)); |
339 | 0 | int r = mutt_system(buf_string(cmd)); |
340 | 0 | if (r == -1) |
341 | 0 | mutt_error(_("Error running \"%s\""), buf_string(cmd)); |
342 | 0 | unlink(buf_string(tempfile)); |
343 | 0 | buf_pool_release(&cmd); |
344 | |
|
345 | 0 | if (!OptNoCurses) |
346 | 0 | keypad(stdscr, true); |
347 | 0 | if (r != -1) |
348 | 0 | mutt_set_flag(m, e, MUTT_READ, true, true); |
349 | 0 | const bool c_prompt_after = cs_subset_bool(NeoMutt->sub, "prompt_after"); |
350 | 0 | if ((r != -1) && c_prompt_after) |
351 | 0 | { |
352 | 0 | mutt_unget_ch(mutt_any_key_to_continue(_("Command: "))); |
353 | 0 | rc = km_dokey(MENU_PAGER); |
354 | 0 | } |
355 | 0 | else |
356 | 0 | { |
357 | 0 | rc = 0; |
358 | 0 | } |
359 | |
|
360 | 0 | cleanup: |
361 | 0 | mx_msg_close(m, &msg); |
362 | 0 | buf_pool_release(&tempfile); |
363 | 0 | return rc; |
364 | 0 | } |
365 | | |
366 | | /** |
367 | | * notify_crypto - Notify the user about the crypto status of the Email |
368 | | * @param e Email to display |
369 | | * @param msg Raw Email |
370 | | * @param cmflags Message flags, e.g. #MUTT_CM_DECODE |
371 | | */ |
372 | | static void notify_crypto(struct Email *e, struct Message *msg, CopyMessageFlags cmflags) |
373 | 0 | { |
374 | 0 | if ((WithCrypto != 0) && (e->security & APPLICATION_SMIME) && (cmflags & MUTT_CM_VERIFY)) |
375 | 0 | { |
376 | 0 | if (e->security & SEC_GOODSIGN) |
377 | 0 | { |
378 | 0 | if (crypt_smime_verify_sender(e, msg) == 0) |
379 | 0 | mutt_message(_("S/MIME signature successfully verified")); |
380 | 0 | else |
381 | 0 | mutt_error(_("S/MIME certificate owner does not match sender")); |
382 | 0 | } |
383 | 0 | else if (e->security & SEC_PARTSIGN) |
384 | 0 | { |
385 | 0 | mutt_message(_("Warning: Part of this message has not been signed")); |
386 | 0 | } |
387 | 0 | else if (e->security & SEC_SIGN || e->security & SEC_BADSIGN) |
388 | 0 | { |
389 | 0 | mutt_error(_("S/MIME signature could NOT be verified")); |
390 | 0 | } |
391 | 0 | } |
392 | |
|
393 | 0 | if ((WithCrypto != 0) && (e->security & APPLICATION_PGP) && (cmflags & MUTT_CM_VERIFY)) |
394 | 0 | { |
395 | 0 | if (e->security & SEC_GOODSIGN) |
396 | 0 | mutt_message(_("PGP signature successfully verified")); |
397 | 0 | else if (e->security & SEC_PARTSIGN) |
398 | 0 | mutt_message(_("Warning: Part of this message has not been signed")); |
399 | 0 | else if (e->security & SEC_SIGN) |
400 | 0 | mutt_message(_("PGP signature could NOT be verified")); |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | | /** |
405 | | * squash_index_panel - Shrink or hide the Index Panel |
406 | | * @param m Mailbox |
407 | | * @param win_index Index Window |
408 | | * @param win_pager Pager Window |
409 | | */ |
410 | | static void squash_index_panel(struct Mailbox *m, struct MuttWindow *win_index, |
411 | | struct MuttWindow *win_pager) |
412 | 0 | { |
413 | 0 | const short c_pager_index_lines = cs_subset_number(NeoMutt->sub, "pager_index_lines"); |
414 | 0 | if (c_pager_index_lines > 0) |
415 | 0 | { |
416 | 0 | win_index->size = MUTT_WIN_SIZE_FIXED; |
417 | 0 | win_index->req_rows = c_pager_index_lines; |
418 | 0 | win_index->parent->size = MUTT_WIN_SIZE_MINIMISE; |
419 | 0 | } |
420 | 0 | window_set_visible(win_index->parent, (c_pager_index_lines > 0)); |
421 | |
|
422 | 0 | window_set_visible(win_pager->parent, true); |
423 | |
|
424 | 0 | struct MuttWindow *dlg = dialog_find(win_index); |
425 | 0 | mutt_window_reflow(dlg); |
426 | | |
427 | | // Force the menu to reframe itself |
428 | 0 | struct Menu *menu = win_index->wdata; |
429 | 0 | menu_set_index(menu, menu_get_index(menu)); |
430 | 0 | } |
431 | | |
432 | | /** |
433 | | * expand_index_panel - Restore the Index Panel |
434 | | * @param win_index Index Window |
435 | | * @param win_pager Pager Window |
436 | | */ |
437 | | static void expand_index_panel(struct MuttWindow *win_index, struct MuttWindow *win_pager) |
438 | 0 | { |
439 | 0 | win_index->size = MUTT_WIN_SIZE_MAXIMISE; |
440 | 0 | win_index->req_rows = MUTT_WIN_SIZE_UNLIMITED; |
441 | 0 | win_index->parent->size = MUTT_WIN_SIZE_MAXIMISE; |
442 | 0 | win_index->parent->req_rows = MUTT_WIN_SIZE_UNLIMITED; |
443 | 0 | window_set_visible(win_index->parent, true); |
444 | |
|
445 | 0 | window_set_visible(win_pager->parent, false); |
446 | |
|
447 | 0 | struct MuttWindow *dlg = dialog_find(win_index); |
448 | 0 | mutt_window_reflow(dlg); |
449 | 0 | } |
450 | | |
451 | | /** |
452 | | * mutt_display_message - Display a message in the pager |
453 | | * @param win_index Index Window |
454 | | * @param shared Shared Index data |
455 | | * @retval 0 Success |
456 | | * @retval -1 Error |
457 | | */ |
458 | | int mutt_display_message(struct MuttWindow *win_index, struct IndexSharedData *shared) |
459 | 0 | { |
460 | 0 | struct MuttWindow *dlg = dialog_find(win_index); |
461 | 0 | struct MuttWindow *win_pager = window_find_child(dlg, WT_CUSTOM); |
462 | 0 | struct MuttWindow *win_pbar = window_find_child(dlg, WT_STATUS_BAR); |
463 | 0 | struct Buffer *tempfile = buf_pool_get(); |
464 | 0 | struct Message *msg = NULL; |
465 | |
|
466 | 0 | squash_index_panel(shared->mailbox, win_index, win_pager); |
467 | |
|
468 | 0 | int rc = PAGER_LOOP_QUIT; |
469 | 0 | do |
470 | 0 | { |
471 | 0 | msg = mx_msg_open(shared->mailbox, shared->email); |
472 | 0 | if (!msg) |
473 | 0 | break; |
474 | | |
475 | 0 | CopyMessageFlags cmflags = MUTT_CM_DECODE | MUTT_CM_DISPLAY | MUTT_CM_CHARCONV; |
476 | |
|
477 | 0 | buf_reset(tempfile); |
478 | | // win_pager might not be visible and have a size yet, so use win_index |
479 | 0 | rc = email_to_file(msg, tempfile, shared->mailbox, shared->email, NULL, |
480 | 0 | win_index->state.cols, &cmflags); |
481 | 0 | if (rc < 0) |
482 | 0 | break; |
483 | | |
484 | 0 | notify_crypto(shared->email, msg, cmflags); |
485 | | |
486 | | /* Invoke the builtin pager */ |
487 | 0 | struct PagerData pdata = { 0 }; |
488 | 0 | struct PagerView pview = { &pdata }; |
489 | |
|
490 | 0 | pdata.fp = msg->fp; |
491 | 0 | pdata.fname = buf_string(tempfile); |
492 | |
|
493 | 0 | pview.mode = PAGER_MODE_EMAIL; |
494 | 0 | pview.banner = NULL; |
495 | 0 | pview.flags = MUTT_PAGER_MESSAGE | |
496 | 0 | (shared->email->body->nowrap ? MUTT_PAGER_NOWRAP : 0); |
497 | 0 | pview.win_index = win_index; |
498 | 0 | pview.win_pbar = win_pbar; |
499 | 0 | pview.win_pager = win_pager; |
500 | |
|
501 | 0 | rc = mutt_pager(&pview); |
502 | 0 | mx_msg_close(shared->mailbox, &msg); |
503 | 0 | } while (rc == PAGER_LOOP_RELOAD); |
504 | | |
505 | 0 | expand_index_panel(win_index, win_pager); |
506 | |
|
507 | 0 | mx_msg_close(shared->mailbox, &msg); |
508 | 0 | buf_pool_release(&tempfile); |
509 | 0 | return rc; |
510 | 0 | } |