NeoMutt  2025-12-11-980-ge38c27
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}
@ CH_DISPLAY
Display result to user.
Definition copy_email.h:84
+ 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;
141 int col = 0;
142 int l = 0;
143 const bool display = (chflags & CH_DISPLAY);
144
145 mutt_debug(LL_DEBUG5, "pfx=[%s], tag=[%s], flags=%d value=[%.*s]\n", pfx, tag,
146 chflags, (int) ((value[vlen - 1] == '\n') ? vlen - 1 : vlen), value);
147
148 if (tag && *tag && (fprintf(fp, "%s%s: ", NONULL(pfx), tag) < 0))
149 return -1;
150 col = mutt_str_len(tag) + ((tag && (tag[0] != '\0')) ? 2 : 0) + mutt_str_len(pfx);
151
152 while (p && (p[0] != '\0'))
153 {
154 int fold = 0;
155
156 /* find the next word and place it in 'buf'. it may start with
157 * whitespace we can fold before */
158 const char *next = mutt_str_find_word(p);
159 l = MIN(sizeof(buf) - 1, next - p);
160 memcpy(buf, p, l);
161 buf[l] = '\0';
162
163 /* determine width: character cells for display, bytes for sending
164 * (we get pure ascii only) */
165 const int w = mutt_mb_width(buf, col, display);
166 const int enc = mutt_str_startswith(buf, "=?");
167
168 mutt_debug(LL_DEBUG5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n",
169 (buf[0] == '\n' ? "\\n" : buf), col, w, *next);
170
171 /* insert a folding \n before the current word's lwsp except for
172 * header name, first word on a line (word longer than wrap width)
173 * and encoded words */
174 if (!first && !enc && col && ((col + w) >= wraplen))
175 {
176 col = mutt_str_len(pfx);
177 fold = 1;
178 if (fprintf(fp, "\n%s", NONULL(pfx)) <= 0)
179 return -1;
180 }
181
182 /* print the actual word; for display, ignore leading ws for word
183 * and fold with tab for readability */
184 if (display && fold)
185 {
186 char *pc = buf;
187 while ((pc[0] != '\0') && ((pc[0] == ' ') || (pc[0] == '\t')))
188 {
189 pc++;
190 col--;
191 }
192 if (fputc('\t', fp) == EOF)
193 return -1;
194 if (print_val(fp, pfx, pc, chflags, col) < 0)
195 return -1;
196 col += 8;
197 }
198 else if (print_val(fp, pfx, buf, chflags, col) < 0)
199 {
200 return -1;
201 }
202 col += w;
203
204 /* if the current word ends in \n, ignore all its trailing spaces
205 * and reset column; this prevents us from putting only spaces (or
206 * even none) on a line if the trailing spaces are located at our
207 * current line width
208 * XXX this covers ASCII space only, for display we probably
209 * want something like iswspace() here */
210 const char *sp = next;
211 while ((sp[0] != '\0') && ((sp[0] == ' ') || (sp[0] == '\t')))
212 sp++;
213 if (sp[0] == '\n')
214 {
215 if (sp[1] == '\0')
216 break;
217 next = sp;
218 col = 0;
219 }
220
221 p = next;
222 first = 0;
223 }
224
225 /* if we have printed something but didn't \n-terminate it, do it
226 * except the last word we printed ended in \n already */
227 if (col && ((l == 0) || (buf[l - 1] != '\n')))
228 if (putc('\n', fp) == EOF)
229 return -1;
230
231 return 0;
232}
#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:712
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:503
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 241 of file header.c.

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

279{
280 const char *ca = a;
281 const char *cb = *(const char **) b;
282 return mutt_istrn_cmp(ca, cb, strlen(cb));
283}
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 298 of file header.c.

300{
301 const char *t = strchr(start, ':');
302 if (!t || (t >= end))
303 {
304 mutt_debug(LL_DEBUG1, "#2 warning: header not in 'key: value' format!\n");
305 return 0;
306 }
307
308 const size_t vallen = end - start;
309 const bool short_enough = (pfxw + max <= wraplen);
310
311 mutt_debug((short_enough ? LL_DEBUG2 : LL_DEBUG5), "buf[%s%.*s] %s, max width = %d %s %d\n",
312 NONULL(pfx), (int) (vallen - 1) /* skip newline */, start,
313 (short_enough ? "short enough" : "too long"), max,
314 (short_enough ? "<=" : ">"), wraplen);
315
316 int rc = 0;
317 const char *valbuf = NULL;
318 const char *tagbuf = NULL;
319 const bool is_from = (vallen > 5) && mutt_istr_startswith(start, "from ");
320
321 /* only pass through folding machinery if necessary for sending,
322 * never wrap From_ headers on sending */
323 if (!(chflags & CH_DISPLAY) && (short_enough || is_from))
324 {
325 if (pfx && *pfx)
326 {
327 if (fputs(pfx, fp) == EOF)
328 {
329 return -1;
330 }
331 }
332
333 valbuf = mutt_strn_dup(start, end - start);
334 rc = print_val(fp, pfx, valbuf, chflags, mutt_str_len(pfx));
335 }
336 else
337 {
338 if (!is_from)
339 {
340 tagbuf = mutt_strn_dup(start, t - start);
341 /* skip over the colon separating the header field name and value */
342 t++;
343
344 /* skip over any leading whitespace (WSP, as defined in RFC5322)
345 * NOTE: mutt_str_skip_email_wsp() does the wrong thing here.
346 * See tickets 3609 and 3716. */
347 while ((*t == ' ') || (*t == '\t'))
348 t++;
349 }
350 const char *s = is_from ? start : t;
351 valbuf = mutt_strn_dup(s, end - s);
352 rc = fold_one_header(fp, tagbuf, valbuf, end - s, pfx, wraplen, chflags);
353 }
354
355 FREE(&tagbuf);
356 FREE(&valbuf);
357 return rc;
358}
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 368 of file header.c.

370{
371 struct UserHdrsOverride overrides = { { 0 } };
372
373 struct ListNode *tmp = NULL;
374 STAILQ_FOREACH(tmp, userhdrs, entries)
375 {
376 char *const colon = strchr(NONULL(tmp->data), ':');
377 if (!colon)
378 {
379 continue;
380 }
381
382 const char *const value = mutt_str_skip_email_wsp(colon + 1);
383 if (*value == '\0')
384 {
385 continue; /* don't emit empty fields. */
386 }
387
388 /* check whether the current user-header is an override */
389 size_t cur_override = ICONV_ILLEGAL_SEQ;
390 const char *const *idx = bsearch(tmp->data, UserhdrsOverrideHeaders,
392 sizeof(char *), userhdrs_override_cmp);
393 if (idx)
394 {
395 cur_override = idx - UserhdrsOverrideHeaders;
396 overrides.is_overridden[cur_override] = true;
397 }
398
399 if (privacy && (cur_override == USERHDRS_OVERRIDE_USER_AGENT))
400 {
401 continue;
402 }
403
404 *colon = '\0';
405 mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NONE, sub);
406 *colon = ':';
407 }
408
409 return overrides;
410}
@ CH_NONE
No flags are set.
Definition copy_email.h:65
#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:614
#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:278
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:427
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 427 of file header.c.

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

527{
528 struct ListNode *np = NULL;
529 size_t length = 0;
530
531 STAILQ_FOREACH(np, r, entries)
532 {
533 if (++length == trim)
534 break;
535 }
536
537 struct ListNode **ref = MUTT_MEM_CALLOC(length, struct ListNode *);
538
539 // store in reverse order
540 size_t tmp = length;
541 STAILQ_FOREACH(np, r, entries)
542 {
543 ref[--tmp] = np;
544 if (tmp == 0)
545 break;
546 }
547
548 for (size_t i = 0; i < length; i++)
549 {
550 fputc(' ', fp);
551 fputs(ref[i]->data, fp);
552 if (i != length - 1)
553 fputc('\n', fp);
554 }
555
556 FREE(&ref);
557}
#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 584 of file header.c.

587{
588 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
589 (mode == MUTT_WRITE_HEADER_POSTPONE)) &&
590 !privacy)
591 {
592 struct Buffer *date = buf_pool_get();
593 mutt_date_make_date(date, cs_subset_bool(sub, "local_date_header"));
594 fprintf(fp, "Date: %s\n", buf_string(date));
595 buf_pool_release(&date);
596 }
597
598 /* UseFrom is not consulted here so that we can still write a From:
599 * field if the user sets it with the 'my-header' command */
600 if (!TAILQ_EMPTY(&env->from) && !privacy)
601 {
602 mutt_addrlist_write_file(&env->from, fp, "From");
603 }
604
605 if (!TAILQ_EMPTY(&env->sender) && !privacy)
606 {
607 mutt_addrlist_write_file(&env->sender, fp, "Sender");
608 }
609
610 if (!TAILQ_EMPTY(&env->to))
611 {
612 mutt_addrlist_write_file(&env->to, fp, "To");
613 }
614 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
615 {
616 if (!OptNewsSend)
617 fputs("To:\n", fp);
618 }
619
620 if (!TAILQ_EMPTY(&env->cc))
621 {
622 mutt_addrlist_write_file(&env->cc, fp, "Cc");
623 }
624 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
625 {
626 if (!OptNewsSend)
627 fputs("Cc:\n", fp);
628 }
629
630 if (!TAILQ_EMPTY(&env->bcc))
631 {
632 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
633
634 if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
635 (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
636 ((mode == MUTT_WRITE_HEADER_NORMAL) && c_write_bcc))
637 {
638 mutt_addrlist_write_file(&env->bcc, fp, "Bcc");
639 }
640 }
641 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
642 {
643 if (!OptNewsSend)
644 fputs("Bcc:\n", fp);
645 }
646
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
663 if (env->subject)
664 {
665 if (hide_protected_subject &&
666 ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
668 {
669 const char *const c_crypt_protected_headers_subject = cs_subset_string(sub, "crypt_protected_headers_subject");
670 mutt_write_one_header(fp, "Subject", c_crypt_protected_headers_subject,
671 NULL, 0, CH_NONE, sub);
672 }
673 else
674 {
675 mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NONE, sub);
676 }
677 }
678 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
679 {
680 fputs("Subject:\n", fp);
681 }
682
683 /* save message id if the user has set it */
684 if (env->message_id && !privacy)
685 fprintf(fp, "Message-ID: %s\n", env->message_id);
686
687 if (!TAILQ_EMPTY(&env->reply_to))
688 {
689 mutt_addrlist_write_file(&env->reply_to, fp, "Reply-To");
690 }
691 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
692 {
693 fputs("Reply-To:\n", fp);
694 }
695
696 if (!TAILQ_EMPTY(&env->mail_followup_to))
697 {
698 if (!OptNewsSend)
699 {
700 mutt_addrlist_write_file(&env->mail_followup_to, fp, "Mail-Followup-To");
701 }
702 }
703
704 /* Add any user defined headers */
705 struct UserHdrsOverride userhdrs_overrides = write_userhdrs(fp, &env->userhdrs,
706 privacy, sub);
707
708 if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
710 {
711 if (!STAILQ_EMPTY(&env->references))
712 {
713 fputs("References:", fp);
714 mutt_write_references(&env->references, fp, 10);
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(b, fp, sub);
723 }
724 }
725
726 if (!STAILQ_EMPTY(&env->in_reply_to))
727 {
728 fputs("In-Reply-To:", fp);
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 if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
739 if (mode == MUTT_WRITE_HEADER_MIME)
741 }
742#endif
743
744 const bool c_user_agent = cs_subset_bool(sub, "user_agent");
745 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
746 c_user_agent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
747 {
748 /* Add a vanity header */
749 fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
750 }
751
752 return (ferror(fp) == 0) ? 0 : -1;
753}
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
Wrapper for mutt_write_address()
Definition address.c:1261
int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition autocrypt.c:822
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition autocrypt.c:784
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:400
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:368
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition header.c:763
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition header.c:526
@ 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 763 of file header.c.

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