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