NeoMutt  2021-10-22-8-g9cb437
Teaching an old dog new tricks
DOXYGEN
sendlib.c File Reference

Miscellaneous functions for sending an email. More...

#include "config.h"
#include <errno.h>
#include <iconv.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "sendlib.h"
#include "lib.h"
#include "attach/lib.h"
#include "ncrypt/lib.h"
#include "copy.h"
#include "handler.h"
#include "mutt_globals.h"
#include "mutt_mailbox.h"
#include "muttlib.h"
#include "mx.h"
#include "options.h"
+ Include dependency graph for sendlib.c:

Go to the source code of this file.

Data Structures

struct  ContentState
 Info about the body of an email. More...
 

Functions

static void update_content_info (struct Content *info, struct ContentState *s, char *buf, size_t buflen)
 Cache some info about an email. More...
 
static size_t convert_file_to (FILE *fp, const char *fromcode, int ncodes, char const *const *tocodes, int *tocode, struct Content *info)
 Change the encoding of a file. More...
 
static size_t convert_file_from_to (FILE *fp, const char *fromcodes, const char *tocodes, char **fromcode, char **tocode, struct Content *info)
 Convert a file between encodings. More...
 
struct Contentmutt_get_content_info (const char *fname, struct Body *b, struct ConfigSubset *sub)
 Analyze file to determine MIME encoding to use. More...
 
enum ContentType mutt_lookup_mime_type (struct Body *att, const char *path)
 Find the MIME type for an attachment. More...
 
static void transform_to_7bit (struct Body *a, FILE *fp_in, struct ConfigSubset *sub)
 Convert MIME parts to 7-bit. More...
 
void mutt_message_to_7bit (struct Body *a, FILE *fp, struct ConfigSubset *sub)
 Convert an email's MIME parts to 7-bit. More...
 
static void set_encoding (struct Body *b, struct Content *info, struct ConfigSubset *sub)
 Determine which Content-Transfer-Encoding to use. More...
 
void mutt_stamp_attachment (struct Body *a)
 Timestamp an Attachment. More...
 
void mutt_update_encoding (struct Body *a, struct ConfigSubset *sub)
 Update the encoding type. More...
 
struct Bodymutt_make_message_attach (struct Mailbox *m, struct Email *e, bool attach_msg, struct ConfigSubset *sub)
 Create a message attachment. More...
 
static void run_mime_type_query (struct Body *att, struct ConfigSubset *sub)
 Run an external command to determine the MIME type. More...
 
struct Bodymutt_make_file_attach (const char *path, struct ConfigSubset *sub)
 Create a file attachment. More...
 
static void encode_headers (struct ListHead *h, struct ConfigSubset *sub)
 RFC2047-encode a list of headers. More...
 
const char * mutt_fqdn (bool may_hide_host, const struct ConfigSubset *sub)
 Get the Fully-Qualified Domain Name. More...
 
static char * gen_msgid (struct ConfigSubset *sub)
 Generate a unique Message ID. More...
 
void mutt_prepare_envelope (struct Envelope *env, bool final, struct ConfigSubset *sub)
 Prepare an email header. More...
 
void mutt_unprepare_envelope (struct Envelope *env)
 Undo the encodings of mutt_prepare_envelope() More...
 
static int bounce_message (FILE *fp, struct Mailbox *m, struct Email *e, struct AddressList *to, const char *resent_from, struct AddressList *env_from, struct ConfigSubset *sub)
 Bounce an email message. More...
 
int mutt_bounce_message (FILE *fp, struct Mailbox *m, struct Email *e, struct AddressList *to, struct ConfigSubset *sub)
 Bounce an email message. More...
 
static void set_noconv_flags (struct Body *b, bool flag)
 Set/reset the "x-mutt-noconv" flag. More...
 
int mutt_write_multiple_fcc (const char *path, struct Email *e, const char *msgid, bool post, char *fcc, char **finalpath, struct ConfigSubset *sub)
 Handle FCC with multiple, comma separated entries. More...
 
int mutt_write_fcc (const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath, struct ConfigSubset *sub)
 Write email to FCC mailbox. More...
 

Detailed Description

Miscellaneous functions for sending an email.

Authors
  • Michael R. Elkins
  • Pietro Cerutti
  • R Primus

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 sendlib.c.

Function Documentation

◆ update_content_info()

static void update_content_info ( struct Content info,
struct ContentState s,
char *  buf,
size_t  buflen 
)
static

Cache some info about an email.

Parameters
infoInfo about an Attachment
sInfo about the Body of an email
bufBuffer for the result
buflenLength of the buffer

Definition at line 79 of file sendlib.c.

81 {
82  bool from = s->from;
83  int whitespace = s->whitespace;
84  bool dot = s->dot;
85  int linelen = s->linelen;
86  bool was_cr = s->was_cr;
87 
88  if (!buf) /* This signals EOF */
89  {
90  if (was_cr)
91  info->binary = true;
92  if (linelen > info->linemax)
93  info->linemax = linelen;
94 
95  return;
96  }
97 
98  for (; buflen; buf++, buflen--)
99  {
100  char ch = *buf;
101 
102  if (was_cr)
103  {
104  was_cr = false;
105  if (ch == '\n')
106  {
107  if (whitespace)
108  info->space = true;
109  if (dot)
110  info->dot = true;
111  if (linelen > info->linemax)
112  info->linemax = linelen;
113  whitespace = 0;
114  dot = false;
115  linelen = 0;
116  continue;
117  }
118 
119  info->binary = true;
120  }
121 
122  linelen++;
123  if (ch == '\n')
124  {
125  info->crlf++;
126  if (whitespace)
127  info->space = true;
128  if (dot)
129  info->dot = true;
130  if (linelen > info->linemax)
131  info->linemax = linelen;
132  whitespace = 0;
133  linelen = 0;
134  dot = false;
135  }
136  else if (ch == '\r')
137  {
138  info->crlf++;
139  info->cr = true;
140  was_cr = true;
141  continue;
142  }
143  else if (ch & 0x80)
144  info->hibin++;
145  else if ((ch == '\t') || (ch == '\f'))
146  {
147  info->ascii++;
148  whitespace++;
149  }
150  else if (ch == 0)
151  {
152  info->nulbin++;
153  info->lobin++;
154  }
155  else if ((ch < 32) || (ch == 127))
156  info->lobin++;
157  else
158  {
159  if (linelen == 1)
160  {
161  if ((ch == 'F') || (ch == 'f'))
162  from = true;
163  else
164  from = false;
165  if (ch == '.')
166  dot = true;
167  else
168  dot = false;
169  }
170  else if (from)
171  {
172  if ((linelen == 2) && (ch != 'r'))
173  from = false;
174  else if ((linelen == 3) && (ch != 'o'))
175  from = false;
176  else if (linelen == 4)
177  {
178  if (ch == 'm')
179  info->from = true;
180  from = false;
181  }
182  }
183  if (ch == ' ')
184  whitespace++;
185  info->ascii++;
186  }
187 
188  if (linelen > 1)
189  dot = false;
190  if ((ch != ' ') && (ch != '\t'))
191  whitespace = 0;
192  }
193 
194  s->from = from;
195  s->whitespace = whitespace;
196  s->dot = dot;
197  s->linelen = linelen;
198  s->was_cr = was_cr;
199 }
bool was_cr
Definition: sendlib.c:69
int whitespace
Definition: sendlib.c:66
bool from
Definition: sendlib.c:65
int linelen
Definition: sendlib.c:68
bool dot
Definition: sendlib.c:67
long crlf
\r and \n characters
Definition: content.h:38
long hibin
8-bit characters
Definition: content.h:35
bool cr
Has CR, even when in a CRLF pair.
Definition: content.h:45
bool space
Whitespace at the end of lines?
Definition: content.h:41
long ascii
Number of ascii chars.
Definition: content.h:39
bool binary
Long lines, or CR not in CRLF pair.
Definition: content.h:42
bool from
Has a line beginning with "From "?
Definition: content.h:43
long nulbin
Null characters (0x0)
Definition: content.h:37
long linemax
Length of the longest line in the file.
Definition: content.h:40
long lobin
Unprintable 7-bit chars (eg., control chars)
Definition: content.h:36
bool dot
Has a line consisting of a single dot?
Definition: content.h:44
+ Here is the caller graph for this function:

◆ convert_file_to()

static size_t convert_file_to ( FILE *  fp,
const char *  fromcode,
int  ncodes,
char const *const *  tocodes,
int *  tocode,
struct Content info 
)
static

Change the encoding of a file.

Parameters
[in]fpFile to convert
[in]fromcodeOriginal encoding
[in]ncodesNumber of target encodings
[in]tocodesList of target encodings
[out]tocodeChosen encoding
[in]infoEncoding information
Return values
-1Error, no conversion was possible
>0Success, number of bytes converted

Find the best charset conversion of the file from fromcode into one of the tocodes. If successful, set *tocode and Content *info and return the number of characters converted inexactly.

We convert via UTF-8 in order to avoid the condition -1(EINVAL), which would otherwise prevent us from knowing the number of inexact conversions. Where the candidate target charset is UTF-8 we avoid doing the second conversion because iconv_open("UTF-8", "UTF-8") fails with some libraries.

We assume that the output from iconv is never more than 4 times as long as the input for any pair of charsets we might be interested in.

Definition at line 226 of file sendlib.c.

228 {
229  char bufi[256], bufu[512], bufo[4 * sizeof(bufi)];
230  size_t ret;
231 
232  const iconv_t cd1 = mutt_ch_iconv_open("utf-8", fromcode, MUTT_ICONV_NO_FLAGS);
233  if (cd1 == (iconv_t) (-1))
234  return -1;
235 
236  iconv_t *cd = mutt_mem_calloc(ncodes, sizeof(iconv_t));
237  size_t *score = mutt_mem_calloc(ncodes, sizeof(size_t));
238  struct ContentState *states = mutt_mem_calloc(ncodes, sizeof(struct ContentState));
239  struct Content *infos = mutt_mem_calloc(ncodes, sizeof(struct Content));
240 
241  for (int i = 0; i < ncodes; i++)
242  {
243  if (!mutt_istr_equal(tocodes[i], "utf-8"))
244  cd[i] = mutt_ch_iconv_open(tocodes[i], "utf-8", MUTT_ICONV_NO_FLAGS);
245  else
246  {
247  /* Special case for conversion to UTF-8 */
248  cd[i] = (iconv_t) (-1);
249  score[i] = (size_t) (-1);
250  }
251  }
252 
253  rewind(fp);
254  size_t ibl = 0;
255  while (true)
256  {
257  /* Try to fill input buffer */
258  size_t n = fread(bufi + ibl, 1, sizeof(bufi) - ibl, fp);
259  ibl += n;
260 
261  /* Convert to UTF-8 */
262  const char *ib = bufi;
263  char *ob = bufu;
264  size_t obl = sizeof(bufu);
265  n = iconv(cd1, (ICONV_CONST char **) ((ibl != 0) ? &ib : 0), &ibl, &ob, &obl);
266  /* assert(n == (size_t)(-1) || !n); */
267  if ((n == (size_t) (-1)) && (((errno != EINVAL) && (errno != E2BIG)) || (ib == bufi)))
268  {
269  /* assert(errno == EILSEQ || (errno == EINVAL && ib == bufi && ibl < sizeof(bufi))); */
270  ret = (size_t) (-1);
271  break;
272  }
273  const size_t ubl1 = ob - bufu;
274 
275  /* Convert from UTF-8 */
276  for (int i = 0; i < ncodes; i++)
277  {
278  if ((cd[i] != (iconv_t) (-1)) && (score[i] != (size_t) (-1)))
279  {
280  const char *ub = bufu;
281  size_t ubl = ubl1;
282  ob = bufo;
283  obl = sizeof(bufo);
284  n = iconv(cd[i], (ICONV_CONST char **) ((ibl || ubl) ? &ub : 0), &ubl, &ob, &obl);
285  if (n == (size_t) (-1))
286  {
287  /* assert(errno == E2BIG || (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT))); */
288  score[i] = (size_t) (-1);
289  }
290  else
291  {
292  score[i] += n;
293  update_content_info(&infos[i], &states[i], bufo, ob - bufo);
294  }
295  }
296  else if ((cd[i] == (iconv_t) (-1)) && (score[i] == (size_t) (-1)))
297  {
298  /* Special case for conversion to UTF-8 */
299  update_content_info(&infos[i], &states[i], bufu, ubl1);
300  }
301  }
302 
303  if (ibl)
304  {
305  /* Save unused input */
306  memmove(bufi, ib, ibl);
307  }
308  else if (!ubl1 && (ib < bufi + sizeof(bufi)))
309  {
310  ret = 0;
311  break;
312  }
313  }
314 
315  if (ret == 0)
316  {
317  /* Find best score */
318  ret = (size_t) (-1);
319  for (int i = 0; i < ncodes; i++)
320  {
321  if ((cd[i] == (iconv_t) (-1)) && (score[i] == (size_t) (-1)))
322  {
323  /* Special case for conversion to UTF-8 */
324  *tocode = i;
325  ret = 0;
326  break;
327  }
328  else if ((cd[i] == (iconv_t) (-1)) || (score[i] == (size_t) (-1)))
329  continue;
330  else if ((ret == (size_t) (-1)) || (score[i] < ret))
331  {
332  *tocode = i;
333  ret = score[i];
334  if (ret == 0)
335  break;
336  }
337  }
338  if (ret != (size_t) (-1))
339  {
340  memcpy(info, &infos[*tocode], sizeof(struct Content));
341  update_content_info(info, &states[*tocode], 0, 0); /* EOF */
342  }
343  }
344 
345  for (int i = 0; i < ncodes; i++)
346  if (cd[i] != (iconv_t) (-1))
347  iconv_close(cd[i]);
348 
349  iconv_close(cd1);
350  FREE(&cd);
351  FREE(&infos);
352  FREE(&score);
353  FREE(&states);
354 
355  return ret;
356 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:40
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:576
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
static void update_content_info(struct Content *info, struct ContentState *s, char *buf, size_t buflen)
Cache some info about an email.
Definition: sendlib.c:79
Info about the body of an email.
Definition: sendlib.c:64
Info about an attachment.
Definition: content.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ convert_file_from_to()

static size_t convert_file_from_to ( FILE *  fp,
const char *  fromcodes,
const char *  tocodes,
char **  fromcode,
char **  tocode,
struct Content info 
)
static

Convert a file between encodings.

Parameters
[in]fpFile to read from
[in]fromcodesCharsets to try converting FROM
[in]tocodesCharsets to try converting TO
[out]fromcodeFrom charset selected
[out]tocodeTo charset selected
[out]infoInfo about the file
Return values
numCharacters converted
-1Error (as a size_t)

Find the first of the fromcodes that gives a valid conversion and the best charset conversion of the file into one of the tocodes. If successful, set *fromcode and *tocode to dynamically allocated strings, set Content *info, and return the number of characters converted inexactly. If no conversion was possible, return -1.

Both fromcodes and tocodes may be colon-separated lists of charsets. However, if fromcode is zero then fromcodes is assumed to be the name of a single charset even if it contains a colon.

Definition at line 379 of file sendlib.c.

381 {
382  char *fcode = NULL;
383  char **tcode = NULL;
384  const char *c = NULL, *c1 = NULL;
385  size_t ret;
386  int ncodes, i, cn;
387 
388  /* Count the tocodes */
389  ncodes = 0;
390  for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
391  {
392  c1 = strchr(c, ':');
393  if (c1 == c)
394  continue;
395  ncodes++;
396  }
397 
398  /* Copy them */
399  tcode = mutt_mem_malloc(ncodes * sizeof(char *));
400  for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
401  {
402  c1 = strchr(c, ':');
403  if (c1 == c)
404  continue;
405  if (c1)
406  tcode[i] = mutt_strn_dup(c, c1 - c);
407  else
408  tcode[i] = mutt_str_dup(c);
409  }
410 
411  ret = (size_t) (-1);
412  if (fromcode)
413  {
414  /* Try each fromcode in turn */
415  for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
416  {
417  c1 = strchr(c, ':');
418  if (c1 == c)
419  continue;
420  if (c1)
421  fcode = mutt_strn_dup(c, c1 - c);
422  else
423  fcode = mutt_str_dup(c);
424 
425  ret = convert_file_to(fp, fcode, ncodes, (char const *const *) tcode, &cn, info);
426  if (ret != (size_t) (-1))
427  {
428  *fromcode = fcode;
429  *tocode = tcode[cn];
430  tcode[cn] = 0;
431  break;
432  }
433  FREE(&fcode);
434  }
435  }
436  else
437  {
438  /* There is only one fromcode */
439  ret = convert_file_to(fp, fromcodes, ncodes, (char const *const *) tcode, &cn, info);
440  if (ret != (size_t) (-1))
441  {
442  *tocode = tcode[cn];
443  tcode[cn] = 0;
444  }
445  }
446 
447  /* Free memory */
448  for (i = 0; i < ncodes; i++)
449  FREE(&tcode[i]);
450 
451  FREE(&tcode);
452 
453  return ret;
454 }
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:548
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
static size_t convert_file_to(FILE *fp, const char *fromcode, int ncodes, char const *const *tocodes, int *tocode, struct Content *info)
Change the encoding of a file.
Definition: sendlib.c:226
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_get_content_info()

struct Content* mutt_get_content_info ( const char *  fname,
struct Body b,
struct ConfigSubset sub 
)

Analyze file to determine MIME encoding to use.

Parameters
fnameFile to examine
bBody to update
subConfig Subset
Return values
ptrNewly allocated Content

Also set the body charset, sometimes, or not.

Definition at line 465 of file sendlib.c.

467 {
468  struct Content *info = NULL;
469  struct ContentState state = { 0 };
470  FILE *fp = NULL;
471  char *fromcode = NULL;
472  char *tocode = NULL;
473  char buf[100];
474  size_t r;
475 
476  struct stat st = { 0 };
477 
478  if (b && !fname)
479  fname = b->filename;
480  if (!fname)
481  return NULL;
482 
483  if (stat(fname, &st) == -1)
484  {
485  mutt_error(_("Can't stat %s: %s"), fname, strerror(errno));
486  return NULL;
487  }
488 
489  if (!S_ISREG(st.st_mode))
490  {
491  mutt_error(_("%s isn't a regular file"), fname);
492  return NULL;
493  }
494 
495  fp = fopen(fname, "r");
496  if (!fp)
497  {
498  mutt_debug(LL_DEBUG1, "%s: %s (errno %d)\n", fname, strerror(errno), errno);
499  return NULL;
500  }
501 
502  info = mutt_mem_calloc(1, sizeof(struct Content));
503 
504  const char *const c_charset = cs_subset_string(sub, "charset");
505 
506  if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
507  {
508  const char *const c_attach_charset =
509  cs_subset_string(sub, "attach_charset");
510  const char *const c_send_charset = cs_subset_string(sub, "send_charset");
511 
512  char *chs = mutt_param_get(&b->parameter, "charset");
513  const char *fchs =
514  b->use_disp ? (c_attach_charset ? c_attach_charset : c_charset) : c_charset;
515  if (c_charset && (chs || c_send_charset) &&
516  (convert_file_from_to(fp, fchs, chs ? chs : c_send_charset, &fromcode,
517  &tocode, info) != (size_t) (-1)))
518  {
519  if (!chs)
520  {
521  char chsbuf[256];
522  mutt_ch_canonical_charset(chsbuf, sizeof(chsbuf), tocode);
523  mutt_param_set(&b->parameter, "charset", chsbuf);
524  }
525  FREE(&b->charset);
526  b->charset = fromcode;
527  FREE(&tocode);
528  mutt_file_fclose(&fp);
529  return info;
530  }
531  }
532 
533  rewind(fp);
534  while ((r = fread(buf, 1, sizeof(buf), fp)))
535  update_content_info(info, &state, buf, r);
536  update_content_info(info, &state, 0, 0);
537 
538  mutt_file_fclose(&fp);
539 
540  if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
541  {
542  mutt_param_set(&b->parameter, "charset",
543  (!info->hibin ? "us-ascii" :
544  c_charset && !mutt_ch_is_us_ascii(c_charset) ?
545  c_charset :
546  "unknown-8bit"));
547  }
548 
549  return info;
550 }
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
static char * chs
Definition: gnupgparse.c:73
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition: charset.c:355
#define mutt_ch_is_us_ascii(str)
Definition: charset.h:96
#define _(a)
Definition: message.h:28
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
static size_t convert_file_from_to(FILE *fp, const char *fromcodes, const char *tocodes, char **fromcode, char **tocode, struct Content *info)
Convert a file between encodings.
Definition: sendlib.c:379
bool noconv
Don't do character set conversion.
Definition: body.h:45
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:77
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:61
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:46
bool force_charset
Send mode: don't adjust the character set when in send-mode.
Definition: body.h:43
unsigned int type
content-type primary type, ContentType
Definition: body.h:39
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_lookup_mime_type()

enum ContentType mutt_lookup_mime_type ( struct Body att,
const char *  path 
)

Find the MIME type for an attachment.

Parameters
attEmail with attachment
pathPath to attachment
Return values
numMIME type, e.g. TYPE_IMAGE

Given a file at 'path', see if there is a registered MIME type. Returns the major MIME type, and copies the subtype to "d". First look in a system mime.types if we can find one, then look for ~/.mime.types. The longest match is used so that we can match 'ps.gz' when 'gz' also exists.

Definition at line 465 of file sendlib.c.

565 {
566  FILE *fp = NULL;
567  char *p = NULL, *q = NULL, *ct = NULL;
568  char buf[PATH_MAX];
569  char subtype[256] = { 0 };
570  char xtype[256] = { 0 };
571  int sze, cur_sze = 0;
572  bool found_mimetypes = false;
573  enum ContentType type = TYPE_OTHER;
574 
575  int szf = mutt_str_len(path);
576 
577  for (int count = 0; count < 4; count++)
578  {
579  /* can't use strtok() because we use it in an inner loop below, so use
580  * a switch statement here instead. */
581  switch (count)
582  {
583  /* last file with last entry to match wins type/xtype */
584  case 0:
585  /* check default unix mimetypes location first */
586  mutt_str_copy(buf, "/etc/mime.types", sizeof(buf));
587  break;
588  case 1:
589  mutt_str_copy(buf, SYSCONFDIR "/mime.types", sizeof(buf));
590  break;
591  case 2:
592  mutt_str_copy(buf, PKGDATADIR "/mime.types", sizeof(buf));
593  break;
594  case 3:
595  snprintf(buf, sizeof(buf), "%s/.mime.types", NONULL(HomeDir));
596  break;
597  default:
598  mutt_debug(LL_DEBUG1, "Internal error, count = %d\n", count);
599  goto bye; /* shouldn't happen */
600  }
601 
602  fp = fopen(buf, "r");
603  if (fp)
604  {
605  found_mimetypes = true;
606 
607  while (fgets(buf, sizeof(buf) - 1, fp))
608  {
609  /* weed out any comments */
610  p = strchr(buf, '#');
611  if (p)
612  *p = '\0';
613 
614  /* remove any leading space. */
615  ct = buf;
616  SKIPWS(ct);
617 
618  /* position on the next field in this line */
619  p = strpbrk(ct, " \t");
620  if (!p)
621  continue;
622  *p++ = 0;
623  SKIPWS(p);
624 
625  /* cycle through the file extensions */
626  while ((p = strtok(p, " \t\n")))
627  {
628  sze = mutt_str_len(p);
629  if ((sze > cur_sze) && (szf >= sze) && mutt_istr_equal(path + szf - sze, p) &&
630  ((szf == sze) || (path[szf - sze - 1] == '.')))
631  {
632  /* get the content-type */
633 
634  p = strchr(ct, '/');
635  if (!p)
636  {
637  /* malformed line, just skip it. */
638  break;
639  }
640  *p++ = 0;
641 
642  for (q = p; *q && !IS_SPACE(*q); q++)
643  ; // do nothing
644 
645  mutt_strn_copy(subtype, p, q - p, sizeof(subtype));
646 
647  type = mutt_check_mime_type(ct);
648  if (type == TYPE_OTHER)
649  mutt_str_copy(xtype, ct, sizeof(xtype));
650 
651  cur_sze = sze;
652  }
653  p = NULL;
654  }
655  }
656  mutt_file_fclose(&fp);
657  }
658  }
659 
660 bye:
661 
662  /* no mime.types file found */
663  if (!found_mimetypes)
664  {
665  mutt_error(_("Could not find any mime.types file"));
666  }
667 
668  if ((type != TYPE_OTHER) || (*xtype != '\0'))
669  {
670  att->type = type;
671  mutt_str_replace(&att->subtype, subtype);
672  mutt_str_replace(&att->xtype, xtype);
673  }
674 
675  return type;
676 }
char * HomeDir
User's home directory.
Definition: mutt_globals.h:51
ContentType
Content-Type.
Definition: mime.h:30
@ TYPE_OTHER
Unknown Content-Type.
Definition: mime.h:31
char * mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
Copy a sub-string into a buffer.
Definition: string.c:528
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
#define PATH_MAX
Definition: mutt.h:40
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:324
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
char * xtype
content-type if x-unknown
Definition: body.h:60
char * subtype
content-type subtype
Definition: body.h:59
+ Here is the caller graph for this function:

◆ transform_to_7bit()

static void transform_to_7bit ( struct Body a,
FILE *  fp_in,
struct ConfigSubset sub 
)
static

Convert MIME parts to 7-bit.

Parameters
aBody of the email
fp_inFile to read
subConfig Subset

Definition at line 684 of file sendlib.c.

685 {
686  struct Buffer *buf = NULL;
687  struct State s = { 0 };
688  struct stat st = { 0 };
689 
690  for (; a; a = a->next)
691  {
692  if (a->type == TYPE_MULTIPART)
693  {
694  a->encoding = ENC_7BIT;
695  transform_to_7bit(a->parts, fp_in, sub);
696  }
697  else if (mutt_is_message_type(a->type, a->subtype))
698  {
699  mutt_message_to_7bit(a, fp_in, sub);
700  }
701  else
702  {
703  a->noconv = true;
704  a->force_charset = true;
705 
706  /* Because of the potential recursion in message types, we
707  * restrict the lifetime of the buffer tightly */
708  buf = mutt_buffer_pool_get();
709  mutt_buffer_mktemp(buf);
711  if (!s.fp_out)
712  {
713  mutt_perror("fopen");
715  return;
716  }
717  s.fp_in = fp_in;
718  mutt_decode_attachment(a, &s);
720  FREE(&a->d_filename);
721  a->d_filename = a->filename;
722  a->filename = mutt_buffer_strdup(buf);
724  a->unlink = true;
725  if (stat(a->filename, &st) == -1)
726  {
727  mutt_perror("stat");
728  return;
729  }
730  a->length = st.st_size;
731 
732  mutt_update_encoding(a, sub);
733  if (a->encoding == ENC_8BIT)
735  else if (a->encoding == ENC_BINARY)
736  a->encoding = ENC_BASE64;
737  }
738  }
739 }
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:432
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
#define mutt_perror(...)
Definition: logging.h:88
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email's attachment.
Definition: handler.c:1854
@ ENC_7BIT
7-bit text
Definition: mime.h:49
@ ENC_BINARY
Binary.
Definition: mime.h:53
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ ENC_8BIT
8-bit text
Definition: mime.h:50
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1428
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
void mutt_message_to_7bit(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Convert an email's MIME parts to 7-bit.
Definition: sendlib.c:747
static void transform_to_7bit(struct Body *a, FILE *fp_in, struct ConfigSubset *sub)
Convert MIME parts to 7-bit.
Definition: sendlib.c:684
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:908
char * d_filename
filename to be used for the content-disposition header If NULL, filename is used instead.
Definition: body.h:55
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:71
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:66
LOFF_T length
length (in bytes) of attachment
Definition: body.h:52
struct Body * next
next attachment in the list
Definition: body.h:70
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:40
String manipulation buffer.
Definition: buffer.h:34
Keep track when processing files.
Definition: state.h:45
FILE * fp_out
File to write to.
Definition: state.h:47
FILE * fp_in
File to read from.
Definition: state.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_message_to_7bit()

void mutt_message_to_7bit ( struct Body a,
FILE *  fp,
struct ConfigSubset sub 
)

Convert an email's MIME parts to 7-bit.

Parameters
aBody of the email
fpFile to read (OPTIONAL)
subConfig Subset

Definition at line 747 of file sendlib.c.

748 {
749  struct Buffer temp = mutt_buffer_make(0);
750  FILE *fp_in = NULL;
751  FILE *fp_out = NULL;
752  struct stat st = { 0 };
753 
754  if (!a->filename && fp)
755  fp_in = fp;
756  else if (!a->filename || !(fp_in = fopen(a->filename, "r")))
757  {
758  mutt_error(_("Could not open %s"), a->filename ? a->filename : "(null)");
759  return;
760  }
761  else
762  {
763  a->offset = 0;
764  if (stat(a->filename, &st) == -1)
765  {
766  mutt_perror("stat");
767  mutt_file_fclose(&fp_in);
768  goto cleanup;
769  }
770  a->length = st.st_size;
771  }
772 
773  /* Avoid buffer pool due to recursion */
774  mutt_buffer_mktemp(&temp);
775  fp_out = mutt_file_fopen(mutt_buffer_string(&temp), "w+");
776  if (!fp_out)
777  {
778  mutt_perror("fopen");
779  goto cleanup;
780  }
781 
782  if (fseeko(fp_in, a->offset, SEEK_SET) != 0)
783  {
784  mutt_perror("fseeko");
785  goto cleanup;
786  }
787  a->parts = mutt_rfc822_parse_message(fp_in, a);
788 
789  transform_to_7bit(a->parts, fp_in, sub);
790 
791  mutt_copy_hdr(fp_in, fp_out, a->offset, a->offset + a->length,
792  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL, 0);
793 
794  fputs("MIME-Version: 1.0\n", fp_out);
795  mutt_write_mime_header(a->parts, fp_out, sub);
796  fputc('\n', fp_out);
797  mutt_write_mime_body(a->parts, fp_out, sub);
798 
799  if (fp_in != fp)
800  mutt_file_fclose(&fp_in);
801  mutt_file_fclose(&fp_out);
802 
803  a->encoding = ENC_7BIT;
804  FREE(&a->d_filename);
805  a->d_filename = a->filename;
806  if (a->filename && a->unlink)
807  unlink(a->filename);
808  a->filename = mutt_buffer_strdup(&temp);
809  a->unlink = true;
810  if (stat(a->filename, &st) == -1)
811  {
812  mutt_perror("stat");
813  goto cleanup;
814  }
815  a->length = st.st_size;
816  mutt_body_free(&a->parts);
817  a->email->body = NULL;
818 
819 cleanup:
820  if (fp_in && (fp_in != fp))
821  mutt_file_fclose(&fp_in);
822 
823  if (fp_out)
824  {
825  mutt_file_fclose(&fp_out);
827  }
828 
829  mutt_buffer_dealloc(&temp);
830 }
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
int mutt_copy_hdr(FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy header from one file to another.
Definition: copy.c:104
#define CH_XMIT
Transmitting this message? (Ignore Lines: and Content-Length:)
Definition: copy.h:55
#define CH_NONEWLINE
Don't output terminating newline after the header.
Definition: copy.h:60
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:61
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:761
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
Parse a Message/RFC822 body.
Definition: parse.c:1736
int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:314
LOFF_T offset
offset where the actual data begins
Definition: body.h:51
struct Email * email
header information for message/rfc822
Definition: body.h:72
struct Body * body
List of MIME parts.
Definition: email.h:67
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_encoding()

static void set_encoding ( struct Body b,
struct Content info,
struct ConfigSubset sub 
)
static

Determine which Content-Transfer-Encoding to use.

Parameters
[in]bBody of email
[out]infoInfo about the email
[in]subConfig Subset

Definition at line 838 of file sendlib.c.

839 {
840  const bool c_allow_8bit = cs_subset_bool(sub, "allow_8bit");
841  if (b->type == TYPE_TEXT)
842  {
843  const bool c_encode_from = cs_subset_bool(sub, "encode_from");
844  char send_charset[128];
845  char *chsname = mutt_body_get_charset(b, send_charset, sizeof(send_charset));
846  if ((info->lobin && !mutt_istr_startswith(chsname, "iso-2022")) ||
847  (info->linemax > 990) || (info->from && c_encode_from))
848  {
850  }
851  else if (info->hibin)
852  {
853  b->encoding = c_allow_8bit ? ENC_8BIT : ENC_QUOTED_PRINTABLE;
854  }
855  else
856  {
857  b->encoding = ENC_7BIT;
858  }
859  }
860  else if ((b->type == TYPE_MESSAGE) || (b->type == TYPE_MULTIPART))
861  {
862  if (info->lobin || info->hibin)
863  {
864  if (c_allow_8bit && !info->lobin)
865  b->encoding = ENC_8BIT;
866  else
867  mutt_message_to_7bit(b, NULL, sub);
868  }
869  else
870  b->encoding = ENC_7BIT;
871  }
872  else if ((b->type == TYPE_APPLICATION) &&
873  mutt_istr_equal(b->subtype, "pgp-keys"))
874  {
875  b->encoding = ENC_7BIT;
876  }
877  else
878  {
879  /* Determine which encoding is smaller */
880  if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
881  3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
882  {
883  b->encoding = ENC_BASE64;
884  }
885  else
886  {
888  }
889  }
890 }
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body's character set.
Definition: body.c:131
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_stamp_attachment()

void mutt_stamp_attachment ( struct Body a)

Timestamp an Attachment.

Parameters
aAttachment

Definition at line 896 of file sendlib.c.

897 {
898  a->stamp = mutt_date_epoch();
899 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
time_t stamp
Time stamp of last encoding update.
Definition: body.h:75
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_update_encoding()

void mutt_update_encoding ( struct Body a,
struct ConfigSubset sub 
)

Update the encoding type.

Parameters
aBody to update
subConfig Subset

Assumes called from send mode where Body->filename points to actual file

Definition at line 908 of file sendlib.c.

909 {
910  struct Content *info = NULL;
911  char chsbuf[256];
912 
913  /* override noconv when it's us-ascii */
914  if (mutt_ch_is_us_ascii(mutt_body_get_charset(a, chsbuf, sizeof(chsbuf))))
915  a->noconv = false;
916 
917  if (!a->force_charset && !a->noconv)
918  mutt_param_delete(&a->parameter, "charset");
919 
920  info = mutt_get_content_info(a->filename, a, sub);
921  if (!info)
922  return;
923 
924  set_encoding(a, info, sub);
926 
927  FREE(&a->content);
928  a->content = info;
929 }
void mutt_param_delete(struct ParameterList *pl, const char *attribute)
Delete a matching Parameter.
Definition: parameter.c:142
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:896
struct Content * mutt_get_content_info(const char *fname, struct Body *b, struct ConfigSubset *sub)
Analyze file to determine MIME encoding to use.
Definition: sendlib.c:465
static void set_encoding(struct Body *b, struct Content *info, struct ConfigSubset *sub)
Determine which Content-Transfer-Encoding to use.
Definition: sendlib.c:838
struct Content * content
Detailed info about the content of the attachment.
Definition: body.h:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_message_attach()

struct Body* mutt_make_message_attach ( struct Mailbox m,
struct Email e,
bool  attach_msg,
struct ConfigSubset sub 
)

Create a message attachment.

Parameters
mMailbox
eEmail
attach_msgtrue if attaching a message
subConfig Subset
Return values
ptrNewly allocated Body
NULLError

Definition at line 940 of file sendlib.c.

942 {
943  struct Body *body = NULL;
944  FILE *fp = NULL;
945  CopyMessageFlags cmflags;
947 
948  const bool c_mime_forward_decode = cs_subset_bool(sub, "mime_forward_decode");
949  const bool c_forward_decrypt = cs_subset_bool(sub, "forward_decrypt");
950  if (WithCrypto)
951  {
952  if ((c_mime_forward_decode || c_forward_decrypt) && (e->security & SEC_ENCRYPT))
953  {
955  return NULL;
956  }
957  }
958 
959  struct Buffer *buf = mutt_buffer_pool_get();
960  mutt_buffer_mktemp(buf);
961  fp = mutt_file_fopen(mutt_buffer_string(buf), "w+");
962  if (!fp)
963  {
965  return NULL;
966  }
967 
968  body = mutt_body_new();
969  body->type = TYPE_MESSAGE;
970  body->subtype = mutt_str_dup("rfc822");
972  body->unlink = true;
973  body->use_disp = false;
974  body->disposition = DISP_INLINE;
975  body->noconv = true;
976 
978 
979  struct Message *msg = mx_msg_open(m, e->msgno);
980  if (!msg)
981  {
982  mutt_body_free(&body);
983  return NULL;
984  }
985  mutt_parse_mime_message(e, msg->fp);
986 
987  CopyHeaderFlags chflags = CH_XMIT;
988  cmflags = MUTT_CM_NO_FLAGS;
989 
990  /* If we are attaching a message, ignore `$mime_forward_decode` */
991  if (!attach_msg && c_mime_forward_decode)
992  {
993  chflags |= CH_MIME | CH_TXTPLAIN;
994  cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
996  pgp &= ~PGP_ENCRYPT;
998  pgp &= ~SMIME_ENCRYPT;
999  }
1000  else if ((WithCrypto != 0) && c_forward_decrypt && (e->security & SEC_ENCRYPT))
1001  {
1003  {
1004  chflags |= CH_MIME | CH_NONEWLINE;
1005  cmflags = MUTT_CM_DECODE_PGP;
1006  pgp &= ~PGP_ENCRYPT;
1007  }
1008  else if (((WithCrypto & APPLICATION_PGP) != 0) &&
1010  {
1011  chflags |= CH_MIME | CH_TXTPLAIN;
1012  cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1013  pgp &= ~PGP_ENCRYPT;
1014  }
1015  else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
1017  {
1018  chflags |= CH_MIME | CH_TXTPLAIN;
1019  cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1020  pgp &= ~SMIME_ENCRYPT;
1021  }
1022  }
1023 
1024  mutt_copy_message(fp, e, msg, cmflags, chflags, 0);
1025  mx_msg_close(m, &msg);
1026 
1027  fflush(fp);
1028  rewind(fp);
1029 
1030  body->email = email_new();
1031  body->email->offset = 0;
1032  /* we don't need the user headers here */
1033  body->email->env = mutt_rfc822_read_header(fp, body->email, false, false);
1034  if (WithCrypto)
1035  body->email->security = pgp;
1036  mutt_update_encoding(body, sub);
1037  body->parts = body->email->body;
1038 
1039  mutt_file_fclose(&fp);
1040 
1041  return body;
1042 }
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:593
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:881
#define MUTT_CM_DECODE_PGP
Used for decoding PGP messages.
Definition: copy.h:45
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:38
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:42
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:50
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:35
#define CH_TXTPLAIN
Generate text/plain MIME headers.
Definition: copy.h:63
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition: copy.h:32
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:559
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:617
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:135
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:454
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1189
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1143
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:71
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
#define PGP_ENCRYPT
Definition: lib.h:93
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:88
#define SEC_NO_FLAGS
No flags are set.
Definition: lib.h:74
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:75
#define SMIME_ENCRYPT
Definition: lib.h:99
#define WithCrypto
Definition: lib.h:113
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1168
The body of an email.
Definition: body.h:35
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:41
struct Envelope * env
Envelope information.
Definition: email.h:66
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
int msgno
Number displayed to the user.
Definition: email.h:111
A local copy of an email.
Definition: mxapi.h:42
FILE * fp
pointer to the message data
Definition: mxapi.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ run_mime_type_query()

static void run_mime_type_query ( struct Body att,
struct ConfigSubset sub 
)
static

Run an external command to determine the MIME type.

Parameters
attAttachment
subConfig Subset

The command in $mime_type_query_command is run.

Definition at line 1051 of file sendlib.c.

1052 {
1053  FILE *fp = NULL, *fp_err = NULL;
1054  char *buf = NULL;
1055  size_t buflen;
1056  pid_t pid;
1057  struct Buffer *cmd = mutt_buffer_pool_get();
1058 
1059  const char *const c_mime_type_query_command =
1060  cs_subset_string(sub, "mime_type_query_command");
1061 
1062  mutt_buffer_file_expand_fmt_quote(cmd, c_mime_type_query_command, att->filename);
1063 
1064  pid = filter_create(mutt_buffer_string(cmd), NULL, &fp, &fp_err);
1065  if (pid < 0)
1066  {
1067  mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
1069  return;
1070  }
1072 
1073  buf = mutt_file_read_line(buf, &buflen, fp, NULL, MUTT_RL_NO_FLAGS);
1074  if (buf)
1075  {
1076  if (strchr(buf, '/'))
1077  mutt_parse_content_type(buf, att);
1078  FREE(&buf);
1079  }
1080 
1081  mutt_file_fclose(&fp);
1082  mutt_file_fclose(&fp_err);
1083  filter_wait(pid);
1084 }
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:671
void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition: file.c:1439
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:425
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_file_attach()

struct Body* mutt_make_file_attach ( const char *  path,
struct ConfigSubset sub 
)

Create a file attachment.

Parameters
pathFile to attach
subConfig Subset
Return values
ptrNewly allocated Body
NULLError

Definition at line 1093 of file sendlib.c.

1094 {
1095  if (!path || !*path)
1096  return NULL;
1097 
1098  struct Body *att = mutt_body_new();
1099  att->filename = mutt_str_dup(path);
1100 
1101  const char *const c_mime_type_query_command =
1102  cs_subset_string(sub, "mime_type_query_command");
1103  const bool c_mime_type_query_first =
1104  cs_subset_bool(sub, "mime_type_query_first");
1105 
1106  if (c_mime_type_query_command && c_mime_type_query_first)
1107  run_mime_type_query(att, sub);
1108 
1109  /* Attempt to determine the appropriate content-type based on the filename
1110  * suffix. */
1111  if (!att->subtype)
1112  mutt_lookup_mime_type(att, path);
1113 
1114  if (!att->subtype && c_mime_type_query_command && !c_mime_type_query_first)
1115  {
1116  run_mime_type_query(att, sub);
1117  }
1118 
1119  struct Content *info = mutt_get_content_info(path, att, sub);
1120  if (!info)
1121  {
1122  mutt_body_free(&att);
1123  return NULL;
1124  }
1125 
1126  if (!att->subtype)
1127  {
1128  if ((info->nulbin == 0) &&
1129  ((info->lobin == 0) || ((info->lobin + info->hibin + info->ascii) / info->lobin >= 10)))
1130  {
1131  /* Statistically speaking, there should be more than 10% "lobin"
1132  * chars if this is really a binary file... */
1133  att->type = TYPE_TEXT;
1134  att->subtype = mutt_str_dup("plain");
1135  }
1136  else
1137  {
1138  att->type = TYPE_APPLICATION;
1139  att->subtype = mutt_str_dup("octet-stream");
1140  }
1141  }
1142 
1143  FREE(&info);
1144  mutt_update_encoding(att, sub);
1145  return att;
1146 }
enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:564
static void run_mime_type_query(struct Body *att, struct ConfigSubset *sub)
Run an external command to determine the MIME type.
Definition: sendlib.c:1051
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ encode_headers()

static void encode_headers ( struct ListHead *  h,
struct ConfigSubset sub 
)
static

RFC2047-encode a list of headers.

Parameters
hString List of headers
subConfig Subset

The strings are encoded in-place.

Definition at line 1155 of file sendlib.c.

1156 {
1157  char *tmp = NULL;
1158  char *p = NULL;
1159  int i;
1160 
1161  const char *const c_send_charset = cs_subset_string(sub, "send_charset");
1162 
1163  struct ListNode *np = NULL;
1164  STAILQ_FOREACH(np, h, entries)
1165  {
1166  p = strchr(np->data, ':');
1167  if (!p)
1168  continue;
1169 
1170  i = p - np->data;
1171  p = mutt_str_skip_email_wsp(p + 1);
1172  tmp = mutt_str_dup(p);
1173 
1174  if (!tmp)
1175  continue;
1176 
1177  rfc2047_encode(&tmp, NULL, i + 2, c_send_charset);
1178  mutt_mem_realloc(&np->data, i + 2 + mutt_str_len(tmp) + 1);
1179 
1180  sprintf(np->data + i + 2, "%s", tmp);
1181 
1182  FREE(&tmp);
1183  }
1184 }
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
void rfc2047_encode(char **pd, const char *specials, int col, const char *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:619
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_fqdn()

const char* mutt_fqdn ( bool  may_hide_host,
const struct ConfigSubset sub 
)

Get the Fully-Qualified Domain Name.

Parameters
may_hide_hostIf true, hide the hostname (leaving just the domain)
subConfig Subset
Return values
ptrstring pointer into Hostname
NULLError, e.g no Hostname
Warning
Do not free the returned pointer

Definition at line 1195 of file sendlib.c.

1196 {
1197  const char *const c_hostname = cs_subset_string(sub, "hostname");
1198  if (!c_hostname || (c_hostname[0] == '@'))
1199  return NULL;
1200 
1201  const char *p = c_hostname;
1202 
1203  const bool c_hidden_host = cs_subset_bool(sub, "hidden_host");
1204  if (may_hide_host && c_hidden_host)
1205  {
1206  p = strchr(c_hostname, '.');
1207  if (p)
1208  p++;
1209 
1210  // sanity check: don't hide the host if the fqdn is something like example.com
1211  if (!p || !strchr(p, '.'))
1212  p = c_hostname;
1213  }
1214 
1215  return p;
1216 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ gen_msgid()

static char* gen_msgid ( struct ConfigSubset sub)
static

Generate a unique Message ID.

Return values
ptrMessage ID
Note
The caller should free the string

Definition at line 1224 of file sendlib.c.

1225 {
1226  char buf[128];
1227  char rndid[MUTT_RANDTAG_LEN + 1];
1228 
1229  mutt_rand_base32(rndid, sizeof(rndid) - 1);
1230  rndid[MUTT_RANDTAG_LEN] = 0;
1231  const char *fqdn = mutt_fqdn(false, sub);
1232  if (!fqdn)
1233  fqdn = NONULL(ShortHostname);
1234 
1235  struct tm tm = mutt_date_gmtime(MUTT_DATE_NOW);
1236  snprintf(buf, sizeof(buf), "<%d%02d%02d%02d%02d%02d.%s@%s>", tm.tm_year + 1900,
1237  tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, rndid, fqdn);
1238  return mutt_str_dup(buf);
1239 }
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition: date.c:672
#define MUTT_DATE_NOW
Constant representing the 'current time', see: mutt_date_gmtime(), mutt_date_localtime()
Definition: date.h:39
char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:52
void mutt_rand_base32(char *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition: random.c:103
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:1195
#define MUTT_RANDTAG_LEN
Definition: sendlib.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_prepare_envelope()

void mutt_prepare_envelope ( struct Envelope env,
bool  final,
struct ConfigSubset sub 
)

Prepare an email header.

Parameters
envEnvelope to prepare
finaltrue if this email is going to be sent (not postponed)
subConfig Subset

Encode all the headers prior to sending the email.

For postponing (!final) do the necessary encodings only

Definition at line 1251 of file sendlib.c.

1252 {
1253  if (final)
1254  {
1255  if (!TAILQ_EMPTY(&env->bcc) && TAILQ_EMPTY(&env->to) && TAILQ_EMPTY(&env->cc))
1256  {
1257  /* some MTA's will put an Apparently-To: header field showing the Bcc:
1258  * recipients if there is no To: or Cc: field, so attempt to suppress
1259  * it by using an empty To: field. */
1260  struct Address *to = mutt_addr_new();
1261  to->group = true;
1262  mutt_addrlist_append(&env->to, to);
1264 
1265  char buf[1024];
1266  buf[0] = '\0';
1267  mutt_addr_cat(buf, sizeof(buf), "undisclosed-recipients", AddressSpecials);
1268 
1269  to->mailbox = mutt_str_dup(buf);
1270  }
1271 
1272  mutt_set_followup_to(env, sub);
1273 
1274  if (!env->message_id)
1275  env->message_id = gen_msgid(sub);
1276  }
1277 
1278  /* Take care of 8-bit => 7-bit conversion. */
1280  encode_headers(&env->userhdrs, sub);
1281 }
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
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1490
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:42
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
#define TAILQ_EMPTY(head)
Definition: queue.h:721
void rfc2047_encode_envelope(struct Envelope *env)
Encode the fields of an Envelope.
Definition: rfc2047.c:810
void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
Set followup-to field.
Definition: send.c:1313
static char * gen_msgid(struct ConfigSubset *sub)
Generate a unique Message ID.
Definition: sendlib.c:1224
static void encode_headers(struct ListHead *h, struct ConfigSubset *sub)
RFC2047-encode a list of headers.
Definition: sendlib.c:1155
An email address.
Definition: address.h:36
bool group
Group mailbox?
Definition: address.h:39
char * mailbox
Mailbox and host address.
Definition: address.h:38
struct ListHead userhdrs
user defined headers
Definition: envelope.h:85
struct AddressList to
Email's 'To' list.
Definition: envelope.h:58
char * message_id
Message ID.
Definition: envelope.h:71
struct AddressList cc
Email's 'Cc' list.
Definition: envelope.h:59
struct AddressList bcc
Email's 'Bcc' list.
Definition: envelope.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_unprepare_envelope()

void mutt_unprepare_envelope ( struct Envelope env)

Undo the encodings of mutt_prepare_envelope()

Parameters
envEnvelope to unprepare

Decode all the headers of an email, e.g. when the sending failed or was aborted.

Definition at line 1290 of file sendlib.c.

1291 {
1292  struct ListNode *item = NULL;
1293  STAILQ_FOREACH(item, &env->userhdrs, entries)
1294  {
1295  rfc2047_decode(&item->data);
1296  }
1297 
1299 
1300  /* back conversions */
1302 }
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1470
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:790
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:644
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition: envelope.h:63
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bounce_message()

static int bounce_message ( FILE *  fp,
struct Mailbox m,
struct Email e,
struct AddressList *  to,
const char *  resent_from,
struct AddressList *  env_from,
struct ConfigSubset sub 
)
static

Bounce an email message.

Parameters
fpHandle of message
mMailbox
eEmail
toAddress to bounce to
resent_fromAddress of new sender
env_fromEnvelope of original sender
subConfig Subset
Return values
0Success
-1Failure

Definition at line 1316 of file sendlib.c.

1319 {
1320  if (!e)
1321  return -1;
1322 
1323  int rc = 0;
1324 
1325  struct Buffer *tempfile = mutt_buffer_pool_get();
1326  mutt_buffer_mktemp(tempfile);
1327  FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tempfile), "w");
1328  if (fp_tmp)
1329  {
1331 
1332  const bool c_bounce_delivered = cs_subset_bool(sub, "bounce_delivered");
1333  if (!c_bounce_delivered)
1334  chflags |= CH_WEED_DELIVERED;
1335 
1336  if (fseeko(fp, e->offset, SEEK_SET) != 0)
1337  {
1338  (void) mutt_file_fclose(&fp_tmp);
1339  mutt_perror("fseeko");
1340  return -1;
1341  }
1342  fprintf(fp_tmp, "Resent-From: %s\n", resent_from);
1343 
1344  struct Buffer *date = mutt_buffer_pool_get();
1345  mutt_date_make_date(date, cs_subset_bool(sub, "local_date_header"));
1346  fprintf(fp_tmp, "Resent-Date: %s\n", mutt_buffer_string(date));
1347  mutt_buffer_pool_release(&date);
1348 
1349  char *msgid_str = gen_msgid(sub);
1350  fprintf(fp_tmp, "Resent-Message-ID: %s\n", msgid_str);
1351  FREE(&msgid_str);
1352  fputs("Resent-To: ", fp_tmp);
1353  mutt_addrlist_write_file(to, fp_tmp, 11, false);
1354  mutt_copy_header(fp, e, fp_tmp, chflags, NULL, 0);
1355  fputc('\n', fp_tmp);
1356  mutt_file_copy_bytes(fp, fp_tmp, e->body->length);
1357  if (mutt_file_fclose(&fp_tmp) != 0)
1358  {
1359  mutt_perror(mutt_buffer_string(tempfile));
1360  unlink(mutt_buffer_string(tempfile));
1361  return -1;
1362  }
1363 #ifdef USE_SMTP
1364  const char *const c_smtp_url = cs_subset_string(sub, "smtp_url");
1365  if (c_smtp_url)
1366  {
1367  rc = mutt_smtp_send(env_from, to, NULL, NULL, mutt_buffer_string(tempfile),
1368  (e->body->encoding == ENC_8BIT), sub);
1369  }
1370  else
1371 #endif
1372  {
1373  rc = mutt_invoke_sendmail(m, env_from, to, NULL, NULL, mutt_buffer_string(tempfile),
1374  (e->body->encoding == ENC_8BIT), sub);
1375  }
1376  }
1377 
1378  mutt_buffer_pool_release(&tempfile);
1379  return rc;
1380 }
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_copy_header(FILE *fp_in, struct Email *e, FILE *fp_out, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy Email header.
Definition: copy.c:423
#define CH_WEED_DELIVERED
Weed eventual Delivered-To headers.
Definition: copy.h:65
#define CH_NOQFROM
Ignore ">From " line.
Definition: copy.h:67
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:378
int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
Copy some content from one file to another.
Definition: file.c:241
int mutt_invoke_sendmail(struct Mailbox *m, struct AddressList *from, struct AddressList *to, struct AddressList *cc, struct AddressList *bcc, const char *msg, bool eightbit, struct ConfigSubset *sub)
Run sendmail.
Definition: sendmail.c:287
int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to, const struct AddressList *cc, const struct AddressList *bcc, const char *msgfile, bool eightbit, struct ConfigSubset *sub)
Send a message using SMTP.
Definition: smtp.c:913
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bounce_message()

int mutt_bounce_message ( FILE *  fp,
struct Mailbox m,
struct Email e,
struct AddressList *  to,
struct ConfigSubset sub 
)

Bounce an email message.

Parameters
fpHandle of message
mMailbox
eEmail
toAddressList to bounce to
subConfig Subset
Return values
0Success
-1Failure

Definition at line 1392 of file sendlib.c.

1394 {
1395  if (!fp || !e || !to || TAILQ_EMPTY(to))
1396  return -1;
1397 
1398  const char *fqdn = mutt_fqdn(true, sub);
1399  char resent_from[256];
1400  char *err = NULL;
1401 
1402  resent_from[0] = '\0';
1403  struct Address *from = mutt_default_from(sub);
1404  struct AddressList from_list = TAILQ_HEAD_INITIALIZER(from_list);
1405  mutt_addrlist_append(&from_list, from);
1406 
1407  /* mutt_default_from() does not use $real_name if the real name is not set
1408  * in $from, so we add it here. The reason it is not added in
1409  * mutt_default_from() is that during normal sending, we execute
1410  * send-hooks and set the real_name last so that it can be changed based
1411  * upon message criteria. */
1412  if (!from->personal)
1413  {
1414  const char *const c_real_name = cs_subset_string(sub, "real_name");
1415  from->personal = mutt_str_dup(c_real_name);
1416  }
1417 
1418  mutt_addrlist_qualify(&from_list, fqdn);
1419 
1420  rfc2047_encode_addrlist(&from_list, "Resent-From");
1421  if (mutt_addrlist_to_intl(&from_list, &err))
1422  {
1423  mutt_error(_("Bad IDN %s while preparing resent-from"), err);
1424  FREE(&err);
1425  mutt_addrlist_clear(&from_list);
1426  return -1;
1427  }
1428  mutt_addrlist_write(&from_list, resent_from, sizeof(resent_from), false);
1429 
1430 #ifdef USE_NNTP
1431  OptNewsSend = false;
1432 #endif
1433 
1434  /* prepare recipient list. idna conversion appears to happen before this
1435  * function is called, since the user receives confirmation of the address
1436  * list being bounced to. */
1437  struct AddressList resent_to = TAILQ_HEAD_INITIALIZER(resent_to);
1438  mutt_addrlist_copy(&resent_to, to, false);
1439  rfc2047_encode_addrlist(&resent_to, "Resent-To");
1440  int rc = bounce_message(fp, m, e, &resent_to, resent_from, &from_list, sub);
1441  mutt_addrlist_clear(&resent_to);
1442  mutt_addrlist_clear(&from_list);
1443 
1444  return rc;
1445 }
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:737
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:650
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
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1305
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:51
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
Encode any RFC2047 headers, where required, in an Address list.
Definition: rfc2047.c:745
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition: send.c:1438
static int bounce_message(FILE *fp, struct Mailbox *m, struct Email *e, struct AddressList *to, const char *resent_from, struct AddressList *env_from, struct ConfigSubset *sub)
Bounce an email message.
Definition: sendlib.c:1316
char * personal
Real name of address.
Definition: address.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_noconv_flags()

static void set_noconv_flags ( struct Body b,
bool  flag 
)
static

Set/reset the "x-mutt-noconv" flag.

Parameters
bBody of email
flagIf true, set the flag, otherwise remove it

Definition at line 1452 of file sendlib.c.

1453 {
1454  for (; b; b = b->next)
1455  {
1456  if ((b->type == TYPE_MESSAGE) || (b->type == TYPE_MULTIPART))
1457  set_noconv_flags(b->parts, flag);
1458  else if ((b->type == TYPE_TEXT) && b->noconv)
1459  {
1460  if (flag)
1461  mutt_param_set(&b->parameter, "x-mutt-noconv", "yes");
1462  else
1463  mutt_param_delete(&b->parameter, "x-mutt-noconv");
1464  }
1465  }
1466 }
static void set_noconv_flags(struct Body *b, bool flag)
Set/reset the "x-mutt-noconv" flag.
Definition: sendlib.c:1452
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_multiple_fcc()

int mutt_write_multiple_fcc ( const char *  path,
struct Email e,
const char *  msgid,
bool  post,
char *  fcc,
char **  finalpath,
struct ConfigSubset sub 
)

Handle FCC with multiple, comma separated entries.

Parameters
[in]pathPath to mailboxes (comma separated)
[in]eEmail
[in]msgidMessage id
[in]postIf true, postpone message
[in]fccfcc setting to save (postpone only)
[out]finalpathFinal path of email
[in]subConfig Subset
Return values
0Success
-1Failure

Definition at line 1480 of file sendlib.c.

1482 {
1483  char fcc_tok[PATH_MAX];
1484  char fcc_expanded[PATH_MAX];
1485 
1486  mutt_str_copy(fcc_tok, path, sizeof(fcc_tok));
1487 
1488  char *tok = strtok(fcc_tok, ",");
1489  if (!tok)
1490  return -1;
1491 
1492  mutt_debug(LL_DEBUG1, "Fcc: initial mailbox = '%s'\n", tok);
1493  /* mutt_expand_path already called above for the first token */
1494  int status = mutt_write_fcc(tok, e, msgid, post, fcc, finalpath, sub);
1495  if (status != 0)
1496  return status;
1497 
1498  while ((tok = strtok(NULL, ",")))
1499  {
1500  if (*tok == '\0')
1501  continue;
1502 
1503  /* Only call mutt_expand_path if tok has some data */
1504  mutt_debug(LL_DEBUG1, "Fcc: additional mailbox token = '%s'\n", tok);
1505  mutt_str_copy(fcc_expanded, tok, sizeof(fcc_expanded));
1506  mutt_expand_path(fcc_expanded, sizeof(fcc_expanded));
1507  mutt_debug(LL_DEBUG1, " Additional mailbox expanded = '%s'\n", fcc_expanded);
1508  status = mutt_write_fcc(fcc_expanded, e, msgid, post, fcc, finalpath, sub);
1509  if (status != 0)
1510  return status;
1511  }
1512 
1513  return 0;
1514 }
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:122
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath, struct ConfigSubset *sub)
Write email to FCC mailbox.
Definition: sendlib.c:1528
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_fcc()

int mutt_write_fcc ( const char *  path,
struct Email e,
const char *  msgid,
bool  post,
const char *  fcc,
char **  finalpath,
struct ConfigSubset sub 
)

Write email to FCC mailbox.

Parameters
[in]pathPath to mailbox
[in]eEmail
[in]msgidMessage id
[in]postIf true, postpone message, else fcc mode
[in]fccfcc setting to save (postpone only)
[out]finalpathFinal path of email
[in]subConfig Subset
Return values
0Success
-1Failure

Definition at line 1528 of file sendlib.c.

1530 {
1531  struct Message *msg = NULL;
1532  struct Buffer *tempfile = NULL;
1533  FILE *fp_tmp = NULL;
1534  int rc = -1;
1535  bool need_mailbox_cleanup = false;
1536  struct stat st = { 0 };
1537  MsgOpenFlags onm_flags;
1538 
1539  if (post)
1540  set_noconv_flags(e->body, true);
1541 
1542 #ifdef RECORD_FOLDER_HOOK
1543  mutt_folder_hook(path, NULL);
1544 #endif
1545  struct Mailbox *m_fcc = mx_path_resolve(path);
1546  bool old_append = m_fcc->append;
1547  if (!mx_mbox_open(m_fcc, MUTT_APPEND | MUTT_QUIET))
1548  {
1549  mutt_debug(LL_DEBUG1, "unable to open mailbox %s in append-mode, aborting\n", path);
1550  goto done;
1551  }
1552 
1553  /* We need to add a Content-Length field to avoid problems where a line in
1554  * the message body begins with "From " */
1555  if ((m_fcc->type == MUTT_MMDF) || (m_fcc->type == MUTT_MBOX))
1556  {
1557  tempfile = mutt_buffer_pool_get();
1558  mutt_buffer_mktemp(tempfile);
1559  fp_tmp = mutt_file_fopen(mutt_buffer_string(tempfile), "w+");
1560  if (!fp_tmp)
1561  {
1562  mutt_perror(mutt_buffer_string(tempfile));
1563  mx_mbox_close(m_fcc);
1564  goto done;
1565  }
1566  /* remember new mail status before appending message */
1567  need_mailbox_cleanup = true;
1568  stat(path, &st);
1569  }
1570 
1571  e->read = !post; /* make sure to put it in the 'cur' directory (maildir) */
1572  onm_flags = MUTT_ADD_FROM;
1573  if (post)
1574  onm_flags |= MUTT_SET_DRAFT;
1575  msg = mx_msg_open_new(m_fcc, e, onm_flags);
1576  if (!msg)
1577  {
1578  mutt_file_fclose(&fp_tmp);
1579  mx_mbox_close(m_fcc);
1580  goto done;
1581  }
1582 
1583  const bool c_crypt_protected_headers_read =
1584  cs_subset_bool(sub, "crypt_protected_headers_read");
1585 
1586  /* post == 1 => postpone message.
1587  * post == 0 => Normal mode. */
1589  msg->fp, e->env, e->body,
1591  c_crypt_protected_headers_read && mutt_should_hide_protected_subject(e), sub);
1592 
1593  /* (postponement) if this was a reply of some sort, <msgid> contains the
1594  * Message-ID: of message replied to. Save it using a special X-Mutt-
1595  * header so it can be picked up if the message is recalled at a later
1596  * point in time. This will allow the message to be marked as replied if
1597  * the same mailbox is still open. */
1598  if (post && msgid)
1599  fprintf(msg->fp, "X-Mutt-References: %s\n", msgid);
1600 
1601  /* (postponement) save the Fcc: using a special X-Mutt- header so that
1602  * it can be picked up when the message is recalled */
1603  if (post && fcc)
1604  fprintf(msg->fp, "X-Mutt-Fcc: %s\n", fcc);
1605 
1606  if ((m_fcc->type == MUTT_MMDF) || (m_fcc->type == MUTT_MBOX))
1607  fprintf(msg->fp, "Status: RO\n");
1608 
1609  /* (postponement) if the mail is to be signed or encrypted, save this info */
1610  if (((WithCrypto & APPLICATION_PGP) != 0) && post && (e->security & APPLICATION_PGP))
1611  {
1612  fputs("X-Mutt-PGP: ", msg->fp);
1613  if (e->security & SEC_ENCRYPT)
1614  fputc('E', msg->fp);
1615  if (e->security & SEC_OPPENCRYPT)
1616  fputc('O', msg->fp);
1617  if (e->security & SEC_SIGN)
1618  {
1619  fputc('S', msg->fp);
1620 
1621  const char *const c_pgp_sign_as = cs_subset_string(sub, "pgp_sign_as");
1622  if (c_pgp_sign_as)
1623  fprintf(msg->fp, "<%s>", c_pgp_sign_as);
1624  }
1625  if (e->security & SEC_INLINE)
1626  fputc('I', msg->fp);
1627 #ifdef USE_AUTOCRYPT
1628  if (e->security & SEC_AUTOCRYPT)
1629  fputc('A', msg->fp);
1631  fputc('Z', msg->fp);
1632 #endif
1633  fputc('\n', msg->fp);
1634  }
1635 
1636  /* (postponement) if the mail is to be signed or encrypted, save this info */
1637  if (((WithCrypto & APPLICATION_SMIME) != 0) && post && (e->security & APPLICATION_SMIME))
1638  {
1639  fputs("X-Mutt-SMIME: ", msg->fp);
1640  if (e->security & SEC_ENCRYPT)
1641  {
1642  fputc('E', msg->fp);
1643 
1644  const char *const c_smime_encrypt_with =
1645  cs_subset_string(sub, "smime_encrypt_with");
1646  if (c_smime_encrypt_with)
1647  fprintf(msg->fp, "C<%s>", c_smime_encrypt_with);
1648  }
1649  if (e->security & SEC_OPPENCRYPT)
1650  fputc('O', msg->fp);
1651  if (e->security & SEC_SIGN)
1652  {
1653  fputc('S', msg->fp);
1654 
1655  const char *const c_smime_sign_as =
1656  cs_subset_string(sub, "smime_sign_as");
1657  if (c_smime_sign_as)
1658  fprintf(msg->fp, "<%s>", c_smime_sign_as);
1659  }
1660  if (e->security & SEC_INLINE)
1661  fputc('I', msg->fp);
1662  fputc('\n', msg->fp);
1663  }
1664 
1665 #ifdef MIXMASTER
1666  /* (postponement) if the mail is to be sent through a mixmaster
1667  * chain, save that information */
1668 
1669  if (post && !STAILQ_EMPTY(&e->chain))
1670  {
1671  fputs("X-Mutt-Mix:", msg->fp);
1672  struct ListNode *p = NULL;
1673  STAILQ_FOREACH(p, &e->chain, entries)
1674  {
1675  fprintf(msg->fp, " %s", (char *) p->data);
1676  }
1677 
1678  fputc('\n', msg->fp);
1679  }
1680 #endif
1681 
1682  if (fp_tmp)
1683  {
1684  mutt_write_mime_body(e->body, fp_tmp, sub);
1685 
1686  /* make sure the last line ends with a newline. Emacs doesn't ensure this
1687  * will happen, and it can cause problems parsing the mailbox later. */
1688  fseek(fp_tmp, -1, SEEK_END);
1689  if (fgetc(fp_tmp) != '\n')
1690  {
1691  fseek(fp_tmp, 0, SEEK_END);
1692  fputc('\n', fp_tmp);
1693  }
1694 
1695  fflush(fp_tmp);
1696  if (ferror(fp_tmp))
1697  {
1698  mutt_debug(LL_DEBUG1, "%s: write failed\n", mutt_buffer_string(tempfile));
1699  mutt_file_fclose(&fp_tmp);
1700  unlink(mutt_buffer_string(tempfile));
1701  mx_msg_commit(m_fcc, msg); /* XXX really? */
1702  mx_msg_close(m_fcc, &msg);
1703  mx_mbox_close(m_fcc);
1704  goto done;
1705  }
1706 
1707  /* count the number of lines */
1708  int lines = 0;
1709  char line_buf[1024];
1710  rewind(fp_tmp);
1711  while (fgets(line_buf, sizeof(line_buf), fp_tmp))
1712  lines++;
1713  fprintf(msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello(fp_tmp));
1714  fprintf(msg->fp, "Lines: %d\n\n", lines);
1715 
1716  /* copy the body and clean up */
1717  rewind(fp_tmp);
1718  rc = mutt_file_copy_stream(fp_tmp, msg->fp);
1719  if (mutt_file_fclose(&fp_tmp) != 0)
1720  rc = -1;
1721  /* if there was an error, leave the temp version */
1722  if (rc >= 0)
1723  {
1724  unlink(mutt_buffer_string(tempfile));
1725  rc = 0;
1726  }
1727  }
1728  else
1729  {
1730  fputc('\n', msg->fp); /* finish off the header */
1731  rc = mutt_write_mime_body(e->body, msg->fp, sub);
1732  }
1733 
1734  if (mx_msg_commit(m_fcc, msg) != 0)
1735  rc = -1;
1736  else if (finalpath)
1737  *finalpath = mutt_str_dup(msg->committed_path);
1738  mx_msg_close(m_fcc, &msg);
1739  mx_mbox_close(m_fcc);
1740 
1741  if (!post && need_mailbox_cleanup)
1742  mutt_mailbox_cleanup(path, &st);
1743 
1744  if (post)
1745  set_noconv_flags(e->body, false);
1746 
1747 done:
1748  m_fcc->append = old_append;
1749  if (m_fcc->flags == MB_HIDDEN)
1750  mailbox_free(&m_fcc);
1751 
1752 #ifdef RECORD_FOLDER_HOOK
1753  /* We ran a folder hook for the destination mailbox,
1754  * now we run it for the user's current mailbox */
1755  const struct Mailbox *m = ctx_mailbox(Context);
1756  if (m)
1757  mutt_folder_hook(m->path, m->desc);
1758 #endif
1759 
1760  if (fp_tmp)
1761  {
1762  mutt_file_fclose(&fp_tmp);
1763  unlink(mutt_buffer_string(tempfile));
1764  }
1765  mutt_buffer_pool_release(&tempfile);
1766 
1767  return rc;
1768 }
struct Mailbox * ctx_mailbox(struct Context *ctx)
Wrapper to get the mailbox in a Context, or NULL.
Definition: context.c:444
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition: crypt.c:1104
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
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
@ MUTT_WRITE_HEADER_FCC
fcc mode, like normal mode but for Bcc header
Definition: header.h:41
@ MUTT_WRITE_HEADER_POSTPONE
A postponed Email, just the envelope info.
Definition: header.h:42
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:528
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
#define MB_HIDDEN
Definition: mailbox.h:38
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:49
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:48
void mutt_mailbox_cleanup(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Definition: mutt_mailbox.c:430
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:304
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1671
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1168
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1057
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:613
uint8_t MsgOpenFlags
Flags for mx_msg_open_new(), e.g. MUTT_ADD_FROM.
Definition: mx.h:40
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:42
#define MUTT_SET_DRAFT
set the message draft flag
Definition: mx.h:43
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:62
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:64
#define SEC_INLINE
Email has an inline signature.
Definition: lib.h:82
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:84
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:83
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition: lib.h:85
#define SEC_SIGN
Email is signed.
Definition: lib.h:76
#define STAILQ_EMPTY(head)
Definition: queue.h:348
The "current" mailbox.
Definition: context.h:38
bool read
Email is read.
Definition: email.h:48
struct ListHead chain
Mixmaster chain.
Definition: email.h:90
A mailbox.
Definition: mailbox.h:82
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
uint8_t flags
e.g. MB_NORMAL
Definition: mailbox.h:134
struct ConfigSubset * sub
Inherited config items.
Definition: mailbox.h:86
char * committed_path
the final path generated by mx_msg_commit()
Definition: mxapi.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function: