NeoMutt  2023-03-22-27-g3cb248
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 "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. 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 57 of file header.c.

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

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

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

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

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

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

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

362{
363 struct UserHdrsOverride overrides = { { 0 } };
364
365 struct ListNode *tmp = NULL;
366 STAILQ_FOREACH(tmp, userhdrs, entries)
367 {
368 char *const colon = strchr(tmp->data, ':');
369 if (!colon)
370 {
371 continue;
372 }
373
374 const char *const value = mutt_str_skip_email_wsp(colon + 1);
375 if (*value == '\0')
376 {
377 continue; /* don't emit empty fields. */
378 }
379
380 /* check whether the current user-header is an override */
381 size_t cur_override = (size_t) -1;
382 const char *const *idx = bsearch(tmp->data, userhdrs_override_headers,
384 sizeof(char *), userhdrs_override_cmp);
385 if (idx != NULL)
386 {
387 cur_override = idx - userhdrs_override_headers;
388 overrides.is_overridden[cur_override] = true;
389 }
390
391 if (privacy && (cur_override == USERHDRS_OVERRIDE_USER_AGENT))
392 {
393 continue;
394 }
395
396 *colon = '\0';
397 mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS, sub);
398 *colon = ':';
399 }
400
401 return overrides;
402}
#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:271
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:49
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:419
#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:67
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Which email headers have been overridden.
Definition: header.c:69
+ 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 419 of file header.c.

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

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

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

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