NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
header.c
Go to the documentation of this file.
1
25
31
32#include "config.h"
33#include <stdbool.h>
34#include <stdlib.h>
35#include <string.h>
36#include "mutt/lib.h"
37#include "address/lib.h"
38#include "config/lib.h"
39#include "email/lib.h"
40#include "gui/lib.h"
41#include "header.h"
42#include "globals.h"
43#ifdef USE_AUTOCRYPT
44#include "autocrypt/lib.h"
45#endif
46
52static const char *const UserhdrsOverrideHeaders[] = {
53 "content-type:",
54 "user-agent:",
55};
56
65
74
85static int print_val(FILE *fp, const char *pfx, const char *value,
86 CopyHeaderFlags chflags, size_t col)
87{
88 while (value && (value[0] != '\0'))
89 {
90 if (fputc(*value, fp) == EOF)
91 return -1;
92 /* corner-case: break words longer than 998 chars by force,
93 * mandated by RFC5322 */
94 if (!(chflags & CH_DISPLAY) && (++col >= 998))
95 {
96 if (fputs("\n ", fp) < 0)
97 return -1;
98 col = 1;
99 }
100 if (*value == '\n')
101 {
102 if ((value[1] != '\0') && pfx && (pfx[0] != '\0') && (fputs(pfx, fp) == EOF))
103 return -1;
104 /* for display, turn folding spaces into folding tabs */
105 if ((chflags & CH_DISPLAY) && ((value[1] == ' ') || (value[1] == '\t')))
106 {
107 value++;
108 while ((value[0] != '\0') && ((value[0] == ' ') || (value[0] == '\t')))
109 value++;
110 if (fputc('\t', fp) == EOF)
111 return -1;
112 continue;
113 }
114 }
115 value++;
116 }
117 return 0;
118}
119
132static int fold_one_header(FILE *fp, const char *tag, const char *value, size_t vlen,
133 const char *pfx, int wraplen, CopyHeaderFlags chflags)
134{
135 if (!value || (*value == '\0') || !vlen)
136 return 0;
137
138 const char *p = value;
139 char buf[8192] = { 0 };
140 int first = 1, col = 0, l = 0;
141 const bool display = (chflags & CH_DISPLAY);
142
143 mutt_debug(LL_DEBUG5, "pfx=[%s], tag=[%s], flags=%d value=[%.*s]\n", pfx, tag,
144 chflags, (int) ((value[vlen - 1] == '\n') ? vlen - 1 : vlen), value);
145
146 if (tag && *tag && (fprintf(fp, "%s%s: ", NONULL(pfx), tag) < 0))
147 return -1;
148 col = mutt_str_len(tag) + ((tag && (tag[0] != '\0')) ? 2 : 0) + mutt_str_len(pfx);
149
150 while (p && (p[0] != '\0'))
151 {
152 int fold = 0;
153
154 /* find the next word and place it in 'buf'. it may start with
155 * whitespace we can fold before */
156 const char *next = mutt_str_find_word(p);
157 l = MIN(sizeof(buf) - 1, next - p);
158 memcpy(buf, p, l);
159 buf[l] = '\0';
160
161 /* determine width: character cells for display, bytes for sending
162 * (we get pure ascii only) */
163 const int w = mutt_mb_width(buf, col, display);
164 const int enc = mutt_str_startswith(buf, "=?");
165
166 mutt_debug(LL_DEBUG5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n",
167 (buf[0] == '\n' ? "\\n" : buf), col, w, *next);
168
169 /* insert a folding \n before the current word's lwsp except for
170 * header name, first word on a line (word longer than wrap width)
171 * and encoded words */
172 if (!first && !enc && col && ((col + w) >= wraplen))
173 {
174 col = mutt_str_len(pfx);
175 fold = 1;
176 if (fprintf(fp, "\n%s", NONULL(pfx)) <= 0)
177 return -1;
178 }
179
180 /* print the actual word; for display, ignore leading ws for word
181 * and fold with tab for readability */
182 if (display && fold)
183 {
184 char *pc = buf;
185 while ((pc[0] != '\0') && ((pc[0] == ' ') || (pc[0] == '\t')))
186 {
187 pc++;
188 col--;
189 }
190 if (fputc('\t', fp) == EOF)
191 return -1;
192 if (print_val(fp, pfx, pc, chflags, col) < 0)
193 return -1;
194 col += 8;
195 }
196 else if (print_val(fp, pfx, buf, chflags, col) < 0)
197 {
198 return -1;
199 }
200 col += w;
201
202 /* if the current word ends in \n, ignore all its trailing spaces
203 * and reset column; this prevents us from putting only spaces (or
204 * even none) on a line if the trailing spaces are located at our
205 * current line width
206 * XXX this covers ASCII space only, for display we probably
207 * want something like iswspace() here */
208 const char *sp = next;
209 while ((sp[0] != '\0') && ((sp[0] == ' ') || (sp[0] == '\t')))
210 sp++;
211 if (sp[0] == '\n')
212 {
213 if (sp[1] == '\0')
214 break;
215 next = sp;
216 col = 0;
217 }
218
219 p = next;
220 first = 0;
221 }
222
223 /* if we have printed something but didn't \n-terminate it, do it
224 * except the last word we printed ended in \n already */
225 if (col && ((l == 0) || (buf[l - 1] != '\n')))
226 if (putc('\n', fp) == EOF)
227 return -1;
228
229 return 0;
230}
231
239static char *unfold_header(char *s)
240{
241 char *p = s;
242 char *q = s;
243
244 while (p && (p[0] != '\0'))
245 {
246 /* remove CRLF prior to FWSP, turn \t into ' ' */
247 if ((p[0] == '\r') && (p[1] == '\n') && ((p[2] == ' ') || (p[2] == '\t')))
248 {
249 *q++ = ' ';
250 p += 3;
251 continue;
252 }
253 else if ((p[0] == '\n') && ((p[1] == ' ') || (p[1] == '\t')))
254 {
255 /* remove LF prior to FWSP, turn \t into ' ' */
256 *q++ = ' ';
257 p += 2;
258 continue;
259 }
260 *q++ = *p++;
261 }
262 if (q)
263 q[0] = '\0';
264
265 return s;
266}
267
276static int userhdrs_override_cmp(const void *a, const void *b)
277{
278 const char *ca = a;
279 const char *cb = *(const char **) b;
280 return mutt_istrn_cmp(ca, cb, strlen(cb));
281}
282
296static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char *pfx,
297 const char *start, const char *end, CopyHeaderFlags chflags)
298{
299 const char *t = strchr(start, ':');
300 if (!t || (t >= end))
301 {
302 mutt_debug(LL_DEBUG1, "#2 warning: header not in 'key: value' format!\n");
303 return 0;
304 }
305
306 const size_t vallen = end - start;
307 const bool short_enough = (pfxw + max <= wraplen);
308
309 mutt_debug((short_enough ? LL_DEBUG2 : LL_DEBUG5), "buf[%s%.*s] %s, max width = %d %s %d\n",
310 NONULL(pfx), (int) (vallen - 1) /* skip newline */, start,
311 (short_enough ? "short enough" : "too long"), max,
312 (short_enough ? "<=" : ">"), wraplen);
313
314 int rc = 0;
315 const char *valbuf = NULL, *tagbuf = NULL;
316 const bool is_from = (vallen > 5) && mutt_istr_startswith(start, "from ");
317
318 /* only pass through folding machinery if necessary for sending,
319 * never wrap From_ headers on sending */
320 if (!(chflags & CH_DISPLAY) && (short_enough || is_from))
321 {
322 if (pfx && *pfx)
323 {
324 if (fputs(pfx, fp) == EOF)
325 {
326 return -1;
327 }
328 }
329
330 valbuf = mutt_strn_dup(start, end - start);
331 rc = print_val(fp, pfx, valbuf, chflags, mutt_str_len(pfx));
332 }
333 else
334 {
335 if (!is_from)
336 {
337 tagbuf = mutt_strn_dup(start, t - start);
338 /* skip over the colon separating the header field name and value */
339 t++;
340
341 /* skip over any leading whitespace (WSP, as defined in RFC5322)
342 * NOTE: mutt_str_skip_email_wsp() does the wrong thing here.
343 * See tickets 3609 and 3716. */
344 while ((*t == ' ') || (*t == '\t'))
345 t++;
346 }
347 const char *s = is_from ? start : t;
348 valbuf = mutt_strn_dup(s, end - s);
349 rc = fold_one_header(fp, tagbuf, valbuf, end - s, pfx, wraplen, chflags);
350 }
351
352 FREE(&tagbuf);
353 FREE(&valbuf);
354 return rc;
355}
356
365static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs,
366 bool privacy, struct ConfigSubset *sub)
367{
368 struct UserHdrsOverride overrides = { { 0 } };
369
370 struct ListNode *tmp = NULL;
371 STAILQ_FOREACH(tmp, userhdrs, entries)
372 {
373 char *const colon = strchr(NONULL(tmp->data), ':');
374 if (!colon)
375 {
376 continue;
377 }
378
379 const char *const value = mutt_str_skip_email_wsp(colon + 1);
380 if (*value == '\0')
381 {
382 continue; /* don't emit empty fields. */
383 }
384
385 /* check whether the current user-header is an override */
386 size_t cur_override = ICONV_ILLEGAL_SEQ;
387 const char *const *idx = bsearch(tmp->data, UserhdrsOverrideHeaders,
389 sizeof(char *), userhdrs_override_cmp);
390 if (idx)
391 {
392 cur_override = idx - UserhdrsOverrideHeaders;
393 overrides.is_overridden[cur_override] = true;
394 }
395
396 if (privacy && (cur_override == USERHDRS_OVERRIDE_USER_AGENT))
397 {
398 continue;
399 }
400
401 *colon = '\0';
402 mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS, sub);
403 *colon = ':';
404 }
405
406 return overrides;
407}
408
424int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
425 const char *pfx, int wraplen, CopyHeaderFlags chflags,
426 struct ConfigSubset *sub)
427{
428 char *last = NULL, *line = NULL;
429 int max = 0, w, rc = -1;
430 int pfxw = mutt_strwidth(pfx);
431 char *v = mutt_str_dup(value);
432 bool display = (chflags & CH_DISPLAY);
433
434 const bool c_weed = cs_subset_bool(sub, "weed");
435 if (!display || c_weed)
436 v = unfold_header(v);
437
438 /* when not displaying, use sane wrap value */
439 if (!display)
440 {
441 const short c_wrap_headers = cs_subset_number(sub, "wrap_headers");
442 if ((c_wrap_headers < 78) || (c_wrap_headers > 998))
443 wraplen = 78;
444 else
445 wraplen = c_wrap_headers;
446 }
447 else if (wraplen <= 0)
448 {
449 wraplen = 78;
450 }
451
452 const size_t vlen = mutt_str_len(v);
453 if (tag)
454 {
455 /* if header is short enough, simply print it */
456 if (!display && (mutt_strwidth(tag) + 2 + pfxw + mutt_strnwidth(v, vlen) <= wraplen))
457 {
458 mutt_debug(LL_DEBUG5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
459 if (fprintf(fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
460 goto out;
461 rc = 0;
462 goto out;
463 }
464 else
465 {
466 rc = fold_one_header(fp, tag, v, vlen, pfx, wraplen, chflags);
467 goto out;
468 }
469 }
470
471 char *p = v;
472 last = v;
473 line = v;
474 while (p && *p)
475 {
476 p = strchr(p, '\n');
477
478 /* find maximum line width in current header */
479 if (p)
480 *p = '\0';
481 w = mutt_mb_width(line, 0, display);
482 if (w > max)
483 max = w;
484 if (p)
485 *p = '\n';
486
487 if (!p)
488 break;
489
490 line = ++p;
491 if ((*p != ' ') && (*p != '\t'))
492 {
493 if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
494 goto out;
495 last = p;
496 max = 0;
497 }
498 }
499
500 if (last && *last)
501 if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
502 goto out;
503
504 rc = 0;
505
506out:
507 FREE(&v);
508 return rc;
509}
510
520void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
521{
522 struct ListNode *np = NULL;
523 size_t length = 0;
524
525 STAILQ_FOREACH(np, r, entries)
526 {
527 if (++length == trim)
528 break;
529 }
530
531 struct ListNode **ref = MUTT_MEM_CALLOC(length, struct ListNode *);
532
533 // store in reverse order
534 size_t tmp = length;
535 STAILQ_FOREACH(np, r, entries)
536 {
537 ref[--tmp] = np;
538 if (tmp == 0)
539 break;
540 }
541
542 for (size_t i = 0; i < length; i++)
543 {
544 fputc(' ', fp);
545 fputs(ref[i]->data, fp);
546 if (i != length - 1)
547 fputc('\n', fp);
548 }
549
550 FREE(&ref);
551}
552
578int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *b,
579 enum MuttWriteHeaderMode mode, bool privacy,
580 bool hide_protected_subject, struct ConfigSubset *sub)
581{
582 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
583 (mode == MUTT_WRITE_HEADER_POSTPONE)) &&
584 !privacy)
585 {
586 struct Buffer *date = buf_pool_get();
587 mutt_date_make_date(date, cs_subset_bool(sub, "local_date_header"));
588 fprintf(fp, "Date: %s\n", buf_string(date));
589 buf_pool_release(&date);
590 }
591
592 /* UseFrom is not consulted here so that we can still write a From:
593 * field if the user sets it with the 'my-header' command */
594 if (!TAILQ_EMPTY(&env->from) && !privacy)
595 {
596 mutt_addrlist_write_file(&env->from, fp, "From");
597 }
598
599 if (!TAILQ_EMPTY(&env->sender) && !privacy)
600 {
601 mutt_addrlist_write_file(&env->sender, fp, "Sender");
602 }
603
604 if (!TAILQ_EMPTY(&env->to))
605 {
606 mutt_addrlist_write_file(&env->to, fp, "To");
607 }
608 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
609 {
610 if (!OptNewsSend)
611 fputs("To:\n", fp);
612 }
613
614 if (!TAILQ_EMPTY(&env->cc))
615 {
616 mutt_addrlist_write_file(&env->cc, fp, "Cc");
617 }
618 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
619 {
620 if (!OptNewsSend)
621 fputs("Cc:\n", fp);
622 }
623
624 if (!TAILQ_EMPTY(&env->bcc))
625 {
626 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
627
628 if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
629 (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
630 ((mode == MUTT_WRITE_HEADER_NORMAL) && c_write_bcc))
631 {
632 mutt_addrlist_write_file(&env->bcc, fp, "Bcc");
633 }
634 }
635 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
636 {
637 if (!OptNewsSend)
638 fputs("Bcc:\n", fp);
639 }
640
641 if (env->newsgroups)
642 fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
643 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
644 fputs("Newsgroups:\n", fp);
645
646 if (env->followup_to)
647 fprintf(fp, "Followup-To: %s\n", env->followup_to);
648 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
649 fputs("Followup-To:\n", fp);
650
651 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
652 if (env->x_comment_to)
653 fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
654 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && c_x_comment_to)
655 fputs("X-Comment-To:\n", fp);
656
657 if (env->subject)
658 {
659 if (hide_protected_subject &&
660 ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
662 {
663 const char *const c_crypt_protected_headers_subject = cs_subset_string(sub, "crypt_protected_headers_subject");
664 mutt_write_one_header(fp, "Subject", c_crypt_protected_headers_subject,
665 NULL, 0, CH_NO_FLAGS, sub);
666 }
667 else
668 {
669 mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NO_FLAGS, sub);
670 }
671 }
672 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
673 {
674 fputs("Subject:\n", fp);
675 }
676
677 /* save message id if the user has set it */
678 if (env->message_id && !privacy)
679 fprintf(fp, "Message-ID: %s\n", env->message_id);
680
681 if (!TAILQ_EMPTY(&env->reply_to))
682 {
683 mutt_addrlist_write_file(&env->reply_to, fp, "Reply-To");
684 }
685 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
686 {
687 fputs("Reply-To:\n", fp);
688 }
689
690 if (!TAILQ_EMPTY(&env->mail_followup_to))
691 {
692 if (!OptNewsSend)
693 {
694 mutt_addrlist_write_file(&env->mail_followup_to, fp, "Mail-Followup-To");
695 }
696 }
697
698 /* Add any user defined headers */
699 struct UserHdrsOverride userhdrs_overrides = write_userhdrs(fp, &env->userhdrs,
700 privacy, sub);
701
702 if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
704 {
705 if (!STAILQ_EMPTY(&env->references))
706 {
707 fputs("References:", fp);
708 mutt_write_references(&env->references, fp, 10);
709 fputc('\n', fp);
710 }
711
712 /* Add the MIME headers */
713 if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
714 {
715 fputs("MIME-Version: 1.0\n", fp);
716 mutt_write_mime_header(b, fp, sub);
717 }
718 }
719
720 if (!STAILQ_EMPTY(&env->in_reply_to))
721 {
722 fputs("In-Reply-To:", fp);
724 fputc('\n', fp);
725 }
726
727#ifdef USE_AUTOCRYPT
728 const bool c_autocrypt = cs_subset_bool(sub, "autocrypt");
729 if (c_autocrypt)
730 {
731 if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
733 if (mode == MUTT_WRITE_HEADER_MIME)
735 }
736#endif
737
738 const bool c_user_agent = cs_subset_bool(sub, "user_agent");
739 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
740 c_user_agent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
741 {
742 /* Add a vanity header */
743 fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
744 }
745
746 return (ferror(fp) == 0) ? 0 : -1;
747}
748
757int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
758{
759 if (!b || !fp)
760 return -1;
761
762 int len;
763 int tmplen;
764 char buf[256] = { 0 };
765
766 fprintf(fp, "Content-Type: %s/%s", BODY_TYPE(b), b->subtype);
767
768 if (!TAILQ_EMPTY(&b->parameter))
769 {
770 len = 25 + mutt_str_len(b->subtype); /* approximate len. of content-type */
771
772 struct Parameter *np = NULL;
773 TAILQ_FOREACH(np, &b->parameter, entries)
774 {
775 if (!np->attribute || !np->value)
776 continue;
777
778 struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
779 rfc2231_encode_string(&pl_conts, np->attribute, np->value);
780 struct Parameter *cont = NULL;
781 TAILQ_FOREACH(cont, &pl_conts, entries)
782 {
783 fputc(';', fp);
784
785 buf[0] = 0;
786 mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
787
788 /* Dirty hack to make messages readable by Outlook Express
789 * for the Mac: force quotes around the boundary parameter
790 * even when they aren't needed. */
791 if (mutt_istr_equal(cont->attribute, "boundary") && mutt_str_equal(buf, cont->value))
792 snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
793
794 tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
795 if ((len + tmplen + 2) > 76)
796 {
797 fputs("\n\t", fp);
798 len = tmplen + 1;
799 }
800 else
801 {
802 fputc(' ', fp);
803 len += tmplen + 1;
804 }
805
806 fprintf(fp, "%s=%s", cont->attribute, buf);
807 }
808
809 mutt_param_free(&pl_conts);
810 }
811 }
812
813 fputc('\n', fp);
814
815 if (b->content_id)
816 fprintf(fp, "Content-ID: <%s>\n", b->content_id);
817
818 if (b->language)
819 fprintf(fp, "Content-Language: %s\n", b->language);
820
821 if (b->description)
822 fprintf(fp, "Content-Description: %s\n", b->description);
823
824 if (b->disposition != DISP_NONE)
825 {
826 const char *dispstr[] = { "inline", "attachment", "form-data" };
827
828 if (b->disposition < sizeof(dispstr) / sizeof(char *))
829 {
830 fprintf(fp, "Content-Disposition: %s", dispstr[b->disposition]);
831 len = 21 + mutt_str_len(dispstr[b->disposition]);
832
833 if (b->use_disp && ((b->disposition != DISP_INLINE) || b->d_filename))
834 {
835 char *fn = b->d_filename;
836 if (!fn)
837 fn = b->filename;
838
839 if (fn)
840 {
841 /* Strip off the leading path... */
842 char *t = strrchr(fn, '/');
843 if (t)
844 t++;
845 else
846 t = fn;
847
848 struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
849 rfc2231_encode_string(&pl_conts, "filename", t);
850 struct Parameter *cont = NULL;
851 TAILQ_FOREACH(cont, &pl_conts, entries)
852 {
853 fputc(';', fp);
854 buf[0] = 0;
855 mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
856
857 tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
858 if ((len + tmplen + 2) > 76)
859 {
860 fputs("\n\t", fp);
861 len = tmplen + 1;
862 }
863 else
864 {
865 fputc(' ', fp);
866 len += tmplen + 1;
867 }
868
869 fprintf(fp, "%s=%s", cont->attribute, buf);
870 }
871
872 mutt_param_free(&pl_conts);
873 }
874 }
875
876 fputc('\n', fp);
877 }
878 else
879 {
880 mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n", b->disposition);
881 }
882 }
883
884 if (b->encoding != ENC_7BIT)
885 fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(b->encoding));
886
887 const bool c_crypt_protected_headers_write = cs_subset_bool(sub, "crypt_protected_headers_write");
888 bool c_autocrypt = false;
889#ifdef USE_AUTOCRYPT
890 c_autocrypt = cs_subset_bool(sub, "autocrypt");
891#endif
892
893 if ((c_crypt_protected_headers_write || c_autocrypt) && b->mime_headers)
894 {
896 false, false, sub);
897 }
898
899 /* Do NOT add the terminator here!!! */
900 return ferror(fp) ? -1 : 0;
901}
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
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
Wrapper for mutt_write_address()
Definition address.c:1252
Email Address Handling.
Autocrypt end-to-end encryption.
int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition autocrypt.c:801
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition autocrypt.c:763
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
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
Convenience wrapper for the config headers.
#define CH_DISPLAY
Display result to user.
Definition copy_email.h:74
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition copy_email.h:54
#define CH_NO_FLAGS
No flags are set.
Definition copy_email.h:55
size_t mutt_strnwidth(const char *s, size_t n)
Measure a string's width in screen cells.
Definition curs_lib.c:457
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition curs_lib.c:444
Structs that make up an email.
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
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition globals.c:54
Global variables.
const char * GitVer
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
Convenience wrapper for the gui headers.
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
int mutt_mb_width(const char *str, int col, bool indent)
Measure a string's display width (in screen columns)
Definition mbyte.c:138
#define countof(x)
Definition memory.h:49
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition mime.c:67
@ ENC_7BIT
7-bit text
Definition mime.h:49
#define BODY_TYPE(body)
Get the type name of a body part.
Definition mime.h:93
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ DISP_NONE
No preferred disposition.
Definition mime.h:65
#define ENCODING(x)
Get the encoding name for an encoding type.
Definition mime.h:97
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition date.c:398
Convenience wrapper for the library headers.
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
int mutt_istrn_cmp(const char *a, const char *b, size_t num)
Compare two strings ignoring case (to a maximum), safely.
Definition string.c:443
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:610
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition string.c:708
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition parameter.c:62
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#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
size_t rfc2231_encode_string(struct ParameterList *head, const char *attribute, char *value)
Encode a string to be suitable for an RFC2231 header.
Definition rfc2231.c:354
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *b, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition header.c:578
static int fold_one_header(FILE *fp, const char *tag, const char *value, size_t vlen, const char *pfx, int wraplen, CopyHeaderFlags chflags)
Fold one header line.
Definition header.c:132
static const char *const UserhdrsOverrideHeaders[]
The next array/enum pair is used to to keep track of user headers that override pre-defined headers N...
Definition header.c:52
static int userhdrs_override_cmp(const void *a, const void *b)
Compare a user-defined header with an element of the UserhdrsOverrideHeaders list.
Definition header.c:276
static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char *pfx, const char *start, const char *end, CopyHeaderFlags chflags)
Write out one header line.
Definition header.c:296
static char * unfold_header(char *s)
Unfold a wrapped email header.
Definition header.c:239
static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs, bool privacy, struct ConfigSubset *sub)
Write user-defined headers and keep track of the interesting ones.
Definition header.c:365
static int print_val(FILE *fp, const char *pfx, const char *value, CopyHeaderFlags chflags, size_t col)
Add pieces to an email header, wrapping where necessary.
Definition header.c:85
UserHdrsOverrideIdx
Headers that the user may override.
Definition header.c:61
@ USERHDRS_OVERRIDE_CONTENT_TYPE
Override the "Content-Type".
Definition header.c:62
@ USERHDRS_OVERRIDE_USER_AGENT
Override the "User-Agent".
Definition header.c:63
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition header.c:757
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition header.c:520
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:424
Convenience wrapper for the send headers.
MuttWriteHeaderMode
Modes for mutt_rfc822_write_header()
Definition header.h:37
@ MUTT_WRITE_HEADER_FCC
fcc mode, like normal mode but for Bcc header
Definition header.h:39
@ MUTT_WRITE_HEADER_MIME
Write protected headers.
Definition header.h:42
@ MUTT_WRITE_HEADER_NORMAL
A normal Email, write full header + MIME headers.
Definition header.h:38
@ MUTT_WRITE_HEADER_POSTPONE
A postponed Email, just the envelope info.
Definition header.h:40
@ MUTT_WRITE_HEADER_EDITHDRS
"light" mode (used for edit_hdrs)
Definition header.h:41
#define NONULL(x)
Definition string2.h:44
The body of an email.
Definition body.h:36
char * language
content-language (RFC8255)
Definition body.h:78
char * content_id
Content-Id (RFC2392)
Definition body.h:58
char * d_filename
filename to be used for the content-disposition header If NULL, filename is used instead.
Definition body.h:56
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
bool use_disp
Content-Disposition uses filename= ?
Definition body.h:47
char * description
content-description
Definition body.h:55
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
char * subtype
content-type subtype
Definition body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
A set of inherited config items.
Definition subset.h:46
The header of an Email.
Definition envelope.h:57
struct ListHead userhdrs
user defined headers
Definition envelope.h:85
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
char * newsgroups
List of newsgroups.
Definition envelope.h:78
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList sender
Email's sender.
Definition envelope.h:63
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
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Attribute associated with a MIME part.
Definition parameter.h:33
char * attribute
Parameter name.
Definition parameter.h:34
char * value
Parameter value.
Definition parameter.h:35
Which headers have been overridden.
Definition header.c:70
bool is_overridden[countof(UserhdrsOverrideHeaders)]
Which email headers have been overridden.
Definition header.c:72