NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "globals.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.
 
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.
 
static char * unfold_header (char *s)
 Unfold a wrapped email header.
 
static int userhdrs_override_cmp (const void *a, const void *b)
 Compare a user-defined header with an element of the UserhdrsOverrideHeaders list.
 
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.
 
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.
 
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.
 
void mutt_write_references (const struct ListHead *r, FILE *fp, size_t trim)
 Add the message references to a list.
 
int mutt_rfc822_write_header (FILE *fp, struct Envelope *env, struct Body *b, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
 Write out one RFC822 header line.
 
int mutt_write_mime_header (struct Body *b, FILE *fp, struct ConfigSubset *sub)
 Create a MIME header.
 

Variables

static const char *const UserhdrsOverrideHeaders []
 The next array/enum pair is used to to keep track of user headers that override pre-defined headers NeoMutt would emit.
 

Detailed Description

Write a MIME Email Header to a file.

Authors
  • Richard Russon
  • David Purton
  • Pietro Cerutti
  • Alejandro Colomar

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

61{
64};
@ USERHDRS_OVERRIDE_CONTENT_TYPE
Override the "Content-Type".
Definition header.c:62
@ USERHDRS_OVERRIDE_USER_AGENT
Override the "User-Agent".
Definition header.c:63

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

87{
88 while (value && (value[0] != '\0'))
89 {
90 if (fputc(*value, fp) == EOF)
91 return -1;
92 /* corner-case: break words longer than 998 chars by force,
93 * mandated by RFC5322 */
94 if (!(chflags & CH_DISPLAY) && (++col >= 998))
95 {
96 if (fputs("\n ", fp) < 0)
97 return -1;
98 col = 1;
99 }
100 if (*value == '\n')
101 {
102 if ((value[1] != '\0') && pfx && (pfx[0] != '\0') && (fputs(pfx, fp) == EOF))
103 return -1;
104 /* for display, turn folding spaces into folding tabs */
105 if ((chflags & CH_DISPLAY) && ((value[1] == ' ') || (value[1] == '\t')))
106 {
107 value++;
108 while ((value[0] != '\0') && ((value[0] == ' ') || (value[0] == '\t')))
109 value++;
110 if (fputc('\t', fp) == EOF)
111 return -1;
112 continue;
113 }
114 }
115 value++;
116 }
117 return 0;
118}
#define CH_DISPLAY
Display result to user.
Definition copy_email.h:74
+ 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 132 of file header.c.

134{
135 if (!value || (*value == '\0') || !vlen)
136 return 0;
137
138 const char *p = value;
139 char buf[8192] = { 0 };
140 int first = 1, col = 0, l = 0;
141 const bool display = (chflags & CH_DISPLAY);
142
143 mutt_debug(LL_DEBUG5, "pfx=[%s], tag=[%s], flags=%d value=[%.*s]\n", pfx, tag,
144 chflags, (int) ((value[vlen - 1] == '\n') ? vlen - 1 : vlen), value);
145
146 if (tag && *tag && (fprintf(fp, "%s%s: ", NONULL(pfx), tag) < 0))
147 return -1;
148 col = mutt_str_len(tag) + ((tag && (tag[0] != '\0')) ? 2 : 0) + mutt_str_len(pfx);
149
150 while (p && (p[0] != '\0'))
151 {
152 int fold = 0;
153
154 /* find the next word and place it in 'buf'. it may start with
155 * whitespace we can fold before */
156 const char *next = mutt_str_find_word(p);
157 l = MIN(sizeof(buf) - 1, next - p);
158 memcpy(buf, p, l);
159 buf[l] = '\0';
160
161 /* determine width: character cells for display, bytes for sending
162 * (we get pure ascii only) */
163 const int w = mutt_mb_width(buf, col, display);
164 const int enc = mutt_str_startswith(buf, "=?");
165
166 mutt_debug(LL_DEBUG5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n",
167 (buf[0] == '\n' ? "\\n" : buf), col, w, *next);
168
169 /* insert a folding \n before the current word's lwsp except for
170 * header name, first word on a line (word longer than wrap width)
171 * and encoded words */
172 if (!first && !enc && col && ((col + w) >= wraplen))
173 {
174 col = mutt_str_len(pfx);
175 fold = 1;
176 if (fprintf(fp, "\n%s", NONULL(pfx)) <= 0)
177 return -1;
178 }
179
180 /* print the actual word; for display, ignore leading ws for word
181 * and fold with tab for readability */
182 if (display && fold)
183 {
184 char *pc = buf;
185 while ((pc[0] != '\0') && ((pc[0] == ' ') || (pc[0] == '\t')))
186 {
187 pc++;
188 col--;
189 }
190 if (fputc('\t', fp) == EOF)
191 return -1;
192 if (print_val(fp, pfx, pc, chflags, col) < 0)
193 return -1;
194 col += 8;
195 }
196 else if (print_val(fp, pfx, buf, chflags, col) < 0)
197 {
198 return -1;
199 }
200 col += w;
201
202 /* if the current word ends in \n, ignore all its trailing spaces
203 * and reset column; this prevents us from putting only spaces (or
204 * even none) on a line if the trailing spaces are located at our
205 * current line width
206 * XXX this covers ASCII space only, for display we probably
207 * want something like iswspace() here */
208 const char *sp = next;
209 while ((sp[0] != '\0') && ((sp[0] == ' ') || (sp[0] == '\t')))
210 sp++;
211 if (sp[0] == '\n')
212 {
213 if (sp[1] == '\0')
214 break;
215 next = sp;
216 col = 0;
217 }
218
219 p = next;
220 first = 0;
221 }
222
223 /* if we have printed something but didn't \n-terminate it, do it
224 * except the last word we printed ended in \n already */
225 if (col && ((l == 0) || (buf[l - 1] != '\n')))
226 if (putc('\n', fp) == EOF)
227 return -1;
228
229 return 0;
230}
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
int mutt_mb_width(const char *str, int col, bool indent)
Measure a string's display width (in screen columns)
Definition mbyte.c:138
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition string.c:708
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
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:85
#define NONULL(x)
Definition string2.h:44
+ 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 239 of file header.c.

240{
241 char *p = s;
242 char *q = s;
243
244 while (p && (p[0] != '\0'))
245 {
246 /* remove CRLF prior to FWSP, turn \t into ' ' */
247 if ((p[0] == '\r') && (p[1] == '\n') && ((p[2] == ' ') || (p[2] == '\t')))
248 {
249 *q++ = ' ';
250 p += 3;
251 continue;
252 }
253 else if ((p[0] == '\n') && ((p[1] == ' ') || (p[1] == '\t')))
254 {
255 /* remove LF prior to FWSP, turn \t into ' ' */
256 *q++ = ' ';
257 p += 2;
258 continue;
259 }
260 *q++ = *p++;
261 }
262 if (q)
263 q[0] = '\0';
264
265 return s;
266}
+ 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 UserhdrsOverrideHeaders list.

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

Definition at line 276 of file header.c.

277{
278 const char *ca = a;
279 const char *cb = *(const char **) b;
280 return mutt_istrn_cmp(ca, cb, strlen(cb));
281}
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:443
+ 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 296 of file header.c.

298{
299 const char *t = strchr(start, ':');
300 if (!t || (t >= end))
301 {
302 mutt_debug(LL_DEBUG1, "#2 warning: header not in 'key: value' format!\n");
303 return 0;
304 }
305
306 const size_t vallen = end - start;
307 const bool short_enough = (pfxw + max <= wraplen);
308
309 mutt_debug((short_enough ? LL_DEBUG2 : LL_DEBUG5), "buf[%s%.*s] %s, max width = %d %s %d\n",
310 NONULL(pfx), (int) (vallen - 1) /* skip newline */, start,
311 (short_enough ? "short enough" : "too long"), max,
312 (short_enough ? "<=" : ">"), wraplen);
313
314 int rc = 0;
315 const char *valbuf = NULL, *tagbuf = NULL;
316 const bool is_from = (vallen > 5) && mutt_istr_startswith(start, "from ");
317
318 /* only pass through folding machinery if necessary for sending,
319 * never wrap From_ headers on sending */
320 if (!(chflags & CH_DISPLAY) && (short_enough || is_from))
321 {
322 if (pfx && *pfx)
323 {
324 if (fputs(pfx, fp) == EOF)
325 {
326 return -1;
327 }
328 }
329
330 valbuf = mutt_strn_dup(start, end - start);
331 rc = print_val(fp, pfx, valbuf, chflags, mutt_str_len(pfx));
332 }
333 else
334 {
335 if (!is_from)
336 {
337 tagbuf = mutt_strn_dup(start, t - start);
338 /* skip over the colon separating the header field name and value */
339 t++;
340
341 /* skip over any leading whitespace (WSP, as defined in RFC5322)
342 * NOTE: mutt_str_skip_email_wsp() does the wrong thing here.
343 * See tickets 3609 and 3716. */
344 while ((*t == ' ') || (*t == '\t'))
345 t++;
346 }
347 const char *s = is_from ? start : t;
348 valbuf = mutt_strn_dup(s, end - s);
349 rc = fold_one_header(fp, tagbuf, valbuf, end - s, pfx, wraplen, chflags);
350 }
351
352 FREE(&tagbuf);
353 FREE(&valbuf);
354 return rc;
355}
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition from.c:49
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
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:132
+ 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 365 of file header.c.

367{
368 struct UserHdrsOverride overrides = { { 0 } };
369
370 struct ListNode *tmp = NULL;
371 STAILQ_FOREACH(tmp, userhdrs, entries)
372 {
373 char *const colon = strchr(NONULL(tmp->data), ':');
374 if (!colon)
375 {
376 continue;
377 }
378
379 const char *const value = mutt_str_skip_email_wsp(colon + 1);
380 if (*value == '\0')
381 {
382 continue; /* don't emit empty fields. */
383 }
384
385 /* check whether the current user-header is an override */
386 size_t cur_override = ICONV_ILLEGAL_SEQ;
387 const char *const *idx = bsearch(tmp->data, UserhdrsOverrideHeaders,
389 sizeof(char *), userhdrs_override_cmp);
390 if (idx)
391 {
392 cur_override = idx - UserhdrsOverrideHeaders;
393 overrides.is_overridden[cur_override] = true;
394 }
395
396 if (privacy && (cur_override == USERHDRS_OVERRIDE_USER_AGENT))
397 {
398 continue;
399 }
400
401 *colon = '\0';
402 mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS, sub);
403 *colon = ':';
404 }
405
406 return overrides;
407}
#define CH_NO_FLAGS
No flags are set.
Definition copy_email.h:55
#define countof(x)
Definition memory.h:49
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:610
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
static const char *const UserhdrsOverrideHeaders[]
The next array/enum pair is used to to keep track of user headers that override pre-defined headers N...
Definition header.c:52
static int userhdrs_override_cmp(const void *a, const void *b)
Compare a user-defined header with an element of the UserhdrsOverrideHeaders list.
Definition header.c:276
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:424
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Which headers have been overridden.
Definition header.c:70
bool is_overridden[countof(UserhdrsOverrideHeaders)]
Which email headers have been overridden.
Definition header.c:72
+ 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 424 of file header.c.

427{
428 char *last = NULL, *line = NULL;
429 int max = 0, w, rc = -1;
430 int pfxw = mutt_strwidth(pfx);
431 char *v = mutt_str_dup(value);
432 bool display = (chflags & CH_DISPLAY);
433
434 const bool c_weed = cs_subset_bool(sub, "weed");
435 if (!display || c_weed)
436 v = unfold_header(v);
437
438 /* when not displaying, use sane wrap value */
439 if (!display)
440 {
441 const short c_wrap_headers = cs_subset_number(sub, "wrap_headers");
442 if ((c_wrap_headers < 78) || (c_wrap_headers > 998))
443 wraplen = 78;
444 else
445 wraplen = c_wrap_headers;
446 }
447 else if (wraplen <= 0)
448 {
449 wraplen = 78;
450 }
451
452 const size_t vlen = mutt_str_len(v);
453 if (tag)
454 {
455 /* if header is short enough, simply print it */
456 if (!display && (mutt_strwidth(tag) + 2 + pfxw + mutt_strnwidth(v, vlen) <= wraplen))
457 {
458 mutt_debug(LL_DEBUG5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
459 if (fprintf(fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
460 goto out;
461 rc = 0;
462 goto out;
463 }
464 else
465 {
466 rc = fold_one_header(fp, tag, v, vlen, pfx, wraplen, chflags);
467 goto out;
468 }
469 }
470
471 char *p = v;
472 last = v;
473 line = v;
474 while (p && *p)
475 {
476 p = strchr(p, '\n');
477
478 /* find maximum line width in current header */
479 if (p)
480 *p = '\0';
481 w = mutt_mb_width(line, 0, display);
482 if (w > max)
483 max = w;
484 if (p)
485 *p = '\n';
486
487 if (!p)
488 break;
489
490 line = ++p;
491 if ((*p != ' ') && (*p != '\t'))
492 {
493 if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
494 goto out;
495 last = p;
496 max = 0;
497 }
498 }
499
500 if (last && *last)
501 if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
502 goto out;
503
504 rc = 0;
505
506out:
507 FREE(&v);
508 return rc;
509}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
size_t mutt_strnwidth(const char *s, size_t n)
Measure a string's width in screen cells.
Definition curs_lib.c:457
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition curs_lib.c:444
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
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:296
static char * unfold_header(char *s)
Unfold a wrapped email header.
Definition header.c:239
+ 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 )

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

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

Definition at line 520 of file header.c.

521{
522 struct ListNode *np = NULL;
523 size_t length = 0;
524
525 STAILQ_FOREACH(np, r, entries)
526 {
527 if (++length == trim)
528 break;
529 }
530
531 struct ListNode **ref = MUTT_MEM_CALLOC(length, struct ListNode *);
532
533 // store in reverse order
534 size_t tmp = length;
535 STAILQ_FOREACH(np, r, entries)
536 {
537 ref[--tmp] = np;
538 if (tmp == 0)
539 break;
540 }
541
542 for (size_t i = 0; i < length; i++)
543 {
544 fputc(' ', fp);
545 fputs(ref[i]->data, fp);
546 if (i != length - 1)
547 fputc('\n', fp);
548 }
549
550 FREE(&ref);
551}
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
+ Here is the caller graph for this function:

◆ mutt_rfc822_write_header()

int mutt_rfc822_write_header ( FILE * fp,
struct Envelope * env,
struct Body * b,
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
bAttachment
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 578 of file header.c.

581{
582 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
583 (mode == MUTT_WRITE_HEADER_POSTPONE)) &&
584 !privacy)
585 {
586 struct Buffer *date = buf_pool_get();
587 mutt_date_make_date(date, cs_subset_bool(sub, "local_date_header"));
588 fprintf(fp, "Date: %s\n", buf_string(date));
589 buf_pool_release(&date);
590 }
591
592 /* UseFrom is not consulted here so that we can still write a From:
593 * field if the user sets it with the 'my-header' command */
594 if (!TAILQ_EMPTY(&env->from) && !privacy)
595 {
596 mutt_addrlist_write_file(&env->from, fp, "From");
597 }
598
599 if (!TAILQ_EMPTY(&env->sender) && !privacy)
600 {
601 mutt_addrlist_write_file(&env->sender, fp, "Sender");
602 }
603
604 if (!TAILQ_EMPTY(&env->to))
605 {
606 mutt_addrlist_write_file(&env->to, fp, "To");
607 }
608 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
609 {
610 if (!OptNewsSend)
611 fputs("To:\n", fp);
612 }
613
614 if (!TAILQ_EMPTY(&env->cc))
615 {
616 mutt_addrlist_write_file(&env->cc, fp, "Cc");
617 }
618 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
619 {
620 if (!OptNewsSend)
621 fputs("Cc:\n", fp);
622 }
623
624 if (!TAILQ_EMPTY(&env->bcc))
625 {
626 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
627
628 if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
629 (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
630 ((mode == MUTT_WRITE_HEADER_NORMAL) && c_write_bcc))
631 {
632 mutt_addrlist_write_file(&env->bcc, fp, "Bcc");
633 }
634 }
635 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
636 {
637 if (!OptNewsSend)
638 fputs("Bcc:\n", fp);
639 }
640
641 if (env->newsgroups)
642 fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
643 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
644 fputs("Newsgroups:\n", fp);
645
646 if (env->followup_to)
647 fprintf(fp, "Followup-To: %s\n", env->followup_to);
648 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
649 fputs("Followup-To:\n", fp);
650
651 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
652 if (env->x_comment_to)
653 fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
654 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && c_x_comment_to)
655 fputs("X-Comment-To:\n", fp);
656
657 if (env->subject)
658 {
659 if (hide_protected_subject &&
660 ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
662 {
663 const char *const c_crypt_protected_headers_subject = cs_subset_string(sub, "crypt_protected_headers_subject");
664 mutt_write_one_header(fp, "Subject", c_crypt_protected_headers_subject,
665 NULL, 0, CH_NO_FLAGS, sub);
666 }
667 else
668 {
669 mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NO_FLAGS, sub);
670 }
671 }
672 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
673 {
674 fputs("Subject:\n", fp);
675 }
676
677 /* save message id if the user has set it */
678 if (env->message_id && !privacy)
679 fprintf(fp, "Message-ID: %s\n", env->message_id);
680
681 if (!TAILQ_EMPTY(&env->reply_to))
682 {
683 mutt_addrlist_write_file(&env->reply_to, fp, "Reply-To");
684 }
685 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
686 {
687 fputs("Reply-To:\n", fp);
688 }
689
690 if (!TAILQ_EMPTY(&env->mail_followup_to))
691 {
692 if (!OptNewsSend)
693 {
694 mutt_addrlist_write_file(&env->mail_followup_to, fp, "Mail-Followup-To");
695 }
696 }
697
698 /* Add any user defined headers */
699 struct UserHdrsOverride userhdrs_overrides = write_userhdrs(fp, &env->userhdrs,
700 privacy, sub);
701
702 if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
704 {
705 if (!STAILQ_EMPTY(&env->references))
706 {
707 fputs("References:", fp);
708 mutt_write_references(&env->references, fp, 10);
709 fputc('\n', fp);
710 }
711
712 /* Add the MIME headers */
713 if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
714 {
715 fputs("MIME-Version: 1.0\n", fp);
716 mutt_write_mime_header(b, fp, sub);
717 }
718 }
719
720 if (!STAILQ_EMPTY(&env->in_reply_to))
721 {
722 fputs("In-Reply-To:", fp);
724 fputc('\n', fp);
725 }
726
727#ifdef USE_AUTOCRYPT
728 const bool c_autocrypt = cs_subset_bool(sub, "autocrypt");
729 if (c_autocrypt)
730 {
731 if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
733 if (mode == MUTT_WRITE_HEADER_MIME)
735 }
736#endif
737
738 const bool c_user_agent = cs_subset_bool(sub, "user_agent");
739 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
740 c_user_agent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
741 {
742 /* Add a vanity header */
743 fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
744 }
745
746 return (ferror(fp) == 0) ? 0 : -1;
747}
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
Wrapper for mutt_write_address()
Definition address.c:1252
int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition autocrypt.c:801
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition autocrypt.c:763
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition globals.c:54
const char * GitVer
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition date.c:398
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define STAILQ_EMPTY(head)
Definition queue.h:382
#define TAILQ_EMPTY(head)
Definition queue.h:778
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:365
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition header.c:757
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition header.c:520
@ MUTT_WRITE_HEADER_FCC
fcc mode, like normal mode but for Bcc header
Definition header.h:39
@ MUTT_WRITE_HEADER_MIME
Write protected headers.
Definition header.h:42
@ MUTT_WRITE_HEADER_NORMAL
A normal Email, write full header + MIME headers.
Definition header.h:38
@ MUTT_WRITE_HEADER_POSTPONE
A postponed Email, just the envelope info.
Definition header.h:40
@ MUTT_WRITE_HEADER_EDITHDRS
"light" mode (used for edit_hdrs)
Definition header.h:41
String manipulation buffer.
Definition buffer.h:36
struct ListHead userhdrs
user defined headers
Definition envelope.h:85
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
char * newsgroups
List of newsgroups.
Definition envelope.h:78
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList sender
Email's sender.
Definition envelope.h:63
struct ListHead references
message references (in reverse order)
Definition envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
+ 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 * b,
FILE * fp,
struct ConfigSubset * sub )

Create a MIME header.

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

Definition at line 757 of file header.c.

758{
759 if (!b || !fp)
760 return -1;
761
762 int len;
763 int tmplen;
764 char buf[256] = { 0 };
765
766 fprintf(fp, "Content-Type: %s/%s", BODY_TYPE(b), b->subtype);
767
768 if (!TAILQ_EMPTY(&b->parameter))
769 {
770 len = 25 + mutt_str_len(b->subtype); /* approximate len. of content-type */
771
772 struct Parameter *np = NULL;
773 TAILQ_FOREACH(np, &b->parameter, entries)
774 {
775 if (!np->attribute || !np->value)
776 continue;
777
778 struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
779 rfc2231_encode_string(&pl_conts, np->attribute, np->value);
780 struct Parameter *cont = NULL;
781 TAILQ_FOREACH(cont, &pl_conts, entries)
782 {
783 fputc(';', fp);
784
785 buf[0] = 0;
786 mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
787
788 /* Dirty hack to make messages readable by Outlook Express
789 * for the Mac: force quotes around the boundary parameter
790 * even when they aren't needed. */
791 if (mutt_istr_equal(cont->attribute, "boundary") && mutt_str_equal(buf, cont->value))
792 snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
793
794 tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
795 if ((len + tmplen + 2) > 76)
796 {
797 fputs("\n\t", fp);
798 len = tmplen + 1;
799 }
800 else
801 {
802 fputc(' ', fp);
803 len += tmplen + 1;
804 }
805
806 fprintf(fp, "%s=%s", cont->attribute, buf);
807 }
808
809 mutt_param_free(&pl_conts);
810 }
811 }
812
813 fputc('\n', fp);
814
815 if (b->content_id)
816 fprintf(fp, "Content-ID: <%s>\n", b->content_id);
817
818 if (b->language)
819 fprintf(fp, "Content-Language: %s\n", b->language);
820
821 if (b->description)
822 fprintf(fp, "Content-Description: %s\n", b->description);
823
824 if (b->disposition != DISP_NONE)
825 {
826 const char *dispstr[] = { "inline", "attachment", "form-data" };
827
828 if (b->disposition < sizeof(dispstr) / sizeof(char *))
829 {
830 fprintf(fp, "Content-Disposition: %s", dispstr[b->disposition]);
831 len = 21 + mutt_str_len(dispstr[b->disposition]);
832
833 if (b->use_disp && ((b->disposition != DISP_INLINE) || b->d_filename))
834 {
835 char *fn = b->d_filename;
836 if (!fn)
837 fn = b->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", b->disposition);
881 }
882 }
883
884 if (b->encoding != ENC_7BIT)
885 fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(b->encoding));
886
887 const bool c_crypt_protected_headers_write = cs_subset_bool(sub, "crypt_protected_headers_write");
888 bool c_autocrypt = false;
889#ifdef USE_AUTOCRYPT
890 c_autocrypt = cs_subset_bool(sub, "autocrypt");
891#endif
892
893 if ((c_crypt_protected_headers_write || c_autocrypt) && b->mime_headers)
894 {
896 false, false, sub);
897 }
898
899 /* Do NOT add the terminator here!!! */
900 return ferror(fp) ? -1 : 0;
901}
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:708
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition mime.c:67
@ ENC_7BIT
7-bit text
Definition mime.h:49
#define BODY_TYPE(body)
Get the type name of a body part.
Definition mime.h:93
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ DISP_NONE
No preferred disposition.
Definition mime.h:65
#define ENCODING(x)
Get the encoding name for an encoding type.
Definition mime.h:97
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition parameter.c:62
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
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:354
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *b, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition header.c:578
char * language
content-language (RFC8255)
Definition body.h:78
char * content_id
Content-Id (RFC2392)
Definition body.h:58
char * d_filename
filename to be used for the content-disposition header If NULL, filename is used instead.
Definition body.h:56
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
bool use_disp
Content-Disposition uses filename= ?
Definition body.h:47
char * description
content-description
Definition body.h:55
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
char * subtype
content-type subtype
Definition body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
Attribute associated with a MIME part.
Definition parameter.h:33
char * attribute
Parameter name.
Definition parameter.h:34
char * value
Parameter value.
Definition parameter.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ UserhdrsOverrideHeaders

const char* const UserhdrsOverrideHeaders[]
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 52 of file header.c.

52 {
53 "content-type:",
54 "user-agent:",
55};