NeoMutt  2024-03-23-147-g885fbc
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

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};
@ USERHDRS_OVERRIDE_CONTENT_TYPE
Override the "Content-Type".
Definition: header.c:61
@ USERHDRS_OVERRIDE_USER_AGENT
Override the "User-Agent".
Definition: header.c:62

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

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

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

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

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

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

366{
367 struct UserHdrsOverride overrides = { { 0 } };
368
369 struct ListNode *tmp = NULL;
370 STAILQ_FOREACH(tmp, userhdrs, entries)
371 {
372 char *const colon = strchr(NONULL(tmp->data), ':');
373 if (!colon)
374 {
375 continue;
376 }
377
378 const char *const value = mutt_str_skip_email_wsp(colon + 1);
379 if (*value == '\0')
380 {
381 continue; /* don't emit empty fields. */
382 }
383
384 /* check whether the current user-header is an override */
385 size_t cur_override = ICONV_ILLEGAL_SEQ;
386 const char *const *idx = bsearch(tmp->data, UserhdrsOverrideHeaders,
388 sizeof(char *), userhdrs_override_cmp);
389 if (idx)
390 {
391 cur_override = idx - UserhdrsOverrideHeaders;
392 overrides.is_overridden[cur_override] = true;
393 }
394
395 if (privacy && (cur_override == USERHDRS_OVERRIDE_USER_AGENT))
396 {
397 continue;
398 }
399
400 *colon = '\0';
401 mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS, sub);
402 *colon = ':';
403 }
404
405 return overrides;
406}
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:53
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:51
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:275
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:423
#define mutt_array_size(x)
Definition: memory.h:38
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition: charset.h:104
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:657
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
Which headers have been overridden.
Definition: header.c:69
bool is_overridden[mutt_array_size(UserhdrsOverrideHeaders)]
Which email headers have been overridden.
Definition: header.c:71
+ 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 423 of file header.c.

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

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

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

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