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