NeoMutt  2021-02-05-666-ge300cd
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 "context.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 59 of file header.c.

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

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 83 of file header.c.

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 }
#define CH_DISPLAY
Display result to user.
Definition: copy.h:70
+ 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 130 of file header.c.

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 }
#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:138
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
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
#define CH_DISPLAY
Display result to user.
Definition: copy.h:70
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
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:1004
+ 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 235 of file header.c.

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 }
+ 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 272 of file header.c.

273 {
274  const char *ca = a;
275  const char *cb = *(const char **) b;
276  return mutt_istrn_cmp(ca, cb, strlen(cb));
277 }
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
+ 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 292 of file header.c.

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 }
#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:130
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:548
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
#define CH_DISPLAY
Display result to user.
Definition: copy.h:70
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
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
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
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 361 of file header.c.

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 }
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
Which headers have been overridden.
Definition: header.c:68
size_t idx
Definition: mailbox.c:257
#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:51
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:51
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
Override the "User-Agent".
Definition: header.c:62
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Definition: header.c:70
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:420
+ 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 420 of file header.c.

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 }
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
#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:73
static char * unfold_header(char *s)
Unfold a wrapped email header.
Definition: header.c:235
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
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
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
int mutt_mb_width(const char *str, int col, bool display)
Measure a string&#39;s display width (in screen columns)
Definition: mbyte.c:138
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:983
#define CH_DISPLAY
Display result to user.
Definition: copy.h:70
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
int mutt_strnwidth(const char *s, size_t n)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:996
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
#define FREE(x)
Definition: memory.h:40
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 515 of file header.c.

517 {
518  struct ListNode *np = NULL;
519  size_t length = 0;
520 
521  STAILQ_FOREACH(np, r, entries)
522  {
523  if (++length == trim)
524  break;
525  }
526 
527  struct ListNode **ref = mutt_mem_calloc(length, sizeof(struct ListNode *));
528 
529  // store in reverse order
530  size_t tmp = length;
531  STAILQ_FOREACH(np, r, entries)
532  {
533  ref[--tmp] = np;
534  if (tmp == 0)
535  break;
536  }
537 
538  for (size_t i = 0; i < length; i++)
539  {
540  fputc(' ', fp);
541  fputs(ref[i]->data, fp);
542  if (i != length - 1)
543  fputc('\n', fp);
544  }
545 
546  FREE(&ref);
547 }
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:352
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 574 of file header.c.

577 {
578  char buf[1024];
579 
580  if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
581  (mode == MUTT_WRITE_HEADER_POSTPONE)) &&
582  !privacy)
583  {
584  struct Buffer *date = mutt_buffer_pool_get();
585  mutt_date_make_date(date, cs_subset_bool(sub, "local_date_header"));
586  fprintf(fp, "Date: %s\n", mutt_buffer_string(date));
588  }
589 
590  /* UseFrom is not consulted here so that we can still write a From:
591  * field if the user sets it with the 'my_hdr' command */
592  if (!TAILQ_EMPTY(&env->from) && !privacy)
593  {
594  buf[0] = '\0';
595  mutt_addrlist_write(&env->from, buf, sizeof(buf), false);
596  fprintf(fp, "From: %s\n", buf);
597  }
598 
599  if (!TAILQ_EMPTY(&env->sender) && !privacy)
600  {
601  buf[0] = '\0';
602  mutt_addrlist_write(&env->sender, buf, sizeof(buf), false);
603  fprintf(fp, "Sender: %s\n", buf);
604  }
605 
606  if (!TAILQ_EMPTY(&env->to))
607  {
608  fputs("To: ", fp);
609  mutt_addrlist_write_file(&env->to, fp, 4, false);
610  }
611  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
612 #ifdef USE_NNTP
613  if (!OptNewsSend)
614 #endif
615  fputs("To:\n", fp);
616 
617  if (!TAILQ_EMPTY(&env->cc))
618  {
619  fputs("Cc: ", fp);
620  mutt_addrlist_write_file(&env->cc, fp, 4, false);
621  }
622  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
623 #ifdef USE_NNTP
624  if (!OptNewsSend)
625 #endif
626  fputs("Cc:\n", fp);
627 
628  if (!TAILQ_EMPTY(&env->bcc))
629  {
630  const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
631 
632  if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
633  (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
634  ((mode == MUTT_WRITE_HEADER_NORMAL) && c_write_bcc))
635  {
636  fputs("Bcc: ", fp);
637  mutt_addrlist_write_file(&env->bcc, fp, 5, false);
638  }
639  }
640  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
641 #ifdef USE_NNTP
642  if (!OptNewsSend)
643 #endif
644  fputs("Bcc:\n", fp);
645 
646 #ifdef USE_NNTP
647  if (env->newsgroups)
648  fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
649  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
650  fputs("Newsgroups:\n", fp);
651 
652  if (env->followup_to)
653  fprintf(fp, "Followup-To: %s\n", env->followup_to);
654  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
655  fputs("Followup-To:\n", fp);
656 
657  const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
658  if (env->x_comment_to)
659  fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
660  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && c_x_comment_to)
661  fputs("X-Comment-To:\n", fp);
662 #endif
663 
664  if (env->subject)
665  {
666  if (hide_protected_subject &&
667  ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
668  (mode == MUTT_WRITE_HEADER_POSTPONE)))
669  {
670  const char *const c_crypt_protected_headers_subject =
671  cs_subset_string(sub, "crypt_protected_headers_subject");
672  mutt_write_one_header(fp, "Subject", c_crypt_protected_headers_subject,
673  NULL, 0, CH_NO_FLAGS, sub);
674  }
675  else
676  mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NO_FLAGS, sub);
677  }
678  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
679  fputs("Subject:\n", fp);
680 
681  /* save message id if the user has set it */
682  if (env->message_id && !privacy)
683  fprintf(fp, "Message-ID: %s\n", env->message_id);
684 
685  if (!TAILQ_EMPTY(&env->reply_to))
686  {
687  fputs("Reply-To: ", fp);
688  mutt_addrlist_write_file(&env->reply_to, fp, 10, false);
689  }
690  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
691  fputs("Reply-To:\n", fp);
692 
693  if (!TAILQ_EMPTY(&env->mail_followup_to))
694  {
695 #ifdef USE_NNTP
696  if (!OptNewsSend)
697 #endif
698  {
699  fputs("Mail-Followup-To: ", fp);
700  mutt_addrlist_write_file(&env->mail_followup_to, fp, 18, false);
701  }
702  }
703 
704  /* Add any user defined headers */
705  struct UserHdrsOverride userhdrs_overrides =
706  write_userhdrs(fp, &env->userhdrs, privacy, sub);
707 
708  if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
709  (mode == MUTT_WRITE_HEADER_POSTPONE) || (mode == MUTT_WRITE_HEADER_MIME))
710  {
711  if (!STAILQ_EMPTY(&env->references))
712  {
713  fputs("References:", fp);
714  mutt_write_references(&env->references, fp, 10, sub);
715  fputc('\n', fp);
716  }
717 
718  /* Add the MIME headers */
719  if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
720  {
721  fputs("MIME-Version: 1.0\n", fp);
722  mutt_write_mime_header(attach, fp, sub);
723  }
724  }
725 
726  if (!STAILQ_EMPTY(&env->in_reply_to))
727  {
728  fputs("In-Reply-To:", fp);
729  mutt_write_references(&env->in_reply_to, fp, 0, sub);
730  fputc('\n', fp);
731  }
732 
733 #ifdef USE_AUTOCRYPT
734  const bool c_autocrypt = cs_subset_bool(sub, "autocrypt");
735  if (c_autocrypt)
736  {
737  struct Mailbox *m = ctx_mailbox(Context);
738  if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
740  if (mode == MUTT_WRITE_HEADER_MIME)
742  }
743 #endif
744 
745  const bool c_user_agent = cs_subset_bool(sub, "user_agent");
746  if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
747  c_user_agent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
748  {
749  /* Add a vanity header */
750  fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
751  }
752 
753  return (ferror(fp) == 0) ? 0 : -1;
754 }
The "current" mailbox.
Definition: context.h:37
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
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:68
struct Mailbox * ctx_mailbox(struct Context *ctx)
wrapper to get the mailbox in a Context, or NULL
Definition: context.c:444
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
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:378
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:515
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
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:764
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
int mutt_autocrypt_write_gossip_headers(struct Mailbox *m, struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition: autocrypt.c:818
char * x_comment_to
List of &#39;X-comment-to&#39; fields.
Definition: envelope.h:78
A mailbox.
Definition: mailbox.h:81
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:51
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:317
Override the "User-Agent".
Definition: header.c:62
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Definition: header.c:70
char * subject
Email&#39;s subject.
Definition: envelope.h:66
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
int mutt_autocrypt_write_autocrypt_header(struct Mailbox *m, struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition: autocrypt.c:779
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
#define STAILQ_EMPTY(head)
Definition: queue.h:348
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:721
Override the "Content-Type".
Definition: header.c:61
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:45
const char * GitVer
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
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:420
"light" mode (used for edit_hdrs)
Definition: header.h:43
+ 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 764 of file header.c.

765 {
766  if (!a || !fp)
767  return -1;
768 
769  int len;
770  int tmplen;
771  char buf[256] = { 0 };
772 
773  fprintf(fp, "Content-Type: %s/%s", TYPE(a), a->subtype);
774 
775  if (!TAILQ_EMPTY(&a->parameter))
776  {
777  len = 25 + mutt_str_len(a->subtype); /* approximate len. of content-type */
778 
779  struct Parameter *np = NULL;
780  TAILQ_FOREACH(np, &a->parameter, entries)
781  {
782  if (!np->attribute || !np->value)
783  continue;
784 
785  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
786  rfc2231_encode_string(&pl_conts, np->attribute, np->value);
787  struct Parameter *cont = NULL;
788  TAILQ_FOREACH(cont, &pl_conts, entries)
789  {
790  fputc(';', fp);
791 
792  buf[0] = 0;
793  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
794 
795  /* Dirty hack to make messages readable by Outlook Express
796  * for the Mac: force quotes around the boundary parameter
797  * even when they aren't needed. */
798  if (mutt_istr_equal(cont->attribute, "boundary") && mutt_str_equal(buf, cont->value))
799  snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
800 
801  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
802  if (len + tmplen + 2 > 76)
803  {
804  fputs("\n\t", fp);
805  len = tmplen + 1;
806  }
807  else
808  {
809  fputc(' ', fp);
810  len += tmplen + 1;
811  }
812 
813  fprintf(fp, "%s=%s", cont->attribute, buf);
814  }
815 
816  mutt_param_free(&pl_conts);
817  }
818  }
819 
820  fputc('\n', fp);
821 
822  if (a->language)
823  fprintf(fp, "Content-Language: %s\n", a->language);
824 
825  if (a->description)
826  fprintf(fp, "Content-Description: %s\n", a->description);
827 
828  if (a->disposition != DISP_NONE)
829  {
830  const char *dispstr[] = { "inline", "attachment", "form-data" };
831 
832  if (a->disposition < sizeof(dispstr) / sizeof(char *))
833  {
834  fprintf(fp, "Content-Disposition: %s", dispstr[a->disposition]);
835  len = 21 + mutt_str_len(dispstr[a->disposition]);
836 
837  if (a->use_disp && (a->disposition != DISP_INLINE))
838  {
839  char *fn = a->d_filename;
840  if (!fn)
841  fn = a->filename;
842 
843  if (fn)
844  {
845  /* Strip off the leading path... */
846  char *t = strrchr(fn, '/');
847  if (t)
848  t++;
849  else
850  t = fn;
851 
852  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
853  rfc2231_encode_string(&pl_conts, "filename", t);
854  struct Parameter *cont = NULL;
855  TAILQ_FOREACH(cont, &pl_conts, entries)
856  {
857  fputc(';', fp);
858  buf[0] = 0;
859  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
860 
861  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
862  if (len + tmplen + 2 > 76)
863  {
864  fputs("\n\t", fp);
865  len = tmplen + 1;
866  }
867  else
868  {
869  fputc(' ', fp);
870  len += tmplen + 1;
871  }
872 
873  fprintf(fp, "%s=%s", cont->attribute, buf);
874  }
875 
876  mutt_param_free(&pl_conts);
877  }
878  }
879 
880  fputc('\n', fp);
881  }
882  else
883  {
884  mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n", a->disposition);
885  }
886  }
887 
888  if (a->encoding != ENC_7BIT)
889  fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding));
890 
891  const bool c_crypt_protected_headers_write =
892  cs_subset_bool(sub, "crypt_protected_headers_write");
893  bool autocrypt = false;
894 #ifdef USE_AUTOCRYPT
895  autocrypt = cs_subset_bool(sub, "autocrypt");
896 #endif
897 
898  if ((c_crypt_protected_headers_write || autocrypt) && a->mime_headers)
899  {
901  false, false, sub);
902  }
903 
904  /* Do NOT add the terminator here!!! */
905  return ferror(fp) ? -1 : 0;
906 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
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:73
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
7-bit text
Definition: mime.h:49
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
#define ENCODING(x)
Definition: mime.h:92
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:916
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:332
char * description
content-description
Definition: body.h:40
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
#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:664
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 TAILQ_EMPTY(head)
Definition: queue.h:721
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
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:574
+ 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 51 of file header.c.