NeoMutt  2022-04-29-247-gc6aae8
Teaching an old dog new tricks
DOXYGEN
header.c File Reference

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

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

Go to the source code of this file.

Data Structures

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

Enumerations

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

Functions

static int print_val (FILE *fp, const char *pfx, const char *value, CopyHeaderFlags chflags, size_t col)
 Add pieces to an email header, wrapping where necessary. More...
 
static int fold_one_header (FILE *fp, const char *tag, const char *value, size_t vlen, const char *pfx, int wraplen, CopyHeaderFlags chflags)
 Fold one header line. More...
 
static char * unfold_header (char *s)
 Unfold a wrapped email header. More...
 
static int userhdrs_override_cmp (const void *a, const void *b)
 Compare a user-defined header with an element of the userhdrs_override_headers list. More...
 
static int write_one_header (FILE *fp, int pfxw, int max, int wraplen, const char *pfx, const char *start, const char *end, CopyHeaderFlags chflags)
 Write out one header line. More...
 
static struct UserHdrsOverride write_userhdrs (FILE *fp, const struct ListHead *userhdrs, bool privacy, struct ConfigSubset *sub)
 Write user-defined headers and keep track of the interesting ones. More...
 
int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, CopyHeaderFlags chflags, struct ConfigSubset *sub)
 Write one header line to a file. More...
 
void mutt_write_references (const struct ListHead *r, FILE *fp, size_t trim)
 Add the message references to a list. More...
 
int mutt_rfc822_write_header (FILE *fp, struct Envelope *env, struct Body *attach, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
 Write out one RFC822 header line. More...
 
int mutt_write_mime_header (struct Body *a, FILE *fp, struct ConfigSubset *sub)
 Create a MIME header. More...
 

Variables

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

Detailed Description

Write a MIME Email Header to a file.

Authors
  • Richard Russon

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

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

Definition in file header.c.

Enumeration Type Documentation

◆ UserHdrsOverrideIdx

Headers that the user may override.

Enumerator
USERHDRS_OVERRIDE_CONTENT_TYPE 

Override the "Content-Type".

USERHDRS_OVERRIDE_USER_AGENT 

Override the "User-Agent".

Definition at line 58 of file header.c.

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

Function Documentation

◆ print_val()

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

Add pieces to an email header, wrapping where necessary.

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

Definition at line 83 of file header.c.

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

◆ fold_one_header()

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

Fold one header line.

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

Definition at line 130 of file header.c.

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

236{
237 char *p = s;
238 char *q = s;
239
240 while (p && (p[0] != '\0'))
241 {
242 /* remove CRLF prior to FWSP, turn \t into ' ' */
243 if ((p[0] == '\r') && (p[1] == '\n') && ((p[2] == ' ') || (p[2] == '\t')))
244 {
245 *q++ = ' ';
246 p += 3;
247 continue;
248 }
249 /* remove LF prior to FWSP, turn \t into ' ' */
250 else if ((p[0] == '\n') && ((p[1] == ' ') || (p[1] == '\t')))
251 {
252 *q++ = ' ';
253 p += 2;
254 continue;
255 }
256 *q++ = *p++;
257 }
258 if (q)
259 q[0] = '\0';
260
261 return s;
262}
+ Here is the caller graph for this function:

◆ userhdrs_override_cmp()

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

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

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

Definition at line 272 of file header.c.

273{
274 const char *ca = a;
275 const char *cb = *(const char **) b;
276 return mutt_istrn_cmp(ca, cb, strlen(cb));
277}
int mutt_istrn_cmp(const char *a, const char *b, size_t num)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:510
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ write_one_header()

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

Write out one header line.

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

Definition at line 292 of file header.c.

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

◆ write_userhdrs()

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

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

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

Definition at line 361 of file header.c.

363{
364 struct UserHdrsOverride overrides = { { 0 } };
365
366 struct ListNode *tmp = NULL;
367 STAILQ_FOREACH(tmp, userhdrs, entries)
368 {
369 char *const colon = strchr(tmp->data, ':');
370 if (!colon)
371 {
372 continue;
373 }
374
375 const char *const value = mutt_str_skip_email_wsp(colon + 1);
376 if (*value == '\0')
377 {
378 continue; /* don't emit empty fields. */
379 }
380
381 /* check whether the current user-header is an override */
382 size_t cur_override = (size_t) -1;
383 const char *const *idx = bsearch(tmp->data, userhdrs_override_headers,
385 sizeof(char *), userhdrs_override_cmp);
386 if (idx != NULL)
387 {
388 cur_override = idx - userhdrs_override_headers;
389 overrides.is_overridden[cur_override] = true;
390 }
391
392 if (privacy && (cur_override == USERHDRS_OVERRIDE_USER_AGENT))
393 {
394 continue;
395 }
396
397 *colon = '\0';
398 mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS, sub);
399 *colon = ':';
400 }
401
402 return overrides;
403}
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:51
static int userhdrs_override_cmp(const void *a, const void *b)
Compare a user-defined header with an element of the userhdrs_override_headers list.
Definition: header.c:272
static const char *const userhdrs_override_headers[]
The next array/enum pair is used to to keep track of user headers that override pre-defined headers N...
Definition: header.c:50
int mutt_write_one_header(FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, CopyHeaderFlags chflags, struct ConfigSubset *sub)
Write one header line to a file.
Definition: header.c:420
#define mutt_array_size(x)
Definition: memory.h:36
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
#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:68
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Which email headers have been overridden.
Definition: header.c:70
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_one_header()

int mutt_write_one_header ( FILE *  fp,
const char *  tag,
const char *  value,
const char *  pfx,
int  wraplen,
CopyHeaderFlags  chflags,
struct ConfigSubset sub 
)

Write one header line to a file.

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

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

Definition at line 420 of file header.c.

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

515{
516 struct ListNode *np = NULL;
517 size_t length = 0;
518
519 STAILQ_FOREACH(np, r, entries)
520 {
521 if (++length == trim)
522 break;
523 }
524
525 struct ListNode **ref = mutt_mem_calloc(length, sizeof(struct ListNode *));
526
527 // store in reverse order
528 size_t tmp = length;
529 STAILQ_FOREACH(np, r, entries)
530 {
531 ref[--tmp] = np;
532 if (tmp == 0)
533 break;
534 }
535
536 for (size_t i = 0; i < length; i++)
537 {
538 fputc(' ', fp);
539 fputs(ref[i]->data, fp);
540 if (i != length - 1)
541 fputc('\n', fp);
542 }
543
544 FREE(&ref);
545}
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 attach,
enum MuttWriteHeaderMode  mode,
bool  privacy,
bool  hide_protected_subject,
struct ConfigSubset sub 
)

Write out one RFC822 header line.

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

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

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

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

Definition at line 572 of file header.c.

575{
576 char buf[1024] = { 0 };
577
578 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
579 (mode == MUTT_WRITE_HEADER_POSTPONE)) &&
580 !privacy)
581 {
582 struct Buffer *date = mutt_buffer_pool_get();
583 mutt_date_make_date(date, cs_subset_bool(sub, "local_date_header"));
584 fprintf(fp, "Date: %s\n", mutt_buffer_string(date));
586 }
587
588 /* UseFrom is not consulted here so that we can still write a From:
589 * field if the user sets it with the 'my_hdr' command */
590 if (!TAILQ_EMPTY(&env->from) && !privacy)
591 {
592 buf[0] = '\0';
593 mutt_addrlist_write(&env->from, buf, sizeof(buf), false);
594 fprintf(fp, "From: %s\n", buf);
595 }
596
597 if (!TAILQ_EMPTY(&env->sender) && !privacy)
598 {
599 buf[0] = '\0';
600 mutt_addrlist_write(&env->sender, buf, sizeof(buf), false);
601 fprintf(fp, "Sender: %s\n", buf);
602 }
603
604 if (!TAILQ_EMPTY(&env->to))
605 {
606 fputs("To: ", fp);
607 mutt_addrlist_write_file(&env->to, fp, 4, false);
608 }
609 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
610#ifdef USE_NNTP
611 if (!OptNewsSend)
612#endif
613 fputs("To:\n", fp);
614
615 if (!TAILQ_EMPTY(&env->cc))
616 {
617 fputs("Cc: ", fp);
618 mutt_addrlist_write_file(&env->cc, fp, 4, false);
619 }
620 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
621#ifdef USE_NNTP
622 if (!OptNewsSend)
623#endif
624 fputs("Cc:\n", fp);
625
626 if (!TAILQ_EMPTY(&env->bcc))
627 {
628 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
629
630 if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
631 (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
632 ((mode == MUTT_WRITE_HEADER_NORMAL) && c_write_bcc))
633 {
634 fputs("Bcc: ", fp);
635 mutt_addrlist_write_file(&env->bcc, fp, 5, false);
636 }
637 }
638 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
639#ifdef USE_NNTP
640 if (!OptNewsSend)
641#endif
642 fputs("Bcc:\n", fp);
643
644#ifdef USE_NNTP
645 if (env->newsgroups)
646 fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
647 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
648 fputs("Newsgroups:\n", fp);
649
650 if (env->followup_to)
651 fprintf(fp, "Followup-To: %s\n", env->followup_to);
652 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
653 fputs("Followup-To:\n", fp);
654
655 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
656 if (env->x_comment_to)
657 fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
658 else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && c_x_comment_to)
659 fputs("X-Comment-To:\n", fp);
660#endif
661
662 if (env->subject)
663 {
664 if (hide_protected_subject &&
665 ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
667 {
668 const char *const c_crypt_protected_headers_subject = cs_subset_string(sub, "crypt_protected_headers_subject");
669 mutt_write_one_header(fp, "Subject", c_crypt_protected_headers_subject,
670 NULL, 0, CH_NO_FLAGS, sub);
671 }
672 else
673 mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NO_FLAGS, sub);
674 }
675 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
676 fputs("Subject:\n", fp);
677
678 /* save message id if the user has set it */
679 if (env->message_id && !privacy)
680 fprintf(fp, "Message-ID: %s\n", env->message_id);
681
682 if (!TAILQ_EMPTY(&env->reply_to))
683 {
684 fputs("Reply-To: ", fp);
685 mutt_addrlist_write_file(&env->reply_to, fp, 10, false);
686 }
687 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
688 fputs("Reply-To:\n", fp);
689
690 if (!TAILQ_EMPTY(&env->mail_followup_to))
691 {
692#ifdef USE_NNTP
693 if (!OptNewsSend)
694#endif
695 {
696 fputs("Mail-Followup-To: ", fp);
697 mutt_addrlist_write_file(&env->mail_followup_to, fp, 18, false);
698 }
699 }
700
701 /* Add any user defined headers */
702 struct UserHdrsOverride userhdrs_overrides = write_userhdrs(fp, &env->userhdrs,
703 privacy, sub);
704
705 if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
707 {
708 if (!STAILQ_EMPTY(&env->references))
709 {
710 fputs("References:", fp);
711 mutt_write_references(&env->references, fp, 10);
712 fputc('\n', fp);
713 }
714
715 /* Add the MIME headers */
716 if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
717 {
718 fputs("MIME-Version: 1.0\n", fp);
719 mutt_write_mime_header(attach, fp, sub);
720 }
721 }
722
723 if (!STAILQ_EMPTY(&env->in_reply_to))
724 {
725 fputs("In-Reply-To:", fp);
727 fputc('\n', fp);
728 }
729
730#ifdef USE_AUTOCRYPT
731 const bool c_autocrypt = cs_subset_bool(sub, "autocrypt");
732 if (c_autocrypt)
733 {
734 if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
736 if (mode == MUTT_WRITE_HEADER_MIME)
738 }
739#endif
740
741 const bool c_user_agent = cs_subset_bool(sub, "user_agent");
742 if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
743 c_user_agent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
744 {
745 /* Add a vanity header */
746 fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
747 }
748
749 return (ferror(fp) == 0) ? 0 : -1;
750}
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1150
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, int start_col, bool display)
Wrapper for mutt_write_address()
Definition: address.c:1231
int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition: autocrypt.c:806
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition: autocrypt.c:768
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:380
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:760
static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs, bool privacy, struct ConfigSubset *sub)
Write user-defined headers and keep track of the interesting ones.
Definition: header.c:361
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition: header.c:514
@ 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
const char * GitVer
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:51
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define STAILQ_EMPTY(head)
Definition: queue.h:348
#define TAILQ_EMPTY(head)
Definition: queue.h:721
String manipulation buffer.
Definition: buffer.h:34
struct ListHead userhdrs
user defined headers
Definition: envelope.h:87
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition: envelope.h:81
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:82
char * newsgroups
List of newsgroups.
Definition: envelope.h:79
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:85
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:86
char * subject
Email's subject.
Definition: envelope.h:70
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 a,
FILE *  fp,
struct ConfigSubset sub 
)

Create a MIME header.

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

Definition at line 760 of file header.c.

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

◆ userhdrs_override_headers

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

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

Keep the array sorted and in sync with the enum.

Definition at line 50 of file header.c.