NeoMutt  2021-10-22-8-g9cb437
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 
50 static const char *const userhdrs_override_headers[] = {
51  "content-type:",
52  "user-agent:",
53 };
54 
59 {
62 };
63 
68 {
71 };
72 
83 static 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 
130 static 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, ((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 
235 static 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 
272 static 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 
292 static 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), 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 
361 static 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 curr_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  curr_override = idx - userhdrs_override_headers;
389  overrides.is_overridden[curr_override] = true;
390  }
391 
392  if (privacy && (curr_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 
420 int 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 
500 out:
501  FREE(&v);
502  return rc;
503 }
504 
514 void 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 
572 int 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];
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) ||
666  (mode == MUTT_WRITE_HEADER_POSTPONE)))
667  {
668  const char *const c_crypt_protected_headers_subject =
669  cs_subset_string(sub, "crypt_protected_headers_subject");
670  mutt_write_one_header(fp, "Subject", c_crypt_protected_headers_subject,
671  NULL, 0, CH_NO_FLAGS, sub);
672  }
673  else
674  mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NO_FLAGS, sub);
675  }
676  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
677  fputs("Subject:\n", fp);
678 
679  /* save message id if the user has set it */
680  if (env->message_id && !privacy)
681  fprintf(fp, "Message-ID: %s\n", env->message_id);
682 
683  if (!TAILQ_EMPTY(&env->reply_to))
684  {
685  fputs("Reply-To: ", fp);
686  mutt_addrlist_write_file(&env->reply_to, fp, 10, false);
687  }
688  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
689  fputs("Reply-To:\n", fp);
690 
691  if (!TAILQ_EMPTY(&env->mail_followup_to))
692  {
693 #ifdef USE_NNTP
694  if (!OptNewsSend)
695 #endif
696  {
697  fputs("Mail-Followup-To: ", fp);
698  mutt_addrlist_write_file(&env->mail_followup_to, fp, 18, false);
699  }
700  }
701 
702  /* Add any user defined headers */
703  struct UserHdrsOverride userhdrs_overrides =
704  write_userhdrs(fp, &env->userhdrs, privacy, sub);
705 
706  if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
707  (mode == MUTT_WRITE_HEADER_POSTPONE) || (mode == MUTT_WRITE_HEADER_MIME))
708  {
709  if (!STAILQ_EMPTY(&env->references))
710  {
711  fputs("References:", fp);
712  mutt_write_references(&env->references, fp, 10);
713  fputc('\n', fp);
714  }
715 
716  /* Add the MIME headers */
717  if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
718  {
719  fputs("MIME-Version: 1.0\n", fp);
720  mutt_write_mime_header(attach, fp, sub);
721  }
722  }
723 
724  if (!STAILQ_EMPTY(&env->in_reply_to))
725  {
726  fputs("In-Reply-To:", fp);
727  mutt_write_references(&env->in_reply_to, fp, 0);
728  fputc('\n', fp);
729  }
730 
731 #ifdef USE_AUTOCRYPT
732  const bool c_autocrypt = cs_subset_bool(sub, "autocrypt");
733  if (c_autocrypt)
734  {
735  if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
737  if (mode == MUTT_WRITE_HEADER_MIME)
739  }
740 #endif
741 
742  const bool c_user_agent = cs_subset_bool(sub, "user_agent");
743  if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
744  c_user_agent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
745  {
746  /* Add a vanity header */
747  fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
748  }
749 
750  return (ferror(fp) == 0) ? 0 : -1;
751 }
752 
761 int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
762 {
763  if (!a || !fp)
764  return -1;
765 
766  int len;
767  int tmplen;
768  char buf[256] = { 0 };
769 
770  fprintf(fp, "Content-Type: %s/%s", TYPE(a), a->subtype);
771 
772  if (!TAILQ_EMPTY(&a->parameter))
773  {
774  len = 25 + mutt_str_len(a->subtype); /* approximate len. of content-type */
775 
776  struct Parameter *np = NULL;
777  TAILQ_FOREACH(np, &a->parameter, entries)
778  {
779  if (!np->attribute || !np->value)
780  continue;
781 
782  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
783  rfc2231_encode_string(&pl_conts, np->attribute, np->value);
784  struct Parameter *cont = NULL;
785  TAILQ_FOREACH(cont, &pl_conts, entries)
786  {
787  fputc(';', fp);
788 
789  buf[0] = 0;
790  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
791 
792  /* Dirty hack to make messages readable by Outlook Express
793  * for the Mac: force quotes around the boundary parameter
794  * even when they aren't needed. */
795  if (mutt_istr_equal(cont->attribute, "boundary") && mutt_str_equal(buf, cont->value))
796  snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
797 
798  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
799  if (len + tmplen + 2 > 76)
800  {
801  fputs("\n\t", fp);
802  len = tmplen + 1;
803  }
804  else
805  {
806  fputc(' ', fp);
807  len += tmplen + 1;
808  }
809 
810  fprintf(fp, "%s=%s", cont->attribute, buf);
811  }
812 
813  mutt_param_free(&pl_conts);
814  }
815  }
816 
817  fputc('\n', fp);
818 
819  if (a->language)
820  fprintf(fp, "Content-Language: %s\n", a->language);
821 
822  if (a->description)
823  fprintf(fp, "Content-Description: %s\n", a->description);
824 
825  if (a->disposition != DISP_NONE)
826  {
827  const char *dispstr[] = { "inline", "attachment", "form-data" };
828 
829  if (a->disposition < sizeof(dispstr) / sizeof(char *))
830  {
831  fprintf(fp, "Content-Disposition: %s", dispstr[a->disposition]);
832  len = 21 + mutt_str_len(dispstr[a->disposition]);
833 
834  if (a->use_disp && (a->disposition != DISP_INLINE))
835  {
836  char *fn = a->d_filename;
837  if (!fn)
838  fn = a->filename;
839 
840  if (fn)
841  {
842  /* Strip off the leading path... */
843  char *t = strrchr(fn, '/');
844  if (t)
845  t++;
846  else
847  t = fn;
848 
849  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
850  rfc2231_encode_string(&pl_conts, "filename", t);
851  struct Parameter *cont = NULL;
852  TAILQ_FOREACH(cont, &pl_conts, entries)
853  {
854  fputc(';', fp);
855  buf[0] = 0;
856  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
857 
858  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
859  if (len + tmplen + 2 > 76)
860  {
861  fputs("\n\t", fp);
862  len = tmplen + 1;
863  }
864  else
865  {
866  fputc(' ', fp);
867  len += tmplen + 1;
868  }
869 
870  fprintf(fp, "%s=%s", cont->attribute, buf);
871  }
872 
873  mutt_param_free(&pl_conts);
874  }
875  }
876 
877  fputc('\n', fp);
878  }
879  else
880  {
881  mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n", a->disposition);
882  }
883  }
884 
885  if (a->encoding != ENC_7BIT)
886  fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding));
887 
888  const bool c_crypt_protected_headers_write =
889  cs_subset_bool(sub, "crypt_protected_headers_write");
890  bool autocrypt = false;
891 #ifdef USE_AUTOCRYPT
892  autocrypt = cs_subset_bool(sub, "autocrypt");
893 #endif
894 
895  if ((c_crypt_protected_headers_write || autocrypt) && a->mime_headers)
896  {
898  false, false, sub);
899  }
900 
901  /* Do NOT add the terminator here!!! */
902  return ferror(fp) ? -1 : 0;
903 }
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:809
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition: autocrypt.c:771
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
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
int mutt_strnwidth(const char *s, size_t n)
Measure a string's width in screen cells.
Definition: curs_lib.c:1001
int mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:988
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:378
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
static char * unfold_header(char *s)
Unfold a wrapped email header.
Definition: header.c:235
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:761
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
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
@ 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:138
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:40
#define MIN(a, b)
Definition: memory.h:31
#define mutt_array_size(x)
Definition: memory.h:33
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.
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:607
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:1004
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:548
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
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:346
#define NONULL(x)
Definition: string2.h:37
The body of an email.
Definition: body.h:35
char * language
content-language (RFC8255)
Definition: body.h:76
char * d_filename
filename to be used for the content-disposition header If NULL, filename is used instead.
Definition: body.h:55
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:74
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:61
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:46
char * description
content-description
Definition: body.h:54
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:41
char * subtype
content-type subtype
Definition: body.h:59
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:57
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:55
struct ListHead userhdrs
user defined headers
Definition: envelope.h:85
struct AddressList to
Email's 'To' list.
Definition: envelope.h:58
char * followup_to
List of 'followup-to' fields.
Definition: envelope.h:79
struct AddressList reply_to
Email's 'reply-to'.
Definition: envelope.h:62
char * message_id
Message ID.
Definition: envelope.h:71
char * x_comment_to
List of 'X-comment-to' fields.
Definition: envelope.h:80
char * newsgroups
List of newsgroups.
Definition: envelope.h:77
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition: envelope.h:63
struct AddressList cc
Email's 'Cc' list.
Definition: envelope.h:59
struct AddressList sender
Email's sender.
Definition: envelope.h:61
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 * subject
Email's subject.
Definition: envelope.h:68
struct AddressList bcc
Email's 'Bcc' list.
Definition: envelope.h:60
struct AddressList from
Email's 'From' list.
Definition: envelope.h:57
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