NeoMutt  2020-08-07-1-gab41a1
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 {
70 };
71 
82 static 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 
129 static 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, ((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  return -1;
195  col += w;
196 
197  /* if the current word ends in \n, ignore all its trailing spaces
198  * and reset column; this prevents us from putting only spaces (or
199  * even none) on a line if the trailing spaces are located at our
200  * current line width
201  * XXX this covers ASCII space only, for display we probably
202  * want something like iswspace() here */
203  const char *sp = next;
204  while ((sp[0] != '\0') && ((sp[0] == ' ') || (sp[0] == '\t')))
205  sp++;
206  if (sp[0] == '\n')
207  {
208  if (sp[1] == '\0')
209  break;
210  next = sp;
211  col = 0;
212  }
213 
214  p = next;
215  first = 0;
216  }
217 
218  /* if we have printed something but didn't \n-terminate it, do it
219  * except the last word we printed ended in \n already */
220  if (col && ((l == 0) || (buf[l - 1] != '\n')))
221  if (putc('\n', fp) == EOF)
222  return -1;
223 
224  return 0;
225 }
226 
234 static char *unfold_header(char *s)
235 {
236  char *p = s;
237  char *q = s;
238 
239  while (p && (p[0] != '\0'))
240  {
241  /* remove CRLF prior to FWSP, turn \t into ' ' */
242  if ((p[0] == '\r') && (p[1] == '\n') && ((p[2] == ' ') || (p[2] == '\t')))
243  {
244  *q++ = ' ';
245  p += 3;
246  continue;
247  }
248  /* remove LF prior to FWSP, turn \t into ' ' */
249  else if ((p[0] == '\n') && ((p[1] == ' ') || (p[1] == '\t')))
250  {
251  *q++ = ' ';
252  p += 2;
253  continue;
254  }
255  *q++ = *p++;
256  }
257  if (q)
258  q[0] = '\0';
259 
260  return s;
261 }
262 
271 static int userhdrs_override_cmp(const void *a, const void *b)
272 {
273  const char *ca = a;
274  const char *cb = *(const char **) b;
275  return mutt_istrn_cmp(ca, cb, strlen(cb));
276 }
277 
291 static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char *pfx,
292  const char *start, const char *end, CopyHeaderFlags chflags)
293 {
294  const char *t = strchr(start, ':');
295  if (!t || (t > end))
296  {
297  mutt_debug(LL_DEBUG1, "#2 warning: header not in 'key: value' format!\n");
298  return 0;
299  }
300 
301  const size_t vallen = end - start;
302  const bool short_enough = (pfxw + max <= wraplen);
303 
304  mutt_debug((short_enough ? LL_DEBUG2 : LL_DEBUG5), "buf[%s%.*s] %s, max width = %d %s %d\n",
305  NONULL(pfx), vallen - 1 /* skip newline */, start,
306  (short_enough ? "short enough" : "too long"), max,
307  (short_enough ? "<=" : ">"), wraplen);
308 
309  int rc = 0;
310  const char *valbuf = NULL, *tagbuf = NULL;
311  const bool is_from = (vallen > 5) && mutt_istr_startswith(start, "from ");
312 
313  /* only pass through folding machinery if necessary for sending,
314  * never wrap From_ headers on sending */
315  if (!(chflags & CH_DISPLAY) && (short_enough || is_from))
316  {
317  if (pfx && *pfx)
318  {
319  if (fputs(pfx, fp) == EOF)
320  {
321  return -1;
322  }
323  }
324 
325  valbuf = mutt_strn_dup(start, end - start);
326  rc = print_val(fp, pfx, valbuf, chflags, mutt_str_len(pfx));
327  }
328  else
329  {
330  if (!is_from)
331  {
332  tagbuf = mutt_strn_dup(start, t - start);
333  /* skip over the colon separating the header field name and value */
334  t++;
335 
336  /* skip over any leading whitespace (WSP, as defined in RFC5322)
337  * NOTE: mutt_str_skip_email_wsp() does the wrong thing here.
338  * See tickets 3609 and 3716. */
339  while ((*t == ' ') || (*t == '\t'))
340  t++;
341  }
342  const char *s = is_from ? start : t;
343  valbuf = mutt_strn_dup(s, end - s);
344  rc = fold_one_header(fp, tagbuf, valbuf, end - s, pfx, wraplen, chflags);
345  }
346 
347  FREE(&tagbuf);
348  FREE(&valbuf);
349  return rc;
350 }
351 
360 static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs,
361  bool privacy, struct ConfigSubset *sub)
362 {
363  struct UserHdrsOverride overrides = { { 0 } };
364 
365  struct ListNode *tmp = NULL;
366  STAILQ_FOREACH(tmp, userhdrs, entries)
367  {
368  char *const colon = strchr(tmp->data, ':');
369  if (!colon)
370  {
371  continue;
372  }
373 
374  const char *const value = mutt_str_skip_email_wsp(colon + 1);
375  if (*value == '\0')
376  {
377  continue; /* don't emit empty fields. */
378  }
379 
380  /* check whether the current user-header is an override */
381  size_t curr_override = (size_t) -1;
382  const char *const *idx = bsearch(tmp->data, userhdrs_override_headers,
384  sizeof(char *), userhdrs_override_cmp);
385  if (idx != NULL)
386  {
387  curr_override = idx - userhdrs_override_headers;
388  overrides.is_overridden[curr_override] = true;
389  }
390 
391  if (privacy && (curr_override == USERHDRS_OVERRIDE_USER_AGENT))
392  {
393  continue;
394  }
395 
396  *colon = '\0';
397  mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS, sub);
398  *colon = ':';
399  }
400 
401  return overrides;
402 }
403 
419 int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
420  const char *pfx, int wraplen, CopyHeaderFlags chflags,
421  struct ConfigSubset *sub)
422 {
423  char *last = NULL, *line = NULL;
424  int max = 0, w, rc = -1;
425  int pfxw = mutt_strwidth(pfx);
426  char *v = mutt_str_dup(value);
427  bool display = (chflags & CH_DISPLAY);
428 
429  const bool c_weed = cs_subset_bool(sub, "weed");
430  if (!display || c_weed)
431  v = unfold_header(v);
432 
433  /* when not displaying, use sane wrap value */
434  if (!display)
435  {
436  const short c_wrap_headers = cs_subset_number(sub, "wrap_headers");
437  if ((c_wrap_headers < 78) || (c_wrap_headers > 998))
438  wraplen = 78;
439  else
440  wraplen = c_wrap_headers;
441  }
442  else if (wraplen <= 0)
443  wraplen = 78;
444 
445  const size_t vlen = mutt_str_len(v);
446  if (tag)
447  {
448  /* if header is short enough, simply print it */
449  if (!display && (mutt_strwidth(tag) + 2 + pfxw + mutt_strnwidth(v, vlen) <= wraplen))
450  {
451  mutt_debug(LL_DEBUG5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
452  if (fprintf(fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
453  goto out;
454  rc = 0;
455  goto out;
456  }
457  else
458  {
459  rc = fold_one_header(fp, tag, v, vlen, pfx, wraplen, chflags);
460  goto out;
461  }
462  }
463 
464  char *p = v;
465  last = v;
466  line = v;
467  while (p && *p)
468  {
469  p = strchr(p, '\n');
470 
471  /* find maximum line width in current header */
472  if (p)
473  *p = '\0';
474  w = mutt_mb_width(line, 0, display);
475  if (w > max)
476  max = w;
477  if (p)
478  *p = '\n';
479 
480  if (!p)
481  break;
482 
483  line = ++p;
484  if ((*p != ' ') && (*p != '\t'))
485  {
486  if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
487  goto out;
488  last = p;
489  max = 0;
490  }
491  }
492 
493  if (last && *last)
494  if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
495  goto out;
496 
497  rc = 0;
498 
499 out:
500  FREE(&v);
501  return rc;
502 }
503 
514 void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim,
515  struct ConfigSubset *sub)
516 {
517  struct ListNode *np = NULL;
518  size_t length = 0;
519 
520  STAILQ_FOREACH(np, r, entries)
521  {
522  if (++length == trim)
523  break;
524  }
525 
526  struct ListNode **ref = mutt_mem_calloc(length, sizeof(struct ListNode *));
527 
528  // store in reverse order
529  size_t tmp = length;
530  STAILQ_FOREACH(np, r, entries)
531  {
532  ref[--tmp] = np;
533  if (tmp == 0)
534  break;
535  }
536 
537  for (size_t i = 0; i < length; i++)
538  {
539  fputc(' ', fp);
540  fputs(ref[i]->data, fp);
541  if (i != length - 1)
542  fputc('\n', fp);
543  }
544 
545  FREE(&ref);
546 }
547 
573 int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *attach,
574  enum MuttWriteHeaderMode mode, bool privacy,
575  bool hide_protected_subject, struct ConfigSubset *sub)
576 {
577  char buf[1024];
578 
579  if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy)
580  {
581  struct Buffer *date = mutt_buffer_pool_get();
582  mutt_date_make_date(date);
583  fprintf(fp, "Date: %s\n", mutt_b2s(date));
585  }
586 
587  /* UseFrom is not consulted here so that we can still write a From:
588  * field if the user sets it with the 'my_hdr' command */
589  if (!TAILQ_EMPTY(&env->from) && !privacy)
590  {
591  buf[0] = '\0';
592  mutt_addrlist_write(&env->from, buf, sizeof(buf), false);
593  fprintf(fp, "From: %s\n", buf);
594  }
595 
596  if (!TAILQ_EMPTY(&env->sender) && !privacy)
597  {
598  buf[0] = '\0';
599  mutt_addrlist_write(&env->sender, buf, sizeof(buf), false);
600  fprintf(fp, "Sender: %s\n", buf);
601  }
602 
603  if (!TAILQ_EMPTY(&env->to))
604  {
605  fputs("To: ", fp);
606  mutt_addrlist_write_file(&env->to, fp, 4, false);
607  }
608  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
609 #ifdef USE_NNTP
610  if (!OptNewsSend)
611 #endif
612  fputs("To:\n", fp);
613 
614  if (!TAILQ_EMPTY(&env->cc))
615  {
616  fputs("Cc: ", fp);
617  mutt_addrlist_write_file(&env->cc, fp, 4, false);
618  }
619  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
620 #ifdef USE_NNTP
621  if (!OptNewsSend)
622 #endif
623  fputs("Cc:\n", fp);
624 
625  if (!TAILQ_EMPTY(&env->bcc))
626  {
627  const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
628 
629  if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
630  (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
631  ((mode == MUTT_WRITE_HEADER_NORMAL) && c_write_bcc))
632  {
633  fputs("Bcc: ", fp);
634  mutt_addrlist_write_file(&env->bcc, fp, 5, false);
635  }
636  }
637  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
638 #ifdef USE_NNTP
639  if (!OptNewsSend)
640 #endif
641  fputs("Bcc:\n", fp);
642 
643 #ifdef USE_NNTP
644  if (env->newsgroups)
645  fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
646  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
647  fputs("Newsgroups:\n", fp);
648 
649  if (env->followup_to)
650  fprintf(fp, "Followup-To: %s\n", env->followup_to);
651  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
652  fputs("Followup-To:\n", fp);
653 
654  const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
655  if (env->x_comment_to)
656  fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
657  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && c_x_comment_to)
658  fputs("X-Comment-To:\n", fp);
659 #endif
660 
661  if (env->subject)
662  {
663  if (hide_protected_subject &&
664  ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
665  (mode == MUTT_WRITE_HEADER_POSTPONE)))
666  {
667  const char *c_crypt_protected_headers_subject =
668  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 =
703  write_userhdrs(fp, &env->userhdrs, privacy, sub);
704 
705  if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
706  (mode == MUTT_WRITE_HEADER_POSTPONE) || (mode == MUTT_WRITE_HEADER_MIME))
707  {
708  if (!STAILQ_EMPTY(&env->references))
709  {
710  fputs("References:", fp);
711  mutt_write_references(&env->references, fp, 10, sub);
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);
726  mutt_write_references(&env->in_reply_to, fp, 0, sub);
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 
760 int 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  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  fputc(';', fp);
787 
788  buf[0] = 0;
789  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
790 
791  /* Dirty hack to make messages readable by Outlook Express
792  * for the Mac: force quotes around the boundary parameter
793  * even when they aren't needed. */
794  if (mutt_istr_equal(cont->attribute, "boundary") && mutt_str_equal(buf, cont->value))
795  snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
796 
797  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
798  if (len + tmplen + 2 > 76)
799  {
800  fputs("\n\t", fp);
801  len = tmplen + 1;
802  }
803  else
804  {
805  fputc(' ', fp);
806  len += tmplen + 1;
807  }
808 
809  fprintf(fp, "%s=%s", cont->attribute, buf);
810  }
811 
812  mutt_param_free(&pl_conts);
813  }
814  }
815 
816  fputc('\n', fp);
817 
818  if (a->language)
819  fprintf(fp, "Content-Language: %s\n", a->language);
820 
821  if (a->description)
822  fprintf(fp, "Content-Description: %s\n", a->description);
823 
824  if (a->disposition != DISP_NONE)
825  {
826  const char *dispstr[] = { "inline", "attachment", "form-data" };
827 
828  if (a->disposition < sizeof(dispstr) / sizeof(char *))
829  {
830  fprintf(fp, "Content-Disposition: %s", dispstr[a->disposition]);
831  len = 21 + mutt_str_len(dispstr[a->disposition]);
832 
833  if (a->use_disp && (a->disposition != DISP_INLINE))
834  {
835  char *fn = a->d_filename;
836  if (!fn)
837  fn = a->filename;
838 
839  if (fn)
840  {
841  /* Strip off the leading path... */
842  char *t = strrchr(fn, '/');
843  if (t)
844  t++;
845  else
846  t = fn;
847 
848  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
849  rfc2231_encode_string(&pl_conts, "filename", t);
850  struct Parameter *cont = NULL;
851  TAILQ_FOREACH(cont, &pl_conts, entries)
852  {
853  fputc(';', fp);
854  buf[0] = 0;
855  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
856 
857  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
858  if (len + tmplen + 2 > 76)
859  {
860  fputs("\n\t", fp);
861  len = tmplen + 1;
862  }
863  else
864  {
865  fputc(' ', fp);
866  len += tmplen + 1;
867  }
868 
869  fprintf(fp, "%s=%s", cont->attribute, buf);
870  }
871 
872  mutt_param_free(&pl_conts);
873  }
874  }
875 
876  fputc('\n', fp);
877  }
878  else
879  {
880  mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n", a->disposition);
881  }
882  }
883 
884  if (a->encoding != ENC_7BIT)
885  fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding));
886 
887  const bool c_crypt_protected_headers_write =
888  cs_subset_bool(sub, "crypt_protected_headers_write");
889  bool autocrypt = false;
890 #ifdef USE_AUTOCRYPT
891  autocrypt = cs_subset_bool(sub, "autocrypt");
892 #endif
893 
894  if ((c_crypt_protected_headers_write || autocrypt) && a->mime_headers)
895  {
897  false, false, sub);
898  }
899 
900  /* Do NOT add the terminator here!!! */
901  return ferror(fp) ? -1 : 0;
902 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:876
char * attribute
Parameter name.
Definition: parameter.h:34
Convenience wrapper for the gui headers.
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:291
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:63
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:68
static char * unfold_header(char *s)
Unfold a wrapped email header.
Definition: header.c:234
void mutt_date_make_date(struct Buffer *buf)
Write a date in RFC822 format to a buffer.
Definition: date.c:377
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
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:271
#define MIN(a, b)
Definition: memory.h:31
A postponed Email, just the envelope info.
Definition: header.h:42
struct AddressList mail_followup_to
Email&#39;s &#39;mail-followup-to&#39;.
Definition: envelope.h:63
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
Which headers have been overridden.
Definition: header.c:67
Convenience wrapper for the send headers.
Structs that make up an email.
struct AddressList reply_to
Email&#39;s &#39;reply-to&#39;.
Definition: envelope.h:62
Autocrypt end-to-end encryption.
7-bit text
Definition: mime.h:49
struct AddressList bcc
Email&#39;s &#39;Bcc&#39; list.
Definition: envelope.h:60
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
struct ListHead userhdrs
user defined headers
Definition: envelope.h:83
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim, struct ConfigSubset *sub)
Add the message references to a list.
Definition: header.c:514
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
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:110
int mutt_mb_width(const char *str, int col, bool display)
Measure a string&#39;s display width (in screen columns)
Definition: mbyte.c:139
The body of an email.
Definition: body.h:34
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
Convenience wrapper for the config headers.
UserHdrsOverrideIdx
Headers that the user may override.
Definition: header.c:58
Email Address Handling.
#define mutt_array_size(x)
Definition: memory.h:33
fcc mode, like normal mode but for Bcc header
Definition: header.h:41
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
Log at debug level 2.
Definition: logging.h:41
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
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
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:553
#define ENCODING(x)
Definition: mime.h:91
struct AddressList cc
Email&#39;s &#39;Cc&#39; list.
Definition: envelope.h:59
A normal Email, write full header + MIME headers.
Definition: header.h:40
int mutt_istrn_cmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:612
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:760
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
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
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:360
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
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
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:888
char * subtype
content-type subtype
Definition: body.h:37
#define mutt_b2s(buf)
Definition: buffer.h:41
char * x_comment_to
List of &#39;X-comment-to&#39; fields.
Definition: envelope.h:78
A set of inherited config items.
Definition: subset.h:46
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:165
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1359
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
MuttWriteHeaderMode
Modes for mutt_rfc822_write_header()
Definition: header.h:38
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
Write protected headers.
Definition: header.h:44
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:219
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:748
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:177
Override the "User-Agent".
Definition: header.c:61
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:329
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
char * description
content-description
Definition: body.h:40
int mutt_strnwidth(const char *s, size_t n)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1372
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Definition: header.c:69
#define TYPE(body)
Definition: mime.h:89
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
char * data
String.
Definition: list.h:36
char * subject
Email&#39;s subject.
Definition: envelope.h:66
char * value
Parameter value.
Definition: parameter.h:35
Log at debug level 1.
Definition: logging.h:40
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
No preferred disposition.
Definition: mime.h:65
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:68
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:49
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
Attribute associated with a MIME part.
Definition: parameter.h:32
#define FREE(x)
Definition: memory.h:40
char * language
content-language (RFC8255)
Definition: body.h:38
#define STAILQ_EMPTY(head)
Definition: queue.h:345
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct AddressList sender
Email&#39;s sender.
Definition: envelope.h:61
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a &#39;From&#39; header line?
Definition: from.c:48
char * d_filename
filename to be used for the content-disposition header.
Definition: body.h:47
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define TAILQ_EMPTY(head)
Definition: queue.h:714
Override the "Content-Type".
Definition: header.c:60
int const char int line
Definition: acutest.h:617
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:46
Log at debug level 5.
Definition: logging.h:44
const char * GitVer
Convenience wrapper for the library headers.
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
A List node for strings.
Definition: list.h:34
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:630
Content is inline.
Definition: mime.h:62
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition: autocrypt.c:787
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_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
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:976
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:573
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:419
The header of an Email.
Definition: envelope.h:54
"light" mode (used for edit_hdrs)
Definition: header.h:43
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition: autocrypt.c:750