NeoMutt  2020-08-07-1-gab41a1
Teaching an old dog new tricks
DOXYGEN
header.c File Reference

Write a MIME Email Header to a file. More...

#include "config.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "gui/lib.h"
#include "header.h"
#include "mutt_globals.h"
#include "options.h"
#include "autocrypt/lib.h"
+ Include dependency graph for header.c:

Go to the source code of this file.

Data Structures

struct  UserHdrsOverride
 Which headers have been overridden. More...
 

Enumerations

enum  UserHdrsOverrideIdx { USERHDRS_OVERRIDE_CONTENT_TYPE, USERHDRS_OVERRIDE_USER_AGENT }
 Headers that the user may override. More...
 

Functions

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. More...
 
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. More...
 
static char * unfold_header (char *s)
 Unfold a wrapped email header. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
void mutt_write_references (const struct ListHead *r, FILE *fp, size_t trim, struct ConfigSubset *sub)
 Add the message references to a list. More...
 
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. More...
 
int mutt_write_mime_header (struct Body *a, FILE *fp, struct ConfigSubset *sub)
 Create a MIME header. More...
 

Variables

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 NeoMutt would emit. More...
 

Detailed Description

Write a MIME Email Header to a file.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file header.c.

Enumeration Type Documentation

◆ UserHdrsOverrideIdx

Headers that the user may override.

Enumerator
USERHDRS_OVERRIDE_CONTENT_TYPE 

Override the "Content-Type".

USERHDRS_OVERRIDE_USER_AGENT 

Override the "User-Agent".

Definition at line 58 of file header.c.

59 {
62 };
Override the "User-Agent".
Definition: header.c:61
Override the "Content-Type".
Definition: header.c:60

Function Documentation

◆ print_val()

static int print_val ( FILE *  fp,
const char *  pfx,
const char *  value,
CopyHeaderFlags  chflags,
size_t  col 
)
static

Add pieces to an email header, wrapping where necessary.

Parameters
fpFile to write to
pfxPrefix for headers
valueText to be added
chflagsFlags, see CopyHeaderFlags
colColumn that this text starts at
Return values
0Success
-1Failure

Definition at line 82 of file header.c.

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 }
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
+ Here is the caller graph for this function:

◆ fold_one_header()

static int fold_one_header ( FILE *  fp,
const char *  tag,
const char *  value,
size_t  vlen,
const char *  pfx,
int  wraplen,
CopyHeaderFlags  chflags 
)
static

Fold one header line.

Parameters
fpFile to write to
tagHeader key, e.g. "From"
valueHeader value
vlenLength of the header value string
pfxPrefix for header
wraplenColumn to wrap at
chflagsFlags, see CopyHeaderFlags
Return values
0Success
-1Failure

Definition at line 129 of file header.c.

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 }
#define NONULL(x)
Definition: string2.h:37
#define MIN(a, b)
Definition: memory.h:31
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
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
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:165
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 5.
Definition: logging.h:44
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:976
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ unfold_header()

static char* unfold_header ( char *  s)
static

Unfold a wrapped email header.

Parameters
sString to process
Return values
ptrUnfolded string
Note
The string is altered in-place

Definition at line 234 of file header.c.

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 }
+ Here is the caller graph for this function:

◆ userhdrs_override_cmp()

static int userhdrs_override_cmp ( const void *  a,
const void *  b 
)
static

Compare a user-defined header with an element of the userhdrs_override_headers list.

Parameters
aPointer to the string containing the user-defined header
bPointer to an element of the userhdrs_override_headers list
Return values
-1a precedes b
0a and b are identical
1b precedes a

Definition at line 271 of file header.c.

272 {
273  const char *ca = a;
274  const char *cb = *(const char **) b;
275  return mutt_istrn_cmp(ca, cb, strlen(cb));
276 }
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ write_one_header()

static int write_one_header ( FILE *  fp,
int  pfxw,
int  max,
int  wraplen,
const char *  pfx,
const char *  start,
const char *  end,
CopyHeaderFlags  chflags 
)
static

Write out one header line.

Parameters
fpFile to write to
pfxwWidth of prefix string
maxMax width
wraplenColumn to wrap at
pfxPrefix for header
startStart of header line
endEnd of header line
chflagsFlags, see CopyHeaderFlags
Return values
0Success
-1Failure

Definition at line 291 of file header.c.

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 }
#define NONULL(x)
Definition: string2.h:37
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
Log at debug level 2.
Definition: logging.h:41
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:553
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
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
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
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
Log at debug level 1.
Definition: logging.h:40
#define FREE(x)
Definition: memory.h:40
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
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 5.
Definition: logging.h:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ write_userhdrs()

static struct UserHdrsOverride write_userhdrs ( FILE *  fp,
const struct ListHead *  userhdrs,
bool  privacy,
struct ConfigSubset sub 
)
static

Write user-defined headers and keep track of the interesting ones.

Parameters
fpFILE pointer where to write the headers
userhdrsList of headers to write
privacyOmit headers that could identify the user
subConfig Subset
Return values
objUserHdrsOverride struct containing a bitmask of which unique headers were written

Definition at line 360 of file header.c.

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 }
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
Which headers have been overridden.
Definition: header.c:67
#define mutt_array_size(x)
Definition: memory.h:33
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
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:748
Override the "User-Agent".
Definition: header.c:61
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Definition: header.c:69
char * data
String.
Definition: list.h:36
A List node for strings.
Definition: list.h:34
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_one_header()

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.

Parameters
fpFile to write to
tagHeader key, e.g. "From"
valueHeader value
pfxPrefix for header
wraplenColumn to wrap at
chflagsFlags, see CopyHeaderFlags
subConfig Subset
Return values
0Success
-1Failure

split several headers into individual ones and call write_one_header for each one

Definition at line 419 of file header.c.

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 }
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
#define NONULL(x)
Definition: string2.h:37
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
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
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
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1359
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
int mutt_strnwidth(const char *s, size_t n)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1372
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
#define FREE(x)
Definition: memory.h:40
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int const char int line
Definition: acutest.h:617
Log at debug level 5.
Definition: logging.h:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_references()

void mutt_write_references ( const struct ListHead *  r,
FILE *  fp,
size_t  trim,
struct ConfigSubset sub 
)

Add the message references to a list.

Parameters
rString List of references
fpFile to write to
trimTrim the list to at most this many items
subConfig Subset

Write the list in reverse because they are stored in reverse order when parsed to speed up threading.

Definition at line 514 of file header.c.

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 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
char * data
String.
Definition: list.h:36
#define FREE(x)
Definition: memory.h:40
A List node for strings.
Definition: list.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc822_write_header()

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.

Parameters
fpFile to write to
envEnvelope of email
attachAttachment
modeMode, see MuttWriteHeaderMode
privacyIf true, remove headers that might identify the user
hide_protected_subjectIf true, replace subject header
subConfig Subset
Return values
0Success
-1Failure
Note
All RFC2047 encoding should be done outside of this routine, except for the "real name." This will allow this routine to be used more than once, if necessary.

Likewise, all IDN processing should happen outside of this routine.

privacy true => will omit any headers which may identify the user. Output generated is suitable for being sent through anonymous remailer chains.

hide_protected_subject: replaces the Subject header with $crypt_protected_headers_subject in NORMAL or POSTPONE mode.

Definition at line 573 of file header.c.

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 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:68
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
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
Which headers have been overridden.
Definition: header.c:67
struct AddressList reply_to
Email&#39;s &#39;reply-to&#39;.
Definition: envelope.h:62
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
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
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
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
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_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:760
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
#define mutt_b2s(buf)
Definition: buffer.h:41
char * x_comment_to
List of &#39;X-comment-to&#39; fields.
Definition: envelope.h:78
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
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
Override the "User-Agent".
Definition: header.c:61
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Definition: header.c:69
char * subject
Email&#39;s subject.
Definition: envelope.h:66
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
#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
#define TAILQ_EMPTY(head)
Definition: queue.h:714
Override the "Content-Type".
Definition: header.c:60
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:46
const char * GitVer
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
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
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
"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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_mime_header()

int mutt_write_mime_header ( struct Body a,
FILE *  fp,
struct ConfigSubset sub 
)

Create a MIME header.

Parameters
aBody part
fpFile to write to
subConfig Subset
Return values
0Success
-1Failure

Definition at line 760 of file header.c.

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
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
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
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
7-bit text
Definition: mime.h:49
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
#define ENCODING(x)
Definition: mime.h:91
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
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
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
Write protected headers.
Definition: header.h:44
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
char * description
content-description
Definition: body.h:40
#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 * value
Parameter value.
Definition: parameter.h:35
Log at debug level 1.
Definition: logging.h:40
No preferred disposition.
Definition: mime.h:65
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:68
Attribute associated with a MIME part.
Definition: parameter.h:32
char * language
content-language (RFC8255)
Definition: body.h:38
char * d_filename
filename to be used for the content-disposition header.
Definition: body.h:47
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define TAILQ_EMPTY(head)
Definition: queue.h:714
#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
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
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ userhdrs_override_headers

const char* const userhdrs_override_headers[]
static
Initial value:
= {
"content-type:",
"user-agent:",
}

The next array/enum pair is used to to keep track of user headers that override pre-defined headers NeoMutt would emit.

Keep the array sorted and in sync with the enum.

Definition at line 50 of file header.c.