NeoMutt  2021-10-22-8-g9cb437
Teaching an old dog new tricks
DOXYGEN
sendlib.c
Go to the documentation of this file.
1 
31 #include "config.h"
32 #include <errno.h>
33 #include <iconv.h>
34 #include <inttypes.h> // IWYU pragma: keep
35 #include <limits.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <time.h>
41 #include <unistd.h>
42 #include "mutt/lib.h"
43 #include "address/lib.h"
44 #include "config/lib.h"
45 #include "email/lib.h"
46 #include "core/lib.h"
47 #include "mutt.h"
48 #include "sendlib.h"
49 #include "lib.h"
50 #include "attach/lib.h"
51 #include "ncrypt/lib.h"
52 #include "copy.h"
53 #include "handler.h"
54 #include "mutt_globals.h"
55 #include "mutt_mailbox.h"
56 #include "muttlib.h"
57 #include "mx.h"
58 #include "options.h"
59 
64 {
65  bool from;
67  bool dot;
68  int linelen;
69  bool was_cr;
70 };
71 
79 static void update_content_info(struct Content *info, struct ContentState *s,
80  char *buf, size_t buflen)
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 }
200 
226 static size_t convert_file_to(FILE *fp, const char *fromcode, int ncodes,
227  char const *const *tocodes, int *tocode, struct Content *info)
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 }
357 
379 static size_t convert_file_from_to(FILE *fp, const char *fromcodes, const char *tocodes,
380  char **fromcode, char **tocode, struct Content *info)
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 }
455 
465 struct Content *mutt_get_content_info(const char *fname, struct Body *b,
466  struct ConfigSubset *sub)
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 }
551 
564 enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
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 }
677 
684 static void transform_to_7bit(struct Body *a, FILE *fp_in, struct ConfigSubset *sub)
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 }
740 
747 void mutt_message_to_7bit(struct Body *a, FILE *fp, struct ConfigSubset *sub)
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 }
831 
838 static void set_encoding(struct Body *b, struct Content *info, struct ConfigSubset *sub)
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 }
891 
897 {
898  a->stamp = mutt_date_epoch();
899 }
900 
908 void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
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 }
930 
940 struct Body *mutt_make_message_attach(struct Mailbox *m, struct Email *e,
941  bool attach_msg, struct ConfigSubset *sub)
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 }
1043 
1051 static void run_mime_type_query(struct Body *att, struct ConfigSubset *sub)
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 }
1085 
1093 struct Body *mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
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 }
1147 
1155 static void encode_headers(struct ListHead *h, struct ConfigSubset *sub)
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 }
1185 
1195 const char *mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
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 }
1217 
1224 static char *gen_msgid(struct ConfigSubset *sub)
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 }
1240 
1251 void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
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 }
1282 
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 }
1303 
1316 static int bounce_message(FILE *fp, struct Mailbox *m, struct Email *e,
1317  struct AddressList *to, const char *resent_from,
1318  struct AddressList *env_from, struct ConfigSubset *sub)
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 }
1381 
1392 int mutt_bounce_message(FILE *fp, struct Mailbox *m, struct Email *e,
1393  struct AddressList *to, struct ConfigSubset *sub)
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 }
1446 
1452 static void set_noconv_flags(struct Body *b, bool flag)
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 }
1467 
1480 int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid, bool post,
1481  char *fcc, char **finalpath, struct ConfigSubset *sub)
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 }
1515 
1528 int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post,
1529  const char *fcc, char **finalpath, struct ConfigSubset *sub)
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 }
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
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1470
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
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
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:42
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_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1305
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
Email Address Handling.
GUI display the mailboxes in a side panel.
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:593
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
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
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: mutt_globals.h:51
struct Mailbox * ctx_mailbox(struct Context *ctx)
Wrapper to get the mailbox in a Context, or NULL.
Definition: context.c:444
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
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
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
Duplicate the structure of an entire email.
#define CH_XMIT
Transmitting this message? (Ignore Lines: and Content-Length:)
Definition: copy.h:55
#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 CH_NONEWLINE
Don't output terminating newline after the header.
Definition: copy.h:60
#define CH_WEED_DELIVERED
Weed eventual Delivered-To headers.
Definition: copy.h:65
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:42
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:61
#define CH_NOQFROM
Ignore ">From " line.
Definition: copy.h:67
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
Convenience wrapper for the core headers.
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
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition: crypt.c:1104
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:454
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:378
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
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
#define MUTT_DATE_NOW
Constant representing the 'current time', see: mutt_date_gmtime(), mutt_date_localtime()
Definition: date.h:39
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body's character set.
Definition: body.c:131
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
Structs that make up an email.
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_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_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
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
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
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
static char * chs
Definition: gnupgparse.c:73
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#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
Decide how to display email content.
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
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:761
@ 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
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:528
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
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_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:40
@ 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
ContentType
Content-Type.
Definition: mime.h:30
@ TYPE_OTHER
Unknown Content-Type.
Definition: mime.h:31
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition: charset.c:355
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_ch_is_us_ascii(str)
Definition: charset.h:96
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
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
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
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
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
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
Many unsorted constants and some structs.
#define PATH_MAX
Definition: mutt.h:40
Hundreds of global variables to back the user variables.
char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:52
void mutt_mailbox_cleanup(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Definition: mutt_mailbox.c:430
Mailbox helper functions.
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:122
Some miscellaneous functions.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1189
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:304
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1143
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
API for mailboxes.
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
API for encryption/signing of emails.
#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
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:71
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:83
#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
#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
Handling of global boolean variables.
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:51
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
void mutt_param_delete(struct ParameterList *pl, const char *attribute)
Delete a matching Parameter.
Definition: parameter.c:142
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1168
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:425
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:324
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
Parse a Message/RFC822 body.
Definition: parse.c:1736
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
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_EMPTY(head)
Definition: queue.h:348
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
#define TAILQ_EMPTY(head)
Definition: queue.h:721
void mutt_rand_base32(char *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition: random.c:103
void rfc2047_encode(char **pd, const char *specials, int col, const char *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:619
void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
Encode any RFC2047 headers, where required, in an Address list.
Definition: rfc2047.c:745
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
void rfc2047_encode_envelope(struct Envelope *env)
Encode the fields of an Envelope.
Definition: rfc2047.c:810
int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:314
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition: send.c:1438
void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
Set followup-to field.
Definition: send.c:1313
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:896
enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:564
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
static void set_noconv_flags(struct Body *b, bool flag)
Set/reset the "x-mutt-noconv" flag.
Definition: sendlib.c:1452
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:1195
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition: sendlib.c:1093
static char * gen_msgid(struct ConfigSubset *sub)
Generate a unique Message ID.
Definition: sendlib.c:1224
int mutt_bounce_message(FILE *fp, struct Mailbox *m, struct Email *e, struct AddressList *to, struct ConfigSubset *sub)
Bounce an email message.
Definition: sendlib.c:1392
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
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
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.
Definition: sendlib.c:1480
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
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition: sendlib.c:1290
static void encode_headers(struct ListHead *h, struct ConfigSubset *sub)
RFC2047-encode a list of headers.
Definition: sendlib.c:1155
static void set_encoding(struct Body *b, struct Content *info, struct ConfigSubset *sub)
Determine which Content-Transfer-Encoding to use.
Definition: sendlib.c:838
void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
Prepare an email header.
Definition: sendlib.c:1251
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
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg, struct ConfigSubset *sub)
Create a message attachment.
Definition: sendlib.c:940
static void transform_to_7bit(struct Body *a, FILE *fp_in, struct ConfigSubset *sub)
Convert MIME parts to 7-bit.
Definition: sendlib.c:684
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
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:908
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
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
Miscellaneous functions for sending an email.
#define MUTT_RANDTAG_LEN
Definition: sendlib.h:35
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
Key value store.
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
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
char * personal
Real name of address.
Definition: address.h:37
The body of an email.
Definition: body.h:35
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
LOFF_T offset
offset where the actual data begins
Definition: body.h:51
char * xtype
content-type if x-unknown
Definition: body.h:60
bool noconv
Don't do character set conversion.
Definition: body.h:45
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:66
time_t stamp
Time stamp of last encoding update.
Definition: body.h:75
LOFF_T length
length (in bytes) of attachment
Definition: body.h:52
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
struct Email * email
header information for message/rfc822
Definition: body.h:72
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:41
struct Content * content
Detailed info about the content of the attachment.
Definition: body.h:68
struct Body * next
next attachment in the list
Definition: body.h:70
bool force_charset
Send mode: don't adjust the character set when in send-mode.
Definition: body.h:43
char * subtype
content-type subtype
Definition: body.h:59
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:40
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
String manipulation buffer.
Definition: buffer.h:34
A set of inherited config items.
Definition: subset.h:47
Info about the body of an email.
Definition: sendlib.c:64
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
Info about an attachment.
Definition: content.h:34
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
The "current" mailbox.
Definition: context.h:38
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
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
struct Body * body
List of MIME parts.
Definition: email.h:67
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
struct ListHead chain
Mixmaster chain.
Definition: email.h:90
int msgno
Number displayed to the user.
Definition: email.h:111
The header of an Email.
Definition: envelope.h:55
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 mail_followup_to
Email's 'mail-followup-to'.
Definition: envelope.h:63
struct AddressList cc
Email's 'Cc' list.
Definition: envelope.h:59
struct AddressList bcc
Email's 'Bcc' list.
Definition: envelope.h:60
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
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
A local copy of an email.
Definition: mxapi.h:42
FILE * fp
pointer to the message data
Definition: mxapi.h:43
char * committed_path
the final path generated by mx_msg_commit()
Definition: mxapi.h:45
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