NeoMutt  2025-09-05-7-geaa2bd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
copy.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <inttypes.h> // IWYU pragma: keep
34#include <locale.h>
35#include <stdbool.h>
36#include <string.h>
37#include "mutt/lib.h"
38#include "address/lib.h"
39#include "config/lib.h"
40#include "email/lib.h"
41#include "core/lib.h"
42#include "gui/lib.h"
43#include "mutt.h"
44#include "copy.h"
45#include "expando/lib.h"
46#include "index/lib.h"
47#include "ncrypt/lib.h"
48#include "pager/lib.h"
49#include "send/lib.h"
50#include "globals.h"
51#include "handler.h"
52#include "mx.h"
53#ifdef USE_NOTMUCH
54#include "notmuch/lib.h"
55#include "muttlib.h"
56#endif
57#ifdef ENABLE_NLS
58#include <libintl.h>
59#endif
60
61static int address_header_decode(char **h);
62static int copy_delete_attach(struct Body *b, FILE *fp_in, FILE *fp_out,
63 const char *quoted_date);
64
65ARRAY_HEAD(HeaderArray, char *);
66
76static void add_one_header(struct HeaderArray *headers, int pos, char *value)
77{
78 char **old = ARRAY_GET(headers, pos);
79 if (old && *old)
80 {
81 char *new_value = NULL;
82 mutt_str_asprintf(&new_value, "%s%s", *old, value);
83 FREE(old);
84 FREE(&value);
85 value = new_value;
86 }
87 ARRAY_SET(headers, pos, value);
88}
89
106int mutt_copy_hdr(FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end,
107 CopyHeaderFlags chflags, const char *prefix, int wraplen)
108{
109 bool from = false;
110 bool this_is_from = false;
111 bool ignore = false;
112 char buf[1024] = { 0 }; /* should be long enough to get most fields in one pass */
113 char *nl = NULL;
114 struct HeaderArray headers = ARRAY_HEAD_INITIALIZER;
115 int hdr_count;
116 int x;
117 char *this_one = NULL;
118 size_t this_one_len = 0;
119
120 if (off_start < 0)
121 return -1;
122
123 if (ftello(fp_in) != off_start)
124 if (!mutt_file_seek(fp_in, off_start, SEEK_SET))
125 return -1;
126
127 buf[0] = '\n';
128 buf[1] = '\0';
129
130 if ((chflags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0)
131 {
132 /* Without these flags to complicate things
133 * we can do a more efficient line to line copying */
134 while (ftello(fp_in) < off_end)
135 {
136 nl = strchr(buf, '\n');
137
138 if (!fgets(buf, sizeof(buf), fp_in))
139 break;
140
141 /* Is it the beginning of a header? */
142 if (nl && (buf[0] != ' ') && (buf[0] != '\t'))
143 {
144 ignore = true;
145 if (!from && mutt_str_startswith(buf, "From "))
146 {
147 if ((chflags & CH_FROM) == 0)
148 continue;
149 from = true;
150 }
151 else if ((chflags & CH_NOQFROM) && mutt_istr_startswith(buf, ">From "))
152 {
153 continue;
154 }
155 else if ((buf[0] == '\n') || ((buf[0] == '\r') && (buf[1] == '\n')))
156 {
157 break; /* end of header */
158 }
159
160 if ((chflags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
161 (mutt_istr_startswith(buf, "Status:") || mutt_istr_startswith(buf, "X-Status:")))
162 {
163 continue;
164 }
165 if ((chflags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
166 (mutt_istr_startswith(buf, "Content-Length:") ||
167 mutt_istr_startswith(buf, "Lines:")))
168 {
169 continue;
170 }
171 if ((chflags & CH_UPDATE_REFS) && mutt_istr_startswith(buf, "References:"))
172 {
173 continue;
174 }
175 if ((chflags & CH_UPDATE_IRT) && mutt_istr_startswith(buf, "In-Reply-To:"))
176 {
177 continue;
178 }
179 if (chflags & CH_UPDATE_LABEL && mutt_istr_startswith(buf, "X-Label:"))
180 continue;
181 if ((chflags & CH_UPDATE_SUBJECT) && mutt_istr_startswith(buf, "Subject:"))
182 {
183 continue;
184 }
185
186 ignore = false;
187 }
188
189 if (!ignore && (fputs(buf, fp_out) == EOF))
190 return -1;
191 }
192 return 0;
193 }
194
195 hdr_count = 1;
196 x = 0;
197
198 /* We are going to read and collect the headers in an array
199 * so we are able to do re-ordering.
200 * First count the number of entries in the array */
201 if (chflags & CH_REORDER)
202 {
203 struct ListNode *np = NULL;
204 STAILQ_FOREACH(np, &HeaderOrderList, entries)
205 {
206 mutt_debug(LL_DEBUG3, "Reorder list: %s\n", np->data);
207 hdr_count++;
208 }
209 }
210
211 mutt_debug(LL_DEBUG1, "WEED is %sset\n", (chflags & CH_WEED) ? "" : "not ");
212
213 ARRAY_RESERVE(&headers, hdr_count);
214
215 /* Read all the headers into the array */
216 while (ftello(fp_in) < off_end)
217 {
218 nl = strchr(buf, '\n');
219
220 /* Read a line */
221 if (!fgets(buf, sizeof(buf), fp_in))
222 break;
223
224 /* Is it the beginning of a header? */
225 if (nl && (buf[0] != ' ') && (buf[0] != '\t'))
226 {
227 /* Do we have anything pending? */
228 if (this_one)
229 {
230 if (chflags & CH_DECODE)
231 {
232 if (address_header_decode(&this_one) == 0)
233 rfc2047_decode(&this_one);
234 this_one_len = mutt_str_len(this_one);
235
236 /* Convert CRLF line endings to LF */
237 if ((this_one_len > 2) && (this_one[this_one_len - 2] == '\r') &&
238 (this_one[this_one_len - 1] == '\n'))
239 {
240 this_one[this_one_len - 2] = '\n';
241 this_one[this_one_len - 1] = '\0';
242 }
243 }
244
245 add_one_header(&headers, x, this_one);
246 this_one = NULL;
247 }
248
249 ignore = true;
250 this_is_from = false;
251 if (!from && mutt_str_startswith(buf, "From "))
252 {
253 if ((chflags & CH_FROM) == 0)
254 continue;
255 this_is_from = true;
256 from = true;
257 }
258 else if ((buf[0] == '\n') || ((buf[0] == '\r') && (buf[1] == '\n')))
259 {
260 break; /* end of header */
261 }
262
263 /* note: CH_FROM takes precedence over header weeding. */
264 if (!((chflags & CH_FROM) && (chflags & CH_FORCE_FROM) && this_is_from) &&
265 (chflags & CH_WEED) && mutt_matches_ignore(buf))
266 {
267 continue;
268 }
269 if ((chflags & CH_WEED_DELIVERED) && mutt_istr_startswith(buf, "Delivered-To:"))
270 {
271 continue;
272 }
273 if ((chflags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
274 (mutt_istr_startswith(buf, "Status:") || mutt_istr_startswith(buf, "X-Status:")))
275 {
276 continue;
277 }
278 if ((chflags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
279 (mutt_istr_startswith(buf, "Content-Length:") || mutt_istr_startswith(buf, "Lines:")))
280 {
281 continue;
282 }
283 if ((chflags & CH_MIME))
284 {
285 if (mutt_istr_startswith(buf, "mime-version:"))
286 {
287 continue;
288 }
289 size_t plen = mutt_istr_startswith(buf, "content-");
290 if ((plen != 0) && (mutt_istr_startswith(buf + plen, "transfer-encoding:") ||
291 mutt_istr_startswith(buf + plen, "type:")))
292 {
293 continue;
294 }
295 }
296 if ((chflags & CH_UPDATE_REFS) && mutt_istr_startswith(buf, "References:"))
297 {
298 continue;
299 }
300 if ((chflags & CH_UPDATE_IRT) && mutt_istr_startswith(buf, "In-Reply-To:"))
301 {
302 continue;
303 }
304 if ((chflags & CH_UPDATE_LABEL) && mutt_istr_startswith(buf, "X-Label:"))
305 continue;
306 if ((chflags & CH_UPDATE_SUBJECT) && mutt_istr_startswith(buf, "Subject:"))
307 {
308 continue;
309 }
310
311 /* Find x -- the array entry where this header is to be saved */
312 if (chflags & CH_REORDER)
313 {
314 struct ListNode *np = NULL;
315 x = 0;
316 int match = -1;
317 size_t match_len = 0;
318
319 STAILQ_FOREACH(np, &HeaderOrderList, entries)
320 {
321 size_t hdr_order_len = mutt_str_len(np->data);
322 if (mutt_istrn_equal(buf, np->data, hdr_order_len))
323 {
324 if ((match == -1) || (hdr_order_len > match_len))
325 {
326 match = x;
327 match_len = hdr_order_len;
328 }
329 mutt_debug(LL_DEBUG2, "Reorder: %s matches %s\n", np->data, buf);
330 }
331 x++;
332 }
333 if (match != -1)
334 x = match;
335 }
336
337 ignore = false;
338 } /* If beginning of header */
339
340 if (!ignore)
341 {
342 mutt_debug(LL_DEBUG2, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count);
343 if (this_one)
344 {
345 size_t blen = mutt_str_len(buf);
346
347 MUTT_MEM_REALLOC(&this_one, this_one_len + blen + 1, char);
348 mutt_strn_copy(this_one + this_one_len, buf, blen, blen + 1);
349 this_one_len += blen;
350 }
351 else
352 {
353 this_one = mutt_str_dup(buf);
354 this_one_len = mutt_str_len(this_one);
355 }
356 }
357 } /* while (ftello (fp_in) < off_end) */
358
359 /* Do we have anything pending? -- XXX, same code as in above in the loop. */
360 if (this_one)
361 {
362 if (chflags & CH_DECODE)
363 {
364 if (address_header_decode(&this_one) == 0)
365 rfc2047_decode(&this_one);
366 this_one_len = mutt_str_len(this_one);
367 }
368
369 add_one_header(&headers, x, this_one);
370 this_one = NULL;
371 }
372
373 /* Now output the headers in order */
374 bool error = false;
375 char **hp = NULL;
376 const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
377
378 ARRAY_FOREACH(hp, &headers)
379 {
380 if (!error && hp && *hp)
381 {
382 /* We couldn't do the prefixing when reading because RFC2047
383 * decoding may have concatenated lines. */
384 if (chflags & (CH_DECODE | CH_PREFIX))
385 {
386 const char *pre = (chflags & CH_PREFIX) ? prefix : NULL;
387 wraplen = mutt_window_wrap_cols(wraplen, c_wrap);
388
389 if (mutt_write_one_header(fp_out, 0, *hp, pre, wraplen, chflags, NeoMutt->sub) == -1)
390 {
391 error = true;
392 }
393 }
394 else
395 {
396 if (fputs(*hp, fp_out) == EOF)
397 {
398 error = true;
399 }
400 }
401 }
402
403 FREE(hp);
404 }
405 ARRAY_FREE(&headers);
406
407 if (error)
408 return -1;
409 return 0;
410}
411
423int mutt_copy_header(FILE *fp_in, struct Email *e, FILE *fp_out,
424 CopyHeaderFlags chflags, const char *prefix, int wraplen)
425{
426 char *temp_hdr = NULL;
427 const bool c_weed = (chflags & CH_UPDATE) ? false : cs_subset_bool(NeoMutt->sub, "weed");
428
429 if (e->env)
430 {
431 chflags |= ((e->env->changed & MUTT_ENV_CHANGED_IRT) ? CH_UPDATE_IRT : 0) |
435 }
436
437 if (mutt_copy_hdr(fp_in, fp_out, e->offset, e->body->offset, chflags, prefix, wraplen) == -1)
438 return -1;
439
440 if (chflags & CH_TXTPLAIN)
441 {
442 char chsbuf[128] = { 0 };
443 char buf[128] = { 0 };
444 fputs("MIME-Version: 1.0\n", fp_out);
445 fputs("Content-Transfer-Encoding: 8bit\n", fp_out);
446 fputs("Content-Type: text/plain; charset=", fp_out);
447 const char *const c_charset = cc_charset();
448 mutt_ch_canonical_charset(chsbuf, sizeof(chsbuf), c_charset ? c_charset : "us-ascii");
449 mutt_addr_cat(buf, sizeof(buf), chsbuf, MimeSpecials);
450 fputs(buf, fp_out);
451 fputc('\n', fp_out);
452 }
453
454 if ((chflags & CH_UPDATE_IRT) && !STAILQ_EMPTY(&e->env->in_reply_to) &&
455 !(c_weed && mutt_matches_ignore("In-Reply-To")))
456 {
457 fputs("In-Reply-To:", fp_out);
458 struct ListNode *np = NULL;
459 STAILQ_FOREACH(np, &e->env->in_reply_to, entries)
460 {
461 fputc(' ', fp_out);
462 fputs(np->data, fp_out);
463 }
464 fputc('\n', fp_out);
465 }
466
467 if ((chflags & CH_UPDATE_REFS) && !STAILQ_EMPTY(&e->env->references) &&
468 !(c_weed && mutt_matches_ignore("References")))
469 {
470 fputs("References:", fp_out);
471 mutt_write_references(&e->env->references, fp_out, 0);
472 fputc('\n', fp_out);
473 }
474
475 if ((chflags & CH_UPDATE) && ((chflags & CH_NOSTATUS) == 0))
476 {
477 if ((e->old || e->read))
478 {
479 fputs("Status: ", fp_out);
480 if (e->read)
481 fputs("RO", fp_out);
482 else if (e->old)
483 fputc('O', fp_out);
484 fputc('\n', fp_out);
485 }
486
487 if ((e->flagged || e->replied))
488 {
489 fputs("X-Status: ", fp_out);
490 if (e->replied)
491 fputc('A', fp_out);
492 if (e->flagged)
493 fputc('F', fp_out);
494 fputc('\n', fp_out);
495 }
496 }
497
498 if (chflags & CH_UPDATE_LEN && ((chflags & CH_NOLEN) == 0) &&
499 !(c_weed && mutt_matches_ignore("Content-Length")))
500 {
501 fprintf(fp_out, "Content-Length: " OFF_T_FMT "\n", e->body->length);
502 if ((e->lines != 0) || (e->body->length == 0))
503 fprintf(fp_out, "Lines: %d\n", e->lines);
504 }
505
506#ifdef USE_NOTMUCH
507 if (chflags & CH_VIRTUAL)
508 {
509 /* Add some fake headers based on notmuch data */
510 char *folder = nm_email_get_folder(e);
511 if (folder && !(c_weed && mutt_matches_ignore("Folder")))
512 {
513 char buf[1024] = { 0 };
514 mutt_str_copy(buf, folder, sizeof(buf));
515 mutt_pretty_mailbox(buf, sizeof(buf));
516
517 fputs("Folder: ", fp_out);
518 fputs(buf, fp_out);
519 fputc('\n', fp_out);
520 }
521 }
522#endif
523
524 struct Buffer *tags = buf_pool_get();
525 driver_tags_get(&e->tags, tags);
526 if (!buf_is_empty(tags) && !(c_weed && mutt_matches_ignore("Tags")))
527 {
528 fputs("Tags: ", fp_out);
529 fputs(buf_string(tags), fp_out);
530 fputc('\n', fp_out);
531 }
532 buf_pool_release(&tags);
533
534 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
535 const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
536 if ((chflags & CH_UPDATE_LABEL) && e->env->x_label &&
537 !(c_weed && mutt_matches_ignore("X-Label")))
538 {
539 temp_hdr = e->env->x_label;
540 /* env->x_label isn't currently stored with direct references elsewhere.
541 * Mailbox->label_hash strdups the keys. But to be safe, encode a copy */
542 if (!(chflags & CH_DECODE))
543 {
544 temp_hdr = mutt_str_dup(temp_hdr);
545 rfc2047_encode(&temp_hdr, NULL, sizeof("X-Label:"), c_send_charset);
546 }
547 if (mutt_write_one_header(fp_out, "X-Label", temp_hdr, (chflags & CH_PREFIX) ? prefix : 0,
548 mutt_window_wrap_cols(wraplen, c_wrap), chflags,
549 NeoMutt->sub) == -1)
550 {
551 return -1;
552 }
553 if (!(chflags & CH_DECODE))
554 FREE(&temp_hdr);
555 }
556
557 if ((chflags & CH_UPDATE_SUBJECT) && e->env->subject &&
558 !(c_weed && mutt_matches_ignore("Subject")))
559 {
560 temp_hdr = e->env->subject;
561 /* env->subject is directly referenced in Mailbox->subj_hash, so we
562 * have to be careful not to encode (and thus free) that memory. */
563 if (!(chflags & CH_DECODE))
564 {
565 temp_hdr = mutt_str_dup(temp_hdr);
566 rfc2047_encode(&temp_hdr, NULL, sizeof("Subject:"), c_send_charset);
567 }
568 if (mutt_write_one_header(fp_out, "Subject", temp_hdr, (chflags & CH_PREFIX) ? prefix : 0,
569 mutt_window_wrap_cols(wraplen, c_wrap), chflags,
570 NeoMutt->sub) == -1)
571 {
572 return -1;
573 }
574 if (!(chflags & CH_DECODE))
575 FREE(&temp_hdr);
576 }
577
578 if ((chflags & CH_NONEWLINE) == 0)
579 {
580 if (chflags & CH_PREFIX)
581 fputs(prefix, fp_out);
582 fputc('\n', fp_out); /* add header terminator */
583 }
584
585 if (ferror(fp_out) || feof(fp_out))
586 return -1;
587
588 return 0;
589}
590
602static int count_delete_lines(FILE *fp, struct Body *b, LOFF_T *length, size_t datelen)
603{
604 int dellines = 0;
605
606 if (b->deleted)
607 {
608 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
609 {
610 return -1;
611 }
612 for (long l = b->length; l; l--)
613 {
614 const int ch = getc(fp);
615 if (ch == EOF)
616 break;
617 if (ch == '\n')
618 dellines++;
619 }
620 /* 3 and 89 come from the added header of three lines in
621 * copy_delete_attach(). 89 is the size of the header(including
622 * the newlines, tabs, and a single digit length), not including
623 * the date length. */
624 dellines -= 3;
625 *length -= b->length - (89 + datelen);
626 /* Count the number of digits exceeding the first one to write the size */
627 for (long l = 10; b->length >= l; l *= 10)
628 (*length)++;
629 }
630 else
631 {
632 for (b = b->parts; b; b = b->next)
633 {
634 const int del = count_delete_lines(fp, b, length, datelen);
635 if (del == -1)
636 {
637 return -1;
638 }
639 dellines += del;
640 }
641 }
642 return dellines;
643}
644
656int mutt_copy_message_fp(FILE *fp_out, FILE *fp_in, struct Email *e,
657 CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
658{
659 struct Body *body = e->body;
660 struct Buffer *prefix = buf_pool_get();
661 LOFF_T new_offset = -1;
662 int rc = 0;
663
664 if (cmflags & MUTT_CM_PREFIX)
665 {
666 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
667 if (c_text_flowed)
668 {
669 buf_strcpy(prefix, ">");
670 }
671 else
672 {
673 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
674 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
675 struct Mailbox *m_cur = get_current_mailbox();
676 setlocale(LC_TIME, NONULL(c_attribution_locale));
677 mutt_make_string(prefix, -1, c_indent_string, m_cur, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
678 setlocale(LC_TIME, "");
679 }
680 }
681
682 if ((cmflags & MUTT_CM_NOHEADER) == 0)
683 {
684 if (cmflags & MUTT_CM_PREFIX)
685 {
686 chflags |= CH_PREFIX;
687 }
688 else if (e->attach_del && (chflags & CH_UPDATE_LEN))
689 {
690 int new_lines;
691 int rc_attach_del = -1;
692 LOFF_T new_length = body->length;
693 struct Buffer *quoted_date = NULL;
694
695 quoted_date = buf_pool_get();
696 buf_addch(quoted_date, '"');
697 mutt_date_make_date(quoted_date, cs_subset_bool(NeoMutt->sub, "local_date_header"));
698 buf_addch(quoted_date, '"');
699
700 /* Count the number of lines and bytes to be deleted */
701 if (!mutt_file_seek(fp_in, body->offset, SEEK_SET))
702 {
703 goto attach_del_cleanup;
704 }
705 const int del = count_delete_lines(fp_in, body, &new_length, buf_len(quoted_date));
706 if (del == -1)
707 {
708 goto attach_del_cleanup;
709 }
710 new_lines = e->lines - del;
711
712 /* Copy the headers */
713 if (mutt_copy_header(fp_in, e, fp_out, chflags | CH_NOLEN | CH_NONEWLINE, NULL, wraplen))
714 goto attach_del_cleanup;
715 fprintf(fp_out, "Content-Length: " OFF_T_FMT "\n", new_length);
716 if (new_lines <= 0)
717 new_lines = 0;
718 else
719 fprintf(fp_out, "Lines: %d\n", new_lines);
720
721 putc('\n', fp_out);
722 if (ferror(fp_out) || feof(fp_out))
723 goto attach_del_cleanup;
724 new_offset = ftello(fp_out);
725
726 /* Copy the body */
727 if (!mutt_file_seek(fp_in, body->offset, SEEK_SET))
728 goto attach_del_cleanup;
729 if (copy_delete_attach(body, fp_in, fp_out, buf_string(quoted_date)))
730 goto attach_del_cleanup;
731
732 buf_pool_release(&quoted_date);
733
734 LOFF_T fail = ((ftello(fp_out) - new_offset) - new_length);
735 if (fail)
736 {
737 mutt_error(ngettext("The length calculation was wrong by %ld byte",
738 "The length calculation was wrong by %ld bytes", fail),
739 (long) fail);
740 new_length += fail;
741 }
742
743 /* Update original message if we are sync'ing a mailfolder */
744 if (cmflags & MUTT_CM_UPDATE)
745 {
746 e->attach_del = false;
747 e->lines = new_lines;
748 body->offset = new_offset;
749
750 body->length = new_length;
751 mutt_body_free(&body->parts);
752 }
753
754 rc_attach_del = 0;
755
756 attach_del_cleanup:
757 buf_pool_release(&quoted_date);
758 rc = rc_attach_del;
759 goto done;
760 }
761
762 if (mutt_copy_header(fp_in, e, fp_out, chflags,
763 (chflags & CH_PREFIX) ? buf_string(prefix) : NULL, wraplen) == -1)
764 {
765 rc = -1;
766 goto done;
767 }
768
769 new_offset = ftello(fp_out);
770 }
771
772 if (cmflags & MUTT_CM_DECODE)
773 {
774 /* now make a text/plain version of the message */
775 struct State state = { 0 };
776 state.fp_in = fp_in;
777 state.fp_out = fp_out;
778 if (cmflags & MUTT_CM_PREFIX)
779 state.prefix = buf_string(prefix);
780 if (cmflags & MUTT_CM_DISPLAY)
781 {
782 state.flags |= STATE_DISPLAY;
783 state.wraplen = wraplen;
784 const char *const c_pager = pager_get_pager(NeoMutt->sub);
785 if (!c_pager)
786 state.flags |= STATE_PAGER;
787 }
788 if (cmflags & MUTT_CM_PRINTING)
789 state.flags |= STATE_PRINTING;
790 if (cmflags & MUTT_CM_WEED)
791 state.flags |= STATE_WEED;
792 if (cmflags & MUTT_CM_CHARCONV)
793 state.flags |= STATE_CHARCONV;
794 if (cmflags & MUTT_CM_REPLYING)
795 state.flags |= STATE_REPLYING;
796
797 if ((WithCrypto != 0) && cmflags & MUTT_CM_VERIFY)
798 state.flags |= STATE_VERIFY;
799
800 rc = mutt_body_handler(body, &state);
801 }
802 else if ((WithCrypto != 0) && (cmflags & MUTT_CM_DECODE_CRYPT) && (e->security & SEC_ENCRYPT))
803 {
804 struct Body *cur = NULL;
805 FILE *fp = NULL;
806
807 if (((WithCrypto & APPLICATION_PGP) != 0) && (cmflags & MUTT_CM_DECODE_PGP) &&
809 {
810 if (crypt_pgp_decrypt_mime(fp_in, &fp, e->body, &cur))
811 {
812 rc = 1;
813 goto done;
814 }
815 fputs("MIME-Version: 1.0\n", fp_out);
816 }
817
818 if (((WithCrypto & APPLICATION_SMIME) != 0) && (cmflags & MUTT_CM_DECODE_SMIME) &&
820 {
821 if (crypt_smime_decrypt_mime(fp_in, &fp, e->body, &cur))
822 {
823 rc = 1;
824 goto done;
825 }
826 }
827
828 if (!cur)
829 {
830 mutt_error(_("No decryption engine available for message"));
831 rc = 1;
832 goto done;
833 }
834
835 mutt_write_mime_header(cur, fp_out, NeoMutt->sub);
836 fputc('\n', fp_out);
837
838 if (!mutt_file_seek(fp, cur->offset, SEEK_SET))
839 {
840 rc = 1;
841 goto done;
842 }
843
844 if (mutt_file_copy_bytes(fp, fp_out, cur->length) == -1)
845 {
846 mutt_file_fclose(&fp);
847 mutt_body_free(&cur);
848 rc = 1;
849 goto done;
850 }
851 mutt_body_free(&cur);
852 mutt_file_fclose(&fp);
853 }
854 else
855 {
856 if (!mutt_file_seek(fp_in, body->offset, SEEK_SET))
857 {
858 rc = 1;
859 goto done;
860 }
861 if (cmflags & MUTT_CM_PREFIX)
862 {
863 int c;
864 size_t bytes = body->length;
865
866 fputs(buf_string(prefix), fp_out);
867
868 while (((c = fgetc(fp_in)) != EOF) && bytes--)
869 {
870 fputc(c, fp_out);
871 if (c == '\n')
872 {
873 fputs(buf_string(prefix), fp_out);
874 }
875 }
876 }
877 else if (mutt_file_copy_bytes(fp_in, fp_out, body->length) == -1)
878 {
879 rc = 1;
880 goto done;
881 }
882 }
883
884 if ((cmflags & MUTT_CM_UPDATE) && ((cmflags & MUTT_CM_NOHEADER) == 0) &&
885 (new_offset != -1))
886 {
887 body->offset = new_offset;
888 mutt_body_free(&body->parts);
889 }
890
891done:
892 buf_pool_release(&prefix);
893 return rc;
894}
895
910int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg,
911 CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
912{
913 if (!msg || !e->body)
914 {
915 return -1;
916 }
917 if (fp_out == msg->fp)
918 {
919 mutt_debug(LL_DEBUG1, "trying to read/write from/to the same FILE*!\n");
920 return -1;
921 }
922
923 int rc = mutt_copy_message_fp(fp_out, msg->fp, e, cmflags, chflags, wraplen);
924 if ((rc == 0) && (ferror(fp_out) || feof(fp_out)))
925 {
926 mutt_debug(LL_DEBUG1, "failed to detect EOF!\n");
927 rc = -1;
928 }
929 return rc;
930}
931
943static int append_message(struct Mailbox *dest, FILE *fp_in, struct Mailbox *src,
944 struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
945{
946 char buf[256] = { 0 };
947 struct Message *msg = NULL;
948 int rc;
949
950 if (!mutt_file_seek(fp_in, e->offset, SEEK_SET))
951 return -1;
952 if (!fgets(buf, sizeof(buf), fp_in))
953 return -1;
954
955 msg = mx_msg_open_new(dest, e, is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
956 if (!msg)
957 return -1;
958 if ((dest->type == MUTT_MBOX) || (dest->type == MUTT_MMDF))
959 chflags |= CH_FROM | CH_FORCE_FROM;
960 chflags |= ((dest->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
961 rc = mutt_copy_message_fp(msg->fp, fp_in, e, cmflags, chflags, 0);
962 if (mx_msg_commit(dest, msg) != 0)
963 rc = -1;
964
965#ifdef USE_NOTMUCH
966 if (msg->committed_path && (dest->type == MUTT_MAILDIR) && (src->type == MUTT_NOTMUCH))
967 nm_update_filename(src, NULL, msg->committed_path, e);
968#endif
969
970 mx_msg_close(dest, &msg);
971 return rc;
972}
973
985int mutt_append_message(struct Mailbox *m_dst, struct Mailbox *m_src,
986 struct Email *e, struct Message *msg,
987 CopyMessageFlags cmflags, CopyHeaderFlags chflags)
988{
989 if (!e)
990 return -1;
991
992 const bool own_msg = !msg;
993 if (own_msg && !(msg = mx_msg_open(m_src, e)))
994 {
995 return -1;
996 }
997
998 int rc = append_message(m_dst, msg->fp, m_src, e, cmflags, chflags);
999 if (own_msg)
1000 {
1001 mx_msg_close(m_src, &msg);
1002 }
1003 return rc;
1004}
1005
1019static int copy_delete_attach(struct Body *b, FILE *fp_in, FILE *fp_out, const char *quoted_date)
1020{
1021 struct Body *part = NULL;
1022
1023 for (part = b->parts; part; part = part->next)
1024 {
1025 if (part->deleted || part->parts)
1026 {
1027 /* Copy till start of this part */
1028 if (mutt_file_copy_bytes(fp_in, fp_out, part->hdr_offset - ftello(fp_in)))
1029 {
1030 return -1;
1031 }
1032
1033 if (part->deleted)
1034 {
1035 /* If this is modified, count_delete_lines() needs to be changed too */
1036 fprintf(fp_out,
1037 "Content-Type: message/external-body; access-type=x-mutt-deleted;\n"
1038 "\texpiration=%s; length=" OFF_T_FMT "\n"
1039 "\n",
1040 quoted_date, part->length);
1041 if (ferror(fp_out))
1042 {
1043 return -1;
1044 }
1045
1046 /* Copy the original mime headers */
1047 if (mutt_file_copy_bytes(fp_in, fp_out, part->offset - ftello(fp_in)))
1048 {
1049 return -1;
1050 }
1051
1052 /* Skip the deleted body */
1053 if (!mutt_file_seek(fp_in, part->offset + part->length, SEEK_SET))
1054 {
1055 return -1;
1056 }
1057 }
1058 else
1059 {
1060 if (copy_delete_attach(part, fp_in, fp_out, quoted_date))
1061 {
1062 return -1;
1063 }
1064 }
1065 }
1066 }
1067
1068 /* Copy the last parts */
1069 if (mutt_file_copy_bytes(fp_in, fp_out, b->offset + b->length - ftello(fp_in)))
1070 return -1;
1071
1072 return 0;
1073}
1074
1081static int address_header_decode(char **h)
1082{
1083 char *s = *h;
1084 size_t l;
1085 bool rp = false;
1086
1087 switch (mutt_tolower(*s))
1088 {
1089 case 'b':
1090 {
1091 if (!(l = mutt_istr_startswith(s, "bcc:")))
1092 return 0;
1093 break;
1094 }
1095 case 'c':
1096 {
1097 if (!(l = mutt_istr_startswith(s, "cc:")))
1098 return 0;
1099 break;
1100 }
1101 case 'f':
1102 {
1103 if (!(l = mutt_istr_startswith(s, "from:")))
1104 return 0;
1105 break;
1106 }
1107 case 'm':
1108 {
1109 if (!(l = mutt_istr_startswith(s, "mail-followup-to:")))
1110 return 0;
1111 break;
1112 }
1113 case 'r':
1114 {
1115 if ((l = mutt_istr_startswith(s, "return-path:")))
1116 {
1117 rp = true;
1118 break;
1119 }
1120 else if ((l = mutt_istr_startswith(s, "reply-to:")))
1121 {
1122 break;
1123 }
1124 return 0;
1125 }
1126 case 's':
1127 {
1128 if (!(l = mutt_istr_startswith(s, "sender:")))
1129 return 0;
1130 break;
1131 }
1132 case 't':
1133 {
1134 if (!(l = mutt_istr_startswith(s, "to:")))
1135 return 0;
1136 break;
1137 }
1138 default:
1139 return 0;
1140 }
1141
1142 struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
1143 mutt_addrlist_parse(&al, s + l);
1144 if (TAILQ_EMPTY(&al))
1145 return 0;
1146
1149 struct Address *a = NULL;
1150 TAILQ_FOREACH(a, &al, entries)
1151 {
1152 if (a->personal)
1153 {
1155 }
1156 }
1157
1158 /* angle brackets for return path are mandated by RFC5322,
1159 * so leave Return-Path as-is */
1160 if (rp)
1161 {
1162 *h = mutt_str_dup(s);
1163 }
1164 else
1165 {
1166 struct Buffer buf = { 0 };
1167 (*h)[l - 1] = '\0';
1168 mutt_addrlist_write_wrap(&al, &buf, *h);
1169 buf_addch(&buf, '\n');
1170 *h = buf.data;
1171 }
1172
1174
1175 FREE(&s);
1176 return 1;
1177}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1460
void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
Copy a string and wrap it in quotes if it contains special characters.
Definition: address.c:708
size_t mutt_addrlist_write_wrap(const struct AddressList *al, struct Buffer *buf, const char *header)
Write an AddressList to a buffer, perform line wrapping.
Definition: address.c:1189
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1378
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:480
Email Address Handling.
#define ARRAY_SET(head, idx, elem)
Set an element in the array.
Definition: array.h:123
#define ARRAY_RESERVE(head, num)
Reserve memory for the array.
Definition: array.h:189
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:214
#define ARRAY_HEAD(name, type)
Define a named struct for arrays of elements of a certain type.
Definition: array.h:47
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:109
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
void buf_dequote_comment(struct Buffer *buf)
Un-escape characters in an email address comment.
Definition: buffer.c:434
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:439
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:242
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
Definition: config_type.c:361
Convenience wrapper for the config headers.
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:116
static int append_message(struct Mailbox *dest, FILE *fp_in, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Appends a copy of the given message to a mailbox.
Definition: copy.c:943
static int address_header_decode(char **h)
Parse an email's headers.
Definition: copy.c:1081
int mutt_copy_header(FILE *fp_in, struct Email *e, FILE *fp_out, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy Email header.
Definition: copy.c:423
int mutt_append_message(struct Mailbox *m_dst, struct Mailbox *m_src, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:985
static void add_one_header(struct HeaderArray *headers, int pos, char *value)
Add a header to a Headers array.
Definition: copy.c:76
int mutt_copy_hdr(FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy header from one file to another.
Definition: copy.c:106
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:910
static int count_delete_lines(FILE *fp, struct Body *b, LOFF_T *length, size_t datelen)
Count lines to be deleted in this email body.
Definition: copy.c:602
int mutt_copy_message_fp(FILE *fp_out, FILE *fp_in, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Make a copy of a message from a FILE pointer.
Definition: copy.c:656
static int copy_delete_attach(struct Body *b, FILE *fp_in, FILE *fp_out, const char *quoted_date)
Copy a message, deleting marked attachments.
Definition: copy.c:1019
Duplicate the structure of an entire email.
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:56
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition: copy.h:43
#define MUTT_CM_REPLYING
Replying the message.
Definition: copy.h:46
#define MUTT_CM_PREFIX
Quote the header and body.
Definition: copy.h:39
#define CH_XMIT
Transmitting this message? (Ignore Lines: and Content-Length:)
Definition: copy.h:57
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:42
#define MUTT_CM_VERIFY
Do signature verification.
Definition: copy.h:49
#define CH_PREFIX
Quote header using $indent_string string?
Definition: copy.h:59
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:54
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:60
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:58
#define MUTT_CM_DECODE_PGP
Used for decoding PGP messages.
Definition: copy.h:47
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:40
#define CH_NONEWLINE
Don't output terminating newline after the header.
Definition: copy.h:62
#define CH_WEED_DELIVERED
Weed eventual Delivered-To headers.
Definition: copy.h:67
#define CH_UPDATE_LABEL
Update X-Label: from email->env->x_label?
Definition: copy.h:73
#define CH_WEED
Weed the headers?
Definition: copy.h:55
#define CH_REORDER
Re-order output of headers (specified by 'hdr_order')
Definition: copy.h:61
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:44
#define MUTT_CM_DECODE_SMIME
Used for decoding S/MIME messages.
Definition: copy.h:48
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:63
#define CH_UPDATE_REFS
Update References:
Definition: copy.h:71
#define CH_NOQFROM
Ignore ">From " line.
Definition: copy.h:69
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:52
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:64
#define CH_UPDATE_IRT
Update In-Reply-To:
Definition: copy.h:70
#define MUTT_CM_PRINTING
Printing the message - display light.
Definition: copy.h:45
#define CH_FORCE_FROM
Give CH_FROM precedence over CH_WEED?
Definition: copy.h:68
#define MUTT_CM_DECODE_CRYPT
Definition: copy.h:50
#define MUTT_CM_NOHEADER
Don't copy the message header.
Definition: copy.h:38
#define CH_TXTPLAIN
Generate text/plain MIME headers.
Definition: copy.h:65
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition: copy.h:36
#define CH_VIRTUAL
Write virtual header lines too.
Definition: copy.h:75
#define CH_UPDATE_SUBJECT
Update Subject: protected header update.
Definition: copy.h:74
#define CH_NOLEN
Don't write Content-Length: and Lines:
Definition: copy.h:66
#define MUTT_CM_DISPLAY
Output is displayed to the user.
Definition: copy.h:41
Convenience wrapper for the core headers.
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:46
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:45
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
int crypt_pgp_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:210
int crypt_smime_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:432
int mutt_tolower(int arg)
Wrapper for tolower(3)
Definition: ctype.c:125
int mutt_make_string(struct Buffer *buf, size_t max_cols, const struct Expando *exp, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: dlg_index.c:803
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:58
Structs that make up an email.
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition: parse.c:355
#define MUTT_ENV_CHANGED_SUBJECT
Protected header update.
Definition: envelope.h:37
#define MUTT_ENV_CHANGED_XLABEL
X-Label edited.
Definition: envelope.h:36
#define MUTT_ENV_CHANGED_IRT
In-Reply-To changed to link/break threads.
Definition: envelope.h:34
#define MUTT_ENV_CHANGED_REFS
References changed to break thread.
Definition: envelope.h:35
Parse Expando string.
int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
Copy some content from one file to another.
Definition: file.c:195
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:655
#define mutt_file_fclose(FP)
Definition: file.h:139
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:49
struct ListHead HeaderOrderList
List of header fields in the order they should be displayed.
Definition: globals.c:46
Global variables.
#define mutt_error(...)
Definition: logging2.h:93
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
Convenience wrapper for the gui headers.
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1632
Decide how to display email content.
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:756
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition: header.c:519
int mutt_write_one_header(FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, CopyHeaderFlags chflags, struct ConfigSubset *sub)
Write one header line to a file.
Definition: header.c:423
GUI manage the main index (list of emails)
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition: index.c:721
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:46
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1785
#define FREE(x)
Definition: memory.h:62
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:50
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition: charset.c:374
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:397
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
#define STATE_PAGER
Output will be displayed in the Pager.
Definition: state.h:42
#define STATE_WEED
Weed headers even when not in display mode.
Definition: state.h:36
#define STATE_DISPLAY
Output is displayed to the user.
Definition: state.h:33
#define STATE_REPLYING
Are we replying?
Definition: state.h:39
#define STATE_VERIFY
Perform signature verification.
Definition: state.h:34
#define STATE_CHARCONV
Do character set conversions.
Definition: state.h:37
#define STATE_PRINTING
Are we printing? - STATE_DISPLAY "light".
Definition: state.h:38
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:802
char * mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
Copy a sub-string into a buffer.
Definition: string.c:361
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:580
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:243
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:454
Many unsorted constants and some structs.
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:337
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1185
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition: mx.c:1139
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1044
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1164
API for mailboxes.
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:39
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:38
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:96
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:97
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:84
#define WithCrypto
Definition: lib.h:122
Notmuch virtual mailbox type.
char * nm_email_get_folder(struct Email *e)
Get the folder for a Email.
Definition: notmuch.c:1491
const char * pager_get_pager(struct ConfigSubset *sub)
Get the value of $pager.
Definition: config.c:111
GUI display a file/email/help in a viewport with paging.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:782
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:390
#define STAILQ_EMPTY(head)
Definition: queue.h:382
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:694
#define TAILQ_EMPTY(head)
Definition: queue.h:778
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: render.h:33
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:628
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:661
void rfc2047_decode_addrlist(struct AddressList *al)
Decode any RFC2047 headers in an Address list.
Definition: rfc2047.c:801
Convenience wrapper for the send headers.
#define NONULL(x)
Definition: string2.h:36
An email address.
Definition: address.h:36
struct Buffer * personal
Real name of address.
Definition: address.h:37
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:73
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
bool deleted
Attachment marked for deletion.
Definition: body.h:88
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct Body * next
next attachment in the list
Definition: body.h:72
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:81
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
String manipulation buffer.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:37
The envelope/body of an email.
Definition: email.h:39
bool read
Email is read.
Definition: email.h:50
struct Envelope * env
Envelope information.
Definition: email.h:68
int lines
How many lines in the body of this message?
Definition: email.h:62
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:43
struct Body * body
List of MIME parts.
Definition: email.h:69
bool old
Email is seen, but unread.
Definition: email.h:49
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:71
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:99
bool flagged
Marked important?
Definition: email.h:47
bool replied
Email has been replied to.
Definition: email.h:51
struct TagList tags
For drivers that support server tagging.
Definition: email.h:72
char *const subject
Email's subject.
Definition: envelope.h:70
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:90
struct ListHead references
message references (in reverse order)
Definition: envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:84
char * x_label
X-Label.
Definition: envelope.h:76
Parsed Expando trees.
Definition: expando.h:41
A List node for strings.
Definition: list.h:37
char * data
String.
Definition: list.h:38
A mailbox.
Definition: mailbox.h:79
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
A local copy of an email.
Definition: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
char * committed_path
the final path generated by mx_msg_commit()
Definition: message.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
String list.
Definition: slist.h:37
Keep track when processing files.
Definition: state.h:48
int wraplen
Width to wrap lines to (when flags & STATE_DISPLAY)
Definition: state.h:53
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition: state.h:52
FILE * fp_out
File to write to.
Definition: state.h:50
FILE * fp_in
File to read from.
Definition: state.h:49
const char * prefix
String to add to the beginning of each output line.
Definition: state.h:51
void driver_tags_get(struct TagList *tl, struct Buffer *tags)
Get tags all tags separated by space.
Definition: tags.c:164