NeoMutt  2020-06-26-30-g76c339
Teaching an old dog new tricks
DOXYGEN
sendlib.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <iconv.h>
34 #include <inttypes.h> // IWYU pragma: keep
35 #include <limits.h>
36 #include <signal.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43 #include <time.h>
44 #include <unistd.h>
45 #include "mutt/lib.h"
46 #include "address/lib.h"
47 #include "email/lib.h"
48 #include "core/lib.h"
49 #include "gui/lib.h"
50 #include "mutt.h"
51 #include "sendlib.h"
52 #include "context.h"
53 #include "copy.h"
54 #include "format_flags.h"
55 #include "globals.h"
56 #include "handler.h"
57 #include "mutt_mailbox.h"
58 #include "mutt_parse.h"
59 #include "muttlib.h"
60 #include "mx.h"
61 #include "options.h"
62 #include "pager.h"
63 #include "send.h"
64 #include "smtp.h"
65 #include "state.h"
66 #include "ncrypt/lib.h"
67 #ifdef USE_NNTP
68 #include "nntp/lib.h"
69 #endif
70 #ifdef HAVE_SYSEXITS_H
71 #include <sysexits.h>
72 #else
73 #define EX_OK 0
74 #endif
75 #ifdef USE_AUTOCRYPT
76 #include "autocrypt/lib.h"
77 #endif
78 
79 /* These Config Variables are only used in sendlib.c */
86 char *C_Inews;
91 char *C_Sendmail;
97 
98 #define MUTT_RANDTAG_LEN 16
99 
104 {
105  char buffer[3];
106  short size;
107  short linelen;
108 };
109 
114 {
115  bool from;
117  bool dot;
118  int linelen;
119  bool was_cr;
120 };
121 
127 static const char *const userhdrs_override_headers[] = {
128  "content-type:",
129  "user-agent:",
130 };
131 
136 {
139 };
140 
145 {
147 };
148 
155 static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
156 {
157  int c, linelen = 0;
158  char line[77], savechar;
159 
160  while ((c = mutt_ch_fgetconv(fc)) != EOF)
161  {
162  /* Wrap the line if needed. */
163  if ((linelen == 76) && ((istext && (c != '\n')) || !istext))
164  {
165  /* If the last character is "quoted", then be sure to move all three
166  * characters to the next line. Otherwise, just move the last
167  * character... */
168  if (line[linelen - 3] == '=')
169  {
170  line[linelen - 3] = 0;
171  fputs(line, fp_out);
172  fputs("=\n", fp_out);
173  line[linelen] = 0;
174  line[0] = '=';
175  line[1] = line[linelen - 2];
176  line[2] = line[linelen - 1];
177  linelen = 3;
178  }
179  else
180  {
181  savechar = line[linelen - 1];
182  line[linelen - 1] = '=';
183  line[linelen] = 0;
184  fputs(line, fp_out);
185  fputc('\n', fp_out);
186  line[0] = savechar;
187  linelen = 1;
188  }
189  }
190 
191  /* Escape lines that begin with/only contain "the message separator". */
192  if ((linelen == 4) && mutt_str_startswith(line, "From"))
193  {
194  mutt_str_copy(line, "=46rom", sizeof(line));
195  linelen = 6;
196  }
197  else if ((linelen == 4) && mutt_str_startswith(line, "from"))
198  {
199  mutt_str_copy(line, "=66rom", sizeof(line));
200  linelen = 6;
201  }
202  else if ((linelen == 1) && (line[0] == '.'))
203  {
204  mutt_str_copy(line, "=2E", sizeof(line));
205  linelen = 3;
206  }
207 
208  if ((c == '\n') && istext)
209  {
210  /* Check to make sure there is no trailing space on this line. */
211  if ((linelen > 0) && ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t')))
212  {
213  if (linelen < 74)
214  {
215  sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
216  fputs(line, fp_out);
217  }
218  else
219  {
220  int savechar2 = line[linelen - 1];
221 
222  line[linelen - 1] = '=';
223  line[linelen] = 0;
224  fputs(line, fp_out);
225  fprintf(fp_out, "\n=%2.2X", (unsigned char) savechar2);
226  }
227  }
228  else
229  {
230  line[linelen] = 0;
231  fputs(line, fp_out);
232  }
233  fputc('\n', fp_out);
234  linelen = 0;
235  }
236  else if ((c != 9) && ((c < 32) || (c > 126) || (c == '=')))
237  {
238  /* Check to make sure there is enough room for the quoted character.
239  * If not, wrap to the next line. */
240  if (linelen > 73)
241  {
242  line[linelen++] = '=';
243  line[linelen] = 0;
244  fputs(line, fp_out);
245  fputc('\n', fp_out);
246  linelen = 0;
247  }
248  sprintf(line + linelen, "=%2.2X", (unsigned char) c);
249  linelen += 3;
250  }
251  else
252  {
253  /* Don't worry about wrapping the line here. That will happen during
254  * the next iteration when I'll also know what the next character is. */
255  line[linelen++] = c;
256  }
257  }
258 
259  /* Take care of anything left in the buffer */
260  if (linelen > 0)
261  {
262  if ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t'))
263  {
264  /* take care of trailing whitespace */
265  if (linelen < 74)
266  sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
267  else
268  {
269  savechar = line[linelen - 1];
270  line[linelen - 1] = '=';
271  line[linelen] = 0;
272  fputs(line, fp_out);
273  fputc('\n', fp_out);
274  sprintf(line, "=%2.2X", (unsigned char) savechar);
275  }
276  }
277  else
278  line[linelen] = 0;
279  fputs(line, fp_out);
280  }
281 }
282 
288 static int b64_init(struct B64Context *bctx)
289 {
290  memset(bctx->buffer, '\0', sizeof(bctx->buffer));
291  bctx->size = 0;
292  bctx->linelen = 0;
293 
294  return 0;
295 }
296 
302 static void b64_flush(struct B64Context *bctx, FILE *fp_out)
303 {
304  /* for some reasons, mutt_b64_encode expects the
305  * output buffer to be larger than 10B */
306  char encoded[11];
307  size_t ret;
308 
309  if (bctx->size == 0)
310  return;
311 
312  if (bctx->linelen >= 72)
313  {
314  fputc('\n', fp_out);
315  bctx->linelen = 0;
316  }
317 
318  /* ret should always be equal to 4 here, because bctx->size
319  * is a value between 1 and 3 (included), but let's not hardcode it
320  * and prefer the return value of the function */
321  ret = mutt_b64_encode(bctx->buffer, bctx->size, encoded, sizeof(encoded));
322  for (size_t i = 0; i < ret; i++)
323  {
324  fputc(encoded[i], fp_out);
325  bctx->linelen++;
326  }
327 
328  bctx->size = 0;
329 }
330 
337 static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
338 {
339  if (bctx->size == 3)
340  b64_flush(bctx, fp_out);
341 
342  bctx->buffer[bctx->size++] = c;
343 }
344 
351 static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
352 {
353  struct B64Context bctx;
354  int ch, ch1 = EOF;
355 
356  b64_init(&bctx);
357 
358  while ((ch = mutt_ch_fgetconv(fc)) != EOF)
359  {
360  if (SigInt == 1)
361  {
362  SigInt = 0;
363  return;
364  }
365  if (istext && (ch == '\n') && (ch1 != '\r'))
366  b64_putc(&bctx, '\r', fp_out);
367  b64_putc(&bctx, ch, fp_out);
368  ch1 = ch;
369  }
370  b64_flush(&bctx, fp_out);
371  fputc('\n', fp_out);
372 }
373 
379 static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
380 {
381  int ch;
382 
383  while ((ch = mutt_ch_fgetconv(fc)) != EOF)
384  {
385  if (SigInt == 1)
386  {
387  SigInt = 0;
388  return;
389  }
390  fputc(ch, fp_out);
391  }
392 }
393 
401 int mutt_write_mime_header(struct Body *a, FILE *fp)
402 {
403  if (!a || !fp)
404  return -1;
405 
406  int len;
407  int tmplen;
408  char buf[256] = { 0 };
409 
410  fprintf(fp, "Content-Type: %s/%s", TYPE(a), a->subtype);
411 
412  if (!TAILQ_EMPTY(&a->parameter))
413  {
414  len = 25 + mutt_str_len(a->subtype); /* approximate len. of content-type */
415 
416  struct Parameter *np = NULL;
417  TAILQ_FOREACH(np, &a->parameter, entries)
418  {
419  if (!np->attribute || !np->value)
420  continue;
421 
422  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
423  rfc2231_encode_string(&pl_conts, np->attribute, np->value);
424  struct Parameter *cont = NULL;
425  TAILQ_FOREACH(cont, &pl_conts, entries)
426  {
427  fputc(';', fp);
428 
429  buf[0] = 0;
430  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
431 
432  /* Dirty hack to make messages readable by Outlook Express
433  * for the Mac: force quotes around the boundary parameter
434  * even when they aren't needed. */
435  if (mutt_istr_equal(cont->attribute, "boundary") && mutt_str_equal(buf, cont->value))
436  snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
437 
438  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
439  if (len + tmplen + 2 > 76)
440  {
441  fputs("\n\t", fp);
442  len = tmplen + 1;
443  }
444  else
445  {
446  fputc(' ', fp);
447  len += tmplen + 1;
448  }
449 
450  fprintf(fp, "%s=%s", cont->attribute, buf);
451  }
452 
453  mutt_param_free(&pl_conts);
454  }
455  }
456 
457  fputc('\n', fp);
458 
459  if (a->language)
460  fprintf(fp, "Content-Language: %s\n", a->language);
461 
462  if (a->description)
463  fprintf(fp, "Content-Description: %s\n", a->description);
464 
465  if (a->disposition != DISP_NONE)
466  {
467  const char *dispstr[] = { "inline", "attachment", "form-data" };
468 
469  if (a->disposition < sizeof(dispstr) / sizeof(char *))
470  {
471  fprintf(fp, "Content-Disposition: %s", dispstr[a->disposition]);
472  len = 21 + mutt_str_len(dispstr[a->disposition]);
473 
474  if (a->use_disp && (a->disposition != DISP_INLINE))
475  {
476  char *fn = a->d_filename;
477  if (!fn)
478  fn = a->filename;
479 
480  if (fn)
481  {
482  /* Strip off the leading path... */
483  char *t = strrchr(fn, '/');
484  if (t)
485  t++;
486  else
487  t = fn;
488 
489  struct ParameterList pl_conts = TAILQ_HEAD_INITIALIZER(pl_conts);
490  rfc2231_encode_string(&pl_conts, "filename", t);
491  struct Parameter *cont = NULL;
492  TAILQ_FOREACH(cont, &pl_conts, entries)
493  {
494  fputc(';', fp);
495  buf[0] = 0;
496  mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
497 
498  tmplen = mutt_str_len(buf) + mutt_str_len(cont->attribute) + 1;
499  if (len + tmplen + 2 > 76)
500  {
501  fputs("\n\t", fp);
502  len = tmplen + 1;
503  }
504  else
505  {
506  fputc(' ', fp);
507  len += tmplen + 1;
508  }
509 
510  fprintf(fp, "%s=%s", cont->attribute, buf);
511  }
512 
513  mutt_param_free(&pl_conts);
514  }
515  }
516 
517  fputc('\n', fp);
518  }
519  else
520  {
521  mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n", a->disposition);
522  }
523  }
524 
525  if (a->encoding != ENC_7BIT)
526  fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding));
527 
529 #ifdef USE_AUTOCRYPT
530  || C_Autocrypt
531 #endif
532  ) &&
533  a->mime_headers)
534  {
535  mutt_rfc822_write_header(fp, a->mime_headers, NULL, MUTT_WRITE_HEADER_MIME, false, false);
536  }
537 
538  /* Do NOT add the terminator here!!! */
539  return ferror(fp) ? -1 : 0;
540 }
541 
547 static bool write_as_text_part(struct Body *b)
548 {
549  return mutt_is_text_part(b) ||
551 }
552 
560 int mutt_write_mime_body(struct Body *a, FILE *fp)
561 {
562  FILE *fp_in = NULL;
563  struct FgetConv *fc = NULL;
564 
565  if (a->type == TYPE_MULTIPART)
566  {
567  /* First, find the boundary to use */
568  const char *p = mutt_param_get(&a->parameter, "boundary");
569  if (!p)
570  {
571  mutt_debug(LL_DEBUG1, "no boundary parameter found\n");
572  mutt_error(_("No boundary parameter found [report this error]"));
573  return -1;
574  }
575  char boundary[128];
576  mutt_str_copy(boundary, p, sizeof(boundary));
577 
578  for (struct Body *t = a->parts; t; t = t->next)
579  {
580  fprintf(fp, "\n--%s\n", boundary);
581  if (mutt_write_mime_header(t, fp) == -1)
582  return -1;
583  fputc('\n', fp);
584  if (mutt_write_mime_body(t, fp) == -1)
585  return -1;
586  }
587  fprintf(fp, "\n--%s--\n", boundary);
588  return ferror(fp) ? -1 : 0;
589  }
590 
591  /* This is pretty gross, but it's the best solution for now... */
592  if (((WithCrypto & APPLICATION_PGP) != 0) && (a->type == TYPE_APPLICATION) &&
593  mutt_str_equal(a->subtype, "pgp-encrypted") && !a->filename)
594  {
595  fputs("Version: 1\n", fp);
596  return 0;
597  }
598 
599  fp_in = fopen(a->filename, "r");
600  if (!fp_in)
601  {
602  mutt_debug(LL_DEBUG1, "%s no longer exists\n", a->filename);
603  mutt_error(_("%s no longer exists"), a->filename);
604  return -1;
605  }
606 
607  if ((a->type == TYPE_TEXT) && (!a->noconv))
608  {
609  char send_charset[128];
611  fp_in, a->charset,
612  mutt_body_get_charset(a, send_charset, sizeof(send_charset)), 0);
613  }
614  else
615  fc = mutt_ch_fgetconv_open(fp_in, 0, 0, 0);
616 
618  if (a->encoding == ENC_QUOTED_PRINTABLE)
619  encode_quoted(fc, fp, write_as_text_part(a));
620  else if (a->encoding == ENC_BASE64)
621  encode_base64(fc, fp, write_as_text_part(a));
622  else if ((a->type == TYPE_TEXT) && (!a->noconv))
623  encode_8bit(fc, fp);
624  else
625  mutt_file_copy_stream(fp_in, fp);
627 
629  mutt_file_fclose(&fp_in);
630 
631  if (SigInt == 1)
632  {
633  SigInt = 0;
634  return -1;
635  }
636  return ferror(fp) ? -1 : 0;
637 }
638 
643 void mutt_generate_boundary(struct ParameterList *pl)
644 {
645  char rs[MUTT_RANDTAG_LEN + 1];
646 
647  mutt_rand_base32(rs, sizeof(rs) - 1);
648  rs[MUTT_RANDTAG_LEN] = 0;
649  mutt_param_set(pl, "boundary", rs);
650 }
651 
659 static void update_content_info(struct Content *info, struct ContentState *s,
660  char *buf, size_t buflen)
661 {
662  bool from = s->from;
663  int whitespace = s->whitespace;
664  bool dot = s->dot;
665  int linelen = s->linelen;
666  bool was_cr = s->was_cr;
667 
668  if (!buf) /* This signals EOF */
669  {
670  if (was_cr)
671  info->binary = true;
672  if (linelen > info->linemax)
673  info->linemax = linelen;
674 
675  return;
676  }
677 
678  for (; buflen; buf++, buflen--)
679  {
680  char ch = *buf;
681 
682  if (was_cr)
683  {
684  was_cr = false;
685  if (ch == '\n')
686  {
687  if (whitespace)
688  info->space = true;
689  if (dot)
690  info->dot = true;
691  if (linelen > info->linemax)
692  info->linemax = linelen;
693  whitespace = 0;
694  dot = false;
695  linelen = 0;
696  continue;
697  }
698 
699  info->binary = true;
700  }
701 
702  linelen++;
703  if (ch == '\n')
704  {
705  info->crlf++;
706  if (whitespace)
707  info->space = true;
708  if (dot)
709  info->dot = true;
710  if (linelen > info->linemax)
711  info->linemax = linelen;
712  whitespace = 0;
713  linelen = 0;
714  dot = false;
715  }
716  else if (ch == '\r')
717  {
718  info->crlf++;
719  info->cr = true;
720  was_cr = true;
721  continue;
722  }
723  else if (ch & 0x80)
724  info->hibin++;
725  else if ((ch == '\t') || (ch == '\f'))
726  {
727  info->ascii++;
728  whitespace++;
729  }
730  else if (ch == 0)
731  {
732  info->nulbin++;
733  info->lobin++;
734  }
735  else if ((ch < 32) || (ch == 127))
736  info->lobin++;
737  else
738  {
739  if (linelen == 1)
740  {
741  if ((ch == 'F') || (ch == 'f'))
742  from = true;
743  else
744  from = false;
745  if (ch == '.')
746  dot = true;
747  else
748  dot = false;
749  }
750  else if (from)
751  {
752  if ((linelen == 2) && (ch != 'r'))
753  from = false;
754  else if ((linelen == 3) && (ch != 'o'))
755  from = false;
756  else if (linelen == 4)
757  {
758  if (ch == 'm')
759  info->from = true;
760  from = false;
761  }
762  }
763  if (ch == ' ')
764  whitespace++;
765  info->ascii++;
766  }
767 
768  if (linelen > 1)
769  dot = false;
770  if ((ch != ' ') && (ch != '\t'))
771  whitespace = 0;
772  }
773 
774  s->from = from;
775  s->whitespace = whitespace;
776  s->dot = dot;
777  s->linelen = linelen;
778  s->was_cr = was_cr;
779 }
780 
806 static size_t convert_file_to(FILE *fp, const char *fromcode, int ncodes,
807  char const *const *tocodes, int *tocode, struct Content *info)
808 {
809  char bufi[256], bufu[512], bufo[4 * sizeof(bufi)];
810  size_t ret;
811 
812  const iconv_t cd1 = mutt_ch_iconv_open("utf-8", fromcode, 0);
813  if (cd1 == (iconv_t)(-1))
814  return -1;
815 
816  iconv_t *cd = mutt_mem_calloc(ncodes, sizeof(iconv_t));
817  size_t *score = mutt_mem_calloc(ncodes, sizeof(size_t));
818  struct ContentState *states = mutt_mem_calloc(ncodes, sizeof(struct ContentState));
819  struct Content *infos = mutt_mem_calloc(ncodes, sizeof(struct Content));
820 
821  for (int i = 0; i < ncodes; i++)
822  {
823  if (!mutt_istr_equal(tocodes[i], "utf-8"))
824  cd[i] = mutt_ch_iconv_open(tocodes[i], "utf-8", 0);
825  else
826  {
827  /* Special case for conversion to UTF-8 */
828  cd[i] = (iconv_t)(-1);
829  score[i] = (size_t)(-1);
830  }
831  }
832 
833  rewind(fp);
834  size_t ibl = 0;
835  while (true)
836  {
837  /* Try to fill input buffer */
838  size_t n = fread(bufi + ibl, 1, sizeof(bufi) - ibl, fp);
839  ibl += n;
840 
841  /* Convert to UTF-8 */
842  const char *ib = bufi;
843  char *ob = bufu;
844  size_t obl = sizeof(bufu);
845  n = iconv(cd1, (ICONV_CONST char **) ((ibl != 0) ? &ib : 0), &ibl, &ob, &obl);
846  /* assert(n == (size_t)(-1) || !n); */
847  if ((n == (size_t)(-1)) && (((errno != EINVAL) && (errno != E2BIG)) || (ib == bufi)))
848  {
849  /* assert(errno == EILSEQ || (errno == EINVAL && ib == bufi && ibl < sizeof(bufi))); */
850  ret = (size_t)(-1);
851  break;
852  }
853  const size_t ubl1 = ob - bufu;
854 
855  /* Convert from UTF-8 */
856  for (int i = 0; i < ncodes; i++)
857  {
858  if ((cd[i] != (iconv_t)(-1)) && (score[i] != (size_t)(-1)))
859  {
860  const char *ub = bufu;
861  size_t ubl = ubl1;
862  ob = bufo;
863  obl = sizeof(bufo);
864  n = iconv(cd[i], (ICONV_CONST char **) ((ibl || ubl) ? &ub : 0), &ubl, &ob, &obl);
865  if (n == (size_t)(-1))
866  {
867  /* assert(errno == E2BIG || (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT))); */
868  score[i] = (size_t)(-1);
869  }
870  else
871  {
872  score[i] += n;
873  update_content_info(&infos[i], &states[i], bufo, ob - bufo);
874  }
875  }
876  else if ((cd[i] == (iconv_t)(-1)) && (score[i] == (size_t)(-1)))
877  {
878  /* Special case for conversion to UTF-8 */
879  update_content_info(&infos[i], &states[i], bufu, ubl1);
880  }
881  }
882 
883  if (ibl)
884  {
885  /* Save unused input */
886  memmove(bufi, ib, ibl);
887  }
888  else if (!ubl1 && (ib < bufi + sizeof(bufi)))
889  {
890  ret = 0;
891  break;
892  }
893  }
894 
895  if (ret == 0)
896  {
897  /* Find best score */
898  ret = (size_t)(-1);
899  for (int i = 0; i < ncodes; i++)
900  {
901  if ((cd[i] == (iconv_t)(-1)) && (score[i] == (size_t)(-1)))
902  {
903  /* Special case for conversion to UTF-8 */
904  *tocode = i;
905  ret = 0;
906  break;
907  }
908  else if ((cd[i] == (iconv_t)(-1)) || (score[i] == (size_t)(-1)))
909  continue;
910  else if ((ret == (size_t)(-1)) || (score[i] < ret))
911  {
912  *tocode = i;
913  ret = score[i];
914  if (ret == 0)
915  break;
916  }
917  }
918  if (ret != (size_t)(-1))
919  {
920  memcpy(info, &infos[*tocode], sizeof(struct Content));
921  update_content_info(info, &states[*tocode], 0, 0); /* EOF */
922  }
923  }
924 
925  for (int i = 0; i < ncodes; i++)
926  if (cd[i] != (iconv_t)(-1))
927  iconv_close(cd[i]);
928 
929  iconv_close(cd1);
930  FREE(&cd);
931  FREE(&infos);
932  FREE(&score);
933  FREE(&states);
934 
935  return ret;
936 }
937 
959 static size_t convert_file_from_to(FILE *fp, const char *fromcodes, const char *tocodes,
960  char **fromcode, char **tocode, struct Content *info)
961 {
962  char *fcode = NULL;
963  char **tcode = NULL;
964  const char *c = NULL, *c1 = NULL;
965  size_t ret;
966  int ncodes, i, cn;
967 
968  /* Count the tocodes */
969  ncodes = 0;
970  for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
971  {
972  c1 = strchr(c, ':');
973  if (c1 == c)
974  continue;
975  ncodes++;
976  }
977 
978  /* Copy them */
979  tcode = mutt_mem_malloc(ncodes * sizeof(char *));
980  for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
981  {
982  c1 = strchr(c, ':');
983  if (c1 == c)
984  continue;
985  tcode[i] = mutt_strn_dup(c, c1 - c);
986  }
987 
988  ret = (size_t)(-1);
989  if (fromcode)
990  {
991  /* Try each fromcode in turn */
992  for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
993  {
994  c1 = strchr(c, ':');
995  if (c1 == c)
996  continue;
997  fcode = mutt_strn_dup(c, c1 - c);
998 
999  ret = convert_file_to(fp, fcode, ncodes, (char const *const *) tcode, &cn, info);
1000  if (ret != (size_t)(-1))
1001  {
1002  *fromcode = fcode;
1003  *tocode = tcode[cn];
1004  tcode[cn] = 0;
1005  break;
1006  }
1007  FREE(&fcode);
1008  }
1009  }
1010  else
1011  {
1012  /* There is only one fromcode */
1013  ret = convert_file_to(fp, fromcodes, ncodes, (char const *const *) tcode, &cn, info);
1014  if (ret != (size_t)(-1))
1015  {
1016  *tocode = tcode[cn];
1017  tcode[cn] = 0;
1018  }
1019  }
1020 
1021  /* Free memory */
1022  for (i = 0; i < ncodes; i++)
1023  FREE(&tcode[i]);
1024 
1025  FREE(&tcode);
1026 
1027  return ret;
1028 }
1029 
1038 struct Content *mutt_get_content_info(const char *fname, struct Body *b)
1039 {
1040  struct Content *info = NULL;
1041  struct ContentState state = { 0 };
1042  FILE *fp = NULL;
1043  char *fromcode = NULL;
1044  char *tocode = NULL;
1045  char buf[100];
1046  size_t r;
1047 
1048  struct stat sb;
1049 
1050  if (b && !fname)
1051  fname = b->filename;
1052 
1053  if (stat(fname, &sb) == -1)
1054  {
1055  mutt_error(_("Can't stat %s: %s"), fname, strerror(errno));
1056  return NULL;
1057  }
1058 
1059  if (!S_ISREG(sb.st_mode))
1060  {
1061  mutt_error(_("%s isn't a regular file"), fname);
1062  return NULL;
1063  }
1064 
1065  fp = fopen(fname, "r");
1066  if (!fp)
1067  {
1068  mutt_debug(LL_DEBUG1, "%s: %s (errno %d)\n", fname, strerror(errno), errno);
1069  return NULL;
1070  }
1071 
1072  info = mutt_mem_calloc(1, sizeof(struct Content));
1073 
1074  if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
1075  {
1076  char *chs = mutt_param_get(&b->parameter, "charset");
1077  char *fchs = b->use_disp ? (C_AttachCharset ? C_AttachCharset : C_Charset) : C_Charset;
1078  if (C_Charset && (chs || C_SendCharset) &&
1079  (convert_file_from_to(fp, fchs, chs ? chs : C_SendCharset, &fromcode,
1080  &tocode, info) != (size_t)(-1)))
1081  {
1082  if (!chs)
1083  {
1084  char chsbuf[256];
1085  mutt_ch_canonical_charset(chsbuf, sizeof(chsbuf), tocode);
1086  mutt_param_set(&b->parameter, "charset", chsbuf);
1087  }
1088  FREE(&b->charset);
1089  b->charset = fromcode;
1090  FREE(&tocode);
1091  mutt_file_fclose(&fp);
1092  return info;
1093  }
1094  }
1095 
1096  rewind(fp);
1097  while ((r = fread(buf, 1, sizeof(buf), fp)))
1098  update_content_info(info, &state, buf, r);
1099  update_content_info(info, &state, 0, 0);
1100 
1101  mutt_file_fclose(&fp);
1102 
1103  if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
1104  {
1105  mutt_param_set(&b->parameter, "charset",
1106  (!info->hibin ?
1107  "us-ascii" :
1108  C_Charset && !mutt_ch_is_us_ascii(C_Charset) ? C_Charset : "unknown-8bit"));
1109  }
1110 
1111  return info;
1112 }
1113 
1126 enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
1127 {
1128  FILE *fp = NULL;
1129  char *p = NULL, *q = NULL, *ct = NULL;
1130  char buf[PATH_MAX];
1131  char subtype[256] = { 0 };
1132  char xtype[256] = { 0 };
1133  int sze, cur_sze = 0;
1134  bool found_mimetypes = false;
1135  enum ContentType type = TYPE_OTHER;
1136 
1137  int szf = mutt_str_len(path);
1138 
1139  for (int count = 0; count < 4; count++)
1140  {
1141  /* can't use strtok() because we use it in an inner loop below, so use
1142  * a switch statement here instead. */
1143  switch (count)
1144  {
1145  /* last file with last entry to match wins type/xtype */
1146  case 0:
1147  /* check default unix mimetypes location first */
1148  mutt_str_copy(buf, "/etc/mime.types", sizeof(buf));
1149  break;
1150  case 1:
1151  mutt_str_copy(buf, SYSCONFDIR "/mime.types", sizeof(buf));
1152  break;
1153  case 2:
1154  mutt_str_copy(buf, PKGDATADIR "/mime.types", sizeof(buf));
1155  break;
1156  case 3:
1157  snprintf(buf, sizeof(buf), "%s/.mime.types", NONULL(HomeDir));
1158  break;
1159  default:
1160  mutt_debug(LL_DEBUG1, "Internal error, count = %d\n", count);
1161  goto bye; /* shouldn't happen */
1162  }
1163 
1164  fp = fopen(buf, "r");
1165  if (fp)
1166  {
1167  found_mimetypes = true;
1168 
1169  while (fgets(buf, sizeof(buf) - 1, fp))
1170  {
1171  /* weed out any comments */
1172  p = strchr(buf, '#');
1173  if (p)
1174  *p = '\0';
1175 
1176  /* remove any leading space. */
1177  ct = buf;
1178  SKIPWS(ct);
1179 
1180  /* position on the next field in this line */
1181  p = strpbrk(ct, " \t");
1182  if (!p)
1183  continue;
1184  *p++ = 0;
1185  SKIPWS(p);
1186 
1187  /* cycle through the file extensions */
1188  while ((p = strtok(p, " \t\n")))
1189  {
1190  sze = mutt_str_len(p);
1191  if ((sze > cur_sze) && (szf >= sze) && mutt_istr_equal(path + szf - sze, p) &&
1192  ((szf == sze) || (path[szf - sze - 1] == '.')))
1193  {
1194  /* get the content-type */
1195 
1196  p = strchr(ct, '/');
1197  if (!p)
1198  {
1199  /* malformed line, just skip it. */
1200  break;
1201  }
1202  *p++ = 0;
1203 
1204  for (q = p; *q && !IS_SPACE(*q); q++)
1205  ; // do nothing
1206 
1207  mutt_strn_copy(subtype, p, q - p, sizeof(subtype));
1208 
1209  type = mutt_check_mime_type(ct);
1210  if (type == TYPE_OTHER)
1211  mutt_str_copy(xtype, ct, sizeof(xtype));
1212 
1213  cur_sze = sze;
1214  }
1215  p = NULL;
1216  }
1217  }
1218  mutt_file_fclose(&fp);
1219  }
1220  }
1221 
1222 bye:
1223 
1224  /* no mime.types file found */
1225  if (!found_mimetypes)
1226  {
1227  mutt_error(_("Could not find any mime.types file."));
1228  }
1229 
1230  if ((type != TYPE_OTHER) || (*xtype != '\0'))
1231  {
1232  att->type = type;
1233  mutt_str_replace(&att->subtype, subtype);
1234  mutt_str_replace(&att->xtype, xtype);
1235  }
1236 
1237  return type;
1238 }
1239 
1245 static void transform_to_7bit(struct Body *a, FILE *fp_in)
1246 {
1247  struct Buffer *buf = NULL;
1248  struct State s = { 0 };
1249  struct stat sb;
1250 
1251  for (; a; a = a->next)
1252  {
1253  if (a->type == TYPE_MULTIPART)
1254  {
1255  a->encoding = ENC_7BIT;
1256  transform_to_7bit(a->parts, fp_in);
1257  }
1258  else if (mutt_is_message_type(a->type, a->subtype))
1259  {
1260  mutt_message_to_7bit(a, fp_in);
1261  }
1262  else
1263  {
1264  a->noconv = true;
1265  a->force_charset = true;
1266 
1267  /* Because of the potential recursion in message types, we
1268  * restrict the lifetime of the buffer tightly */
1269  buf = mutt_buffer_pool_get();
1270  mutt_buffer_mktemp(buf);
1271  s.fp_out = mutt_file_fopen(mutt_b2s(buf), "w");
1272  if (!s.fp_out)
1273  {
1274  mutt_perror("fopen");
1276  return;
1277  }
1278  s.fp_in = fp_in;
1279  mutt_decode_attachment(a, &s);
1281  FREE(&a->d_filename);
1282  a->d_filename = a->filename;
1283  a->filename = mutt_buffer_strdup(buf);
1285  a->unlink = true;
1286  if (stat(a->filename, &sb) == -1)
1287  {
1288  mutt_perror("stat");
1289  return;
1290  }
1291  a->length = sb.st_size;
1292 
1294  if (a->encoding == ENC_8BIT)
1296  else if (a->encoding == ENC_BINARY)
1297  a->encoding = ENC_BASE64;
1298  }
1299  }
1300 }
1301 
1307 void mutt_message_to_7bit(struct Body *a, FILE *fp)
1308 {
1309  struct Buffer temp = mutt_buffer_make(0);
1310  FILE *fp_in = NULL;
1311  FILE *fp_out = NULL;
1312  struct stat sb;
1313 
1314  if (!a->filename && fp)
1315  fp_in = fp;
1316  else if (!a->filename || !(fp_in = fopen(a->filename, "r")))
1317  {
1318  mutt_error(_("Could not open %s"), a->filename ? a->filename : "(null)");
1319  return;
1320  }
1321  else
1322  {
1323  a->offset = 0;
1324  if (stat(a->filename, &sb) == -1)
1325  {
1326  mutt_perror("stat");
1327  mutt_file_fclose(&fp_in);
1328  goto cleanup;
1329  }
1330  a->length = sb.st_size;
1331  }
1332 
1333  /* Avoid buffer pool due to recursion */
1334  mutt_buffer_mktemp(&temp);
1335  fp_out = mutt_file_fopen(mutt_b2s(&temp), "w+");
1336  if (!fp_out)
1337  {
1338  mutt_perror("fopen");
1339  goto cleanup;
1340  }
1341 
1342  fseeko(fp_in, a->offset, SEEK_SET);
1343  a->parts = mutt_rfc822_parse_message(fp_in, a);
1344 
1345  transform_to_7bit(a->parts, fp_in);
1346 
1347  mutt_copy_hdr(fp_in, fp_out, a->offset, a->offset + a->length,
1348  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL, 0);
1349 
1350  fputs("MIME-Version: 1.0\n", fp_out);
1351  mutt_write_mime_header(a->parts, fp_out);
1352  fputc('\n', fp_out);
1353  mutt_write_mime_body(a->parts, fp_out);
1354 
1355  if (fp_in != fp)
1356  mutt_file_fclose(&fp_in);
1357  mutt_file_fclose(&fp_out);
1358 
1359  a->encoding = ENC_7BIT;
1360  FREE(&a->d_filename);
1361  a->d_filename = a->filename;
1362  if (a->filename && a->unlink)
1363  unlink(a->filename);
1364  a->filename = mutt_buffer_strdup(&temp);
1365  a->unlink = true;
1366  if (stat(a->filename, &sb) == -1)
1367  {
1368  mutt_perror("stat");
1369  goto cleanup;
1370  }
1371  a->length = sb.st_size;
1372  mutt_body_free(&a->parts);
1373  a->email->content = NULL;
1374 
1375 cleanup:
1376  if (fp_in && (fp_in != fp))
1377  mutt_file_fclose(&fp_in);
1378 
1379  if (fp_out)
1380  {
1381  mutt_file_fclose(&fp_out);
1382  mutt_file_unlink(mutt_b2s(&temp));
1383  }
1384 
1385  mutt_buffer_dealloc(&temp);
1386 }
1387 
1393 static void set_encoding(struct Body *b, struct Content *info)
1394 {
1395  if (b->type == TYPE_TEXT)
1396  {
1397  char send_charset[128];
1398  char *chsname = mutt_body_get_charset(b, send_charset, sizeof(send_charset));
1399  if ((info->lobin && !mutt_istr_startswith(chsname, "iso-2022")) ||
1400  (info->linemax > 990) || (info->from && C_EncodeFrom))
1401  {
1403  }
1404  else if (info->hibin)
1405  {
1407  }
1408  else
1409  {
1410  b->encoding = ENC_7BIT;
1411  }
1412  }
1413  else if ((b->type == TYPE_MESSAGE) || (b->type == TYPE_MULTIPART))
1414  {
1415  if (info->lobin || info->hibin)
1416  {
1417  if (C_Allow8bit && !info->lobin)
1418  b->encoding = ENC_8BIT;
1419  else
1420  mutt_message_to_7bit(b, NULL);
1421  }
1422  else
1423  b->encoding = ENC_7BIT;
1424  }
1425  else if ((b->type == TYPE_APPLICATION) &&
1426  mutt_istr_equal(b->subtype, "pgp-keys"))
1427  {
1428  b->encoding = ENC_7BIT;
1429  }
1430  else
1431  {
1432  /* Determine which encoding is smaller */
1433  if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1434  3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1435  {
1436  b->encoding = ENC_BASE64;
1437  }
1438  else
1439  {
1441  }
1442  }
1443 }
1444 
1450 {
1451  a->stamp = mutt_date_epoch();
1452 }
1453 
1462 char *mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
1463 {
1464  char *p = NULL;
1465 
1466  if (b && (b->type != TYPE_TEXT))
1467  return NULL;
1468 
1469  if (b)
1470  p = mutt_param_get(&b->parameter, "charset");
1471 
1472  if (p)
1473  mutt_ch_canonical_charset(buf, buflen, p);
1474  else
1475  mutt_str_copy(buf, "us-ascii", buflen);
1476 
1477  return buf;
1478 }
1479 
1487 {
1488  struct Content *info = NULL;
1489  char chsbuf[256];
1490 
1491  /* override noconv when it's us-ascii */
1492  if (mutt_ch_is_us_ascii(mutt_body_get_charset(a, chsbuf, sizeof(chsbuf))))
1493  a->noconv = false;
1494 
1495  if (!a->force_charset && !a->noconv)
1496  mutt_param_delete(&a->parameter, "charset");
1497 
1498  info = mutt_get_content_info(a->filename, a);
1499  if (!info)
1500  return;
1501 
1502  set_encoding(a, info);
1504 
1505  FREE(&a->content);
1506  a->content = info;
1507 }
1508 
1517 struct Body *mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg)
1518 {
1519  struct Body *body = NULL;
1520  FILE *fp = NULL;
1521  CopyMessageFlags cmflags;
1523 
1524  if (WithCrypto)
1525  {
1527  {
1529  return NULL;
1530  }
1531  }
1532 
1533  struct Buffer *buf = mutt_buffer_pool_get();
1534  mutt_buffer_mktemp(buf);
1535  fp = mutt_file_fopen(mutt_b2s(buf), "w+");
1536  if (!fp)
1537  {
1539  return NULL;
1540  }
1541 
1542  body = mutt_body_new();
1543  body->type = TYPE_MESSAGE;
1544  body->subtype = mutt_str_dup("rfc822");
1545  body->filename = mutt_str_dup(mutt_b2s(buf));
1546  body->unlink = true;
1547  body->use_disp = false;
1548  body->disposition = DISP_INLINE;
1549  body->noconv = true;
1550 
1552 
1554 
1555  CopyHeaderFlags chflags = CH_XMIT;
1556  cmflags = MUTT_CM_NO_FLAGS;
1557 
1558  /* If we are attaching a message, ignore C_MimeForwardDecode */
1559  if (!attach_msg && C_MimeForwardDecode)
1560  {
1561  chflags |= CH_MIME | CH_TXTPLAIN;
1562  cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1564  pgp &= ~PGP_ENCRYPT;
1566  pgp &= ~SMIME_ENCRYPT;
1567  }
1568  else if ((WithCrypto != 0) && C_ForwardDecrypt && (e->security & SEC_ENCRYPT))
1569  {
1571  {
1572  chflags |= CH_MIME | CH_NONEWLINE;
1573  cmflags = MUTT_CM_DECODE_PGP;
1574  pgp &= ~PGP_ENCRYPT;
1575  }
1576  else if (((WithCrypto & APPLICATION_PGP) != 0) &&
1578  {
1579  chflags |= CH_MIME | CH_TXTPLAIN;
1580  cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1581  pgp &= ~PGP_ENCRYPT;
1582  }
1583  else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
1585  {
1586  chflags |= CH_MIME | CH_TXTPLAIN;
1587  cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1588  pgp &= ~SMIME_ENCRYPT;
1589  }
1590  }
1591 
1592  mutt_copy_message(fp, m, e, cmflags, chflags, 0);
1593 
1594  fflush(fp);
1595  rewind(fp);
1596 
1597  body->email = email_new();
1598  body->email->offset = 0;
1599  /* we don't need the user headers here */
1600  body->email->env = mutt_rfc822_read_header(fp, body->email, false, false);
1601  if (WithCrypto)
1602  body->email->security = pgp;
1603  mutt_update_encoding(body);
1604  body->parts = body->email->content;
1605 
1606  mutt_file_fclose(&fp);
1607 
1608  return body;
1609 }
1610 
1617 static void run_mime_type_query(struct Body *att)
1618 {
1619  FILE *fp = NULL, *fp_err = NULL;
1620  char *buf = NULL;
1621  size_t buflen;
1622  pid_t pid;
1623  struct Buffer *cmd = mutt_buffer_pool_get();
1624 
1626 
1627  pid = filter_create(mutt_b2s(cmd), NULL, &fp, &fp_err);
1628  if (pid < 0)
1629  {
1630  mutt_error(_("Error running \"%s\""), mutt_b2s(cmd));
1632  return;
1633  }
1635 
1636  buf = mutt_file_read_line(buf, &buflen, fp, NULL, 0);
1637  if (buf)
1638  {
1639  if (strchr(buf, '/'))
1640  mutt_parse_content_type(buf, att);
1641  FREE(&buf);
1642  }
1643 
1644  mutt_file_fclose(&fp);
1645  mutt_file_fclose(&fp_err);
1646  filter_wait(pid);
1647 }
1648 
1655 struct Body *mutt_make_file_attach(const char *path)
1656 {
1657  struct Body *att = mutt_body_new();
1658  att->filename = mutt_str_dup(path);
1659 
1661  run_mime_type_query(att);
1662 
1663  /* Attempt to determine the appropriate content-type based on the filename
1664  * suffix. */
1665  if (!att->subtype)
1666  mutt_lookup_mime_type(att, path);
1667 
1669  {
1670  run_mime_type_query(att);
1671  }
1672 
1673  struct Content *info = mutt_get_content_info(path, att);
1674  if (!info)
1675  {
1676  mutt_body_free(&att);
1677  return NULL;
1678  }
1679 
1680  if (!att->subtype)
1681  {
1682  if ((info->nulbin == 0) &&
1683  ((info->lobin == 0) || ((info->lobin + info->hibin + info->ascii) / info->lobin >= 10)))
1684  {
1685  /* Statistically speaking, there should be more than 10% "lobin"
1686  * chars if this is really a binary file... */
1687  att->type = TYPE_TEXT;
1688  att->subtype = mutt_str_dup("plain");
1689  }
1690  else
1691  {
1692  att->type = TYPE_APPLICATION;
1693  att->subtype = mutt_str_dup("octet-stream");
1694  }
1695  }
1696 
1697  FREE(&info);
1698  mutt_update_encoding(att);
1699  return att;
1700 }
1701 
1707 static int get_toplevel_encoding(struct Body *a)
1708 {
1709  int e = ENC_7BIT;
1710 
1711  for (; a; a = a->next)
1712  {
1713  if (a->encoding == ENC_BINARY)
1714  return ENC_BINARY;
1715  if (a->encoding == ENC_8BIT)
1716  e = ENC_8BIT;
1717  }
1718 
1719  return e;
1720 }
1721 
1728 static bool check_boundary(const char *boundary, struct Body *b)
1729 {
1730  char *p = NULL;
1731 
1732  if (b->parts && check_boundary(boundary, b->parts))
1733  return true;
1734 
1735  if (b->next && check_boundary(boundary, b->next))
1736  return true;
1737 
1738  p = mutt_param_get(&b->parameter, "boundary");
1739  if (p && mutt_str_equal(p, boundary))
1740  {
1741  return true;
1742  }
1743  return false;
1744 }
1745 
1751 struct Body *mutt_make_multipart(struct Body *b)
1752 {
1753  struct Body *new_body = mutt_body_new();
1754  new_body->type = TYPE_MULTIPART;
1755  new_body->subtype = mutt_str_dup("mixed");
1756  new_body->encoding = get_toplevel_encoding(b);
1757  do
1758  {
1759  mutt_generate_boundary(&new_body->parameter);
1760  if (check_boundary(mutt_param_get(&new_body->parameter, "boundary"), b))
1761  mutt_param_delete(&new_body->parameter, "boundary");
1762  } while (!mutt_param_get(&new_body->parameter, "boundary"));
1763  new_body->use_disp = false;
1764  new_body->disposition = DISP_INLINE;
1765  new_body->parts = b;
1766 
1767  return new_body;
1768 }
1769 
1777 struct Body *mutt_remove_multipart(struct Body *b)
1778 {
1779  struct Body *t = NULL;
1780 
1781  if (b->parts)
1782  {
1783  t = b;
1784  b = b->parts;
1785  t->parts = NULL;
1786  mutt_body_free(&t);
1787  }
1788  return b;
1789 }
1790 
1801 void mutt_write_addrlist(struct AddressList *al, FILE *fp, int linelen, bool display)
1802 {
1803  char buf[1024];
1804  int count = 0;
1805 
1806  struct Address *a = NULL;
1807  TAILQ_FOREACH(a, al, entries)
1808  {
1809  buf[0] = '\0';
1810  mutt_addr_write(buf, sizeof(buf), a, display);
1811  size_t len = mutt_str_len(buf);
1812  if (count && (linelen + len > 74))
1813  {
1814  fputs("\n\t", fp);
1815  linelen = len + 8; /* tab is usually about 8 spaces... */
1816  }
1817  else
1818  {
1819  if (count && a->mailbox)
1820  {
1821  fputc(' ', fp);
1822  linelen++;
1823  }
1824  linelen += len;
1825  }
1826  fputs(buf, fp);
1827  struct Address *next = TAILQ_NEXT(a, entries);
1828  if (!a->group && next && next->mailbox)
1829  {
1830  linelen++;
1831  fputc(',', fp);
1832  }
1833  count++;
1834  }
1835  fputc('\n', fp);
1836 }
1837 
1847 void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
1848 {
1849  struct ListNode *np = NULL;
1850  size_t length = 0;
1851 
1852  STAILQ_FOREACH(np, r, entries)
1853  {
1854  if (++length == trim)
1855  break;
1856  }
1857 
1858  struct ListNode **ref = mutt_mem_calloc(length, sizeof(struct ListNode *));
1859 
1860  // store in reverse order
1861  size_t tmp = length;
1862  STAILQ_FOREACH(np, r, entries)
1863  {
1864  ref[--tmp] = np;
1865  if (tmp == 0)
1866  break;
1867  }
1868 
1869  for (size_t i = 0; i < length; i++)
1870  {
1871  fputc(' ', fp);
1872  fputs(ref[i]->data, fp);
1873  if (i != length - 1)
1874  fputc('\n', fp);
1875  }
1876 
1877  FREE(&ref);
1878 }
1879 
1890 static int print_val(FILE *fp, const char *pfx, const char *value,
1891  CopyHeaderFlags chflags, size_t col)
1892 {
1893  while (value && (value[0] != '\0'))
1894  {
1895  if (fputc(*value, fp) == EOF)
1896  return -1;
1897  /* corner-case: break words longer than 998 chars by force,
1898  * mandated by RFC5322 */
1899  if (!(chflags & CH_DISPLAY) && (++col >= 998))
1900  {
1901  if (fputs("\n ", fp) < 0)
1902  return -1;
1903  col = 1;
1904  }
1905  if (*value == '\n')
1906  {
1907  if ((value[1] != '\0') && pfx && (pfx[0] != '\0') && (fputs(pfx, fp) == EOF))
1908  return -1;
1909  /* for display, turn folding spaces into folding tabs */
1910  if ((chflags & CH_DISPLAY) && ((value[1] == ' ') || (value[1] == '\t')))
1911  {
1912  value++;
1913  while ((value[0] != '\0') && ((value[0] == ' ') || (value[0] == '\t')))
1914  value++;
1915  if (fputc('\t', fp) == EOF)
1916  return -1;
1917  continue;
1918  }
1919  }
1920  value++;
1921  }
1922  return 0;
1923 }
1924 
1937 static int fold_one_header(FILE *fp, const char *tag, const char *value, size_t vlen,
1938  const char *pfx, int wraplen, CopyHeaderFlags chflags)
1939 {
1940  if (!value || (*value == '\0') || !vlen)
1941  return 0;
1942 
1943  const char *p = value;
1944  char buf[8192] = { 0 };
1945  int first = 1, col = 0, l = 0;
1946  const bool display = (chflags & CH_DISPLAY);
1947 
1948  mutt_debug(LL_DEBUG5, "pfx=[%s], tag=[%s], flags=%d value=[%.*s]\n", pfx, tag,
1949  chflags, ((value[vlen - 1] == '\n') ? vlen - 1 : vlen), value);
1950 
1951  if (tag && *tag && (fprintf(fp, "%s%s: ", NONULL(pfx), tag) < 0))
1952  return -1;
1953  col = mutt_str_len(tag) + ((tag && (tag[0] != '\0')) ? 2 : 0) + mutt_str_len(pfx);
1954 
1955  while (p && (p[0] != '\0'))
1956  {
1957  int fold = 0;
1958 
1959  /* find the next word and place it in 'buf'. it may start with
1960  * whitespace we can fold before */
1961  const char *next = mutt_str_find_word(p);
1962  l = MIN(sizeof(buf) - 1, next - p);
1963  memcpy(buf, p, l);
1964  buf[l] = '\0';
1965 
1966  /* determine width: character cells for display, bytes for sending
1967  * (we get pure ascii only) */
1968  const int w = mutt_mb_width(buf, col, display);
1969  const int enc = mutt_str_startswith(buf, "=?");
1970 
1971  mutt_debug(LL_DEBUG5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n",
1972  (buf[0] == '\n' ? "\\n" : buf), col, w, *next);
1973 
1974  /* insert a folding \n before the current word's lwsp except for
1975  * header name, first word on a line (word longer than wrap width)
1976  * and encoded words */
1977  if (!first && !enc && col && ((col + w) >= wraplen))
1978  {
1979  col = mutt_str_len(pfx);
1980  fold = 1;
1981  if (fprintf(fp, "\n%s", NONULL(pfx)) <= 0)
1982  return -1;
1983  }
1984 
1985  /* print the actual word; for display, ignore leading ws for word
1986  * and fold with tab for readability */
1987  if (display && fold)
1988  {
1989  char *pc = buf;
1990  while ((pc[0] != '\0') && ((pc[0] == ' ') || (pc[0] == '\t')))
1991  {
1992  pc++;
1993  col--;
1994  }
1995  if (fputc('\t', fp) == EOF)
1996  return -1;
1997  if (print_val(fp, pfx, pc, chflags, col) < 0)
1998  return -1;
1999  col += 8;
2000  }
2001  else if (print_val(fp, pfx, buf, chflags, col) < 0)
2002  return -1;
2003  col += w;
2004 
2005  /* if the current word ends in \n, ignore all its trailing spaces
2006  * and reset column; this prevents us from putting only spaces (or
2007  * even none) on a line if the trailing spaces are located at our
2008  * current line width
2009  * XXX this covers ASCII space only, for display we probably
2010  * want something like iswspace() here */
2011  const char *sp = next;
2012  while ((sp[0] != '\0') && ((sp[0] == ' ') || (sp[0] == '\t')))
2013  sp++;
2014  if (sp[0] == '\n')
2015  {
2016  if (sp[1] == '\0')
2017  break;
2018  next = sp;
2019  col = 0;
2020  }
2021 
2022  p = next;
2023  first = 0;
2024  }
2025 
2026  /* if we have printed something but didn't \n-terminate it, do it
2027  * except the last word we printed ended in \n already */
2028  if (col && ((l == 0) || (buf[l - 1] != '\n')))
2029  if (putc('\n', fp) == EOF)
2030  return -1;
2031 
2032  return 0;
2033 }
2034 
2042 static char *unfold_header(char *s)
2043 {
2044  char *p = s;
2045  char *q = s;
2046 
2047  while (p && (p[0] != '\0'))
2048  {
2049  /* remove CRLF prior to FWSP, turn \t into ' ' */
2050  if ((p[0] == '\r') && (p[1] == '\n') && ((p[2] == ' ') || (p[2] == '\t')))
2051  {
2052  *q++ = ' ';
2053  p += 3;
2054  continue;
2055  }
2056  /* remove LF prior to FWSP, turn \t into ' ' */
2057  else if ((p[0] == '\n') && ((p[1] == ' ') || (p[1] == '\t')))
2058  {
2059  *q++ = ' ';
2060  p += 2;
2061  continue;
2062  }
2063  *q++ = *p++;
2064  }
2065  if (q)
2066  q[0] = '\0';
2067 
2068  return s;
2069 }
2070 
2084 static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char *pfx,
2085  const char *start, const char *end, CopyHeaderFlags chflags)
2086 {
2087  const char *t = strchr(start, ':');
2088  if (!t || (t > end))
2089  {
2090  mutt_debug(LL_DEBUG1, "#2 warning: header not in 'key: value' format!\n");
2091  return 0;
2092  }
2093 
2094  const size_t vallen = end - start;
2095  const bool short_enough = (pfxw + max <= wraplen);
2096 
2097  mutt_debug((short_enough ? LL_DEBUG2 : LL_DEBUG5), "buf[%s%.*s] %s, max width = %d %s %d\n",
2098  NONULL(pfx), vallen - 1 /* skip newline */, start,
2099  (short_enough ? "short enough" : "too long"), max,
2100  (short_enough ? "<=" : ">"), wraplen);
2101 
2102  int rc = 0;
2103  const char *valbuf = NULL, *tagbuf = NULL;
2104  const bool is_from = (vallen > 5) && mutt_istr_startswith(start, "from ");
2105 
2106  /* only pass through folding machinery if necessary for sending,
2107  * never wrap From_ headers on sending */
2108  if (!(chflags & CH_DISPLAY) && (short_enough || is_from))
2109  {
2110  if (pfx && *pfx)
2111  {
2112  if (fputs(pfx, fp) == EOF)
2113  {
2114  return -1;
2115  }
2116  }
2117 
2118  valbuf = mutt_strn_dup(start, end - start);
2119  rc = print_val(fp, pfx, valbuf, chflags, mutt_str_len(pfx));
2120  }
2121  else
2122  {
2123  if (!is_from)
2124  {
2125  tagbuf = mutt_strn_dup(start, t - start);
2126  /* skip over the colon separating the header field name and value */
2127  t++;
2128 
2129  /* skip over any leading whitespace (WSP, as defined in RFC5322)
2130  * NOTE: mutt_str_skip_email_wsp() does the wrong thing here.
2131  * See tickets 3609 and 3716. */
2132  while ((*t == ' ') || (*t == '\t'))
2133  t++;
2134  }
2135  const char *s = is_from ? start : t;
2136  valbuf = mutt_strn_dup(s, end - s);
2137  rc = fold_one_header(fp, tagbuf, valbuf, end - s, pfx, wraplen, chflags);
2138  }
2139 
2140  FREE(&tagbuf);
2141  FREE(&valbuf);
2142  return rc;
2143 }
2144 
2159 int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
2160  const char *pfx, int wraplen, CopyHeaderFlags chflags)
2161 {
2162  char *last = NULL, *line = NULL;
2163  int max = 0, w, rc = -1;
2164  int pfxw = mutt_strwidth(pfx);
2165  char *v = mutt_str_dup(value);
2166  bool display = (chflags & CH_DISPLAY);
2167 
2168  if (!display || C_Weed)
2169  v = unfold_header(v);
2170 
2171  /* when not displaying, use sane wrap value */
2172  if (!display)
2173  {
2174  if ((C_WrapHeaders < 78) || (C_WrapHeaders > 998))
2175  wraplen = 78;
2176  else
2177  wraplen = C_WrapHeaders;
2178  }
2179  else if (wraplen <= 0)
2180  wraplen = 78;
2181 
2182  const size_t vlen = mutt_str_len(v);
2183  if (tag)
2184  {
2185  /* if header is short enough, simply print it */
2186  if (!display && (mutt_strwidth(tag) + 2 + pfxw + mutt_strnwidth(v, vlen) <= wraplen))
2187  {
2188  mutt_debug(LL_DEBUG5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
2189  if (fprintf(fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
2190  goto out;
2191  rc = 0;
2192  goto out;
2193  }
2194  else
2195  {
2196  rc = fold_one_header(fp, tag, v, vlen, pfx, wraplen, chflags);
2197  goto out;
2198  }
2199  }
2200 
2201  char *p = v;
2202  last = v;
2203  line = v;
2204  while (p && *p)
2205  {
2206  p = strchr(p, '\n');
2207 
2208  /* find maximum line width in current header */
2209  if (p)
2210  *p = '\0';
2211  w = mutt_mb_width(line, 0, display);
2212  if (w > max)
2213  max = w;
2214  if (p)
2215  *p = '\n';
2216 
2217  if (!p)
2218  break;
2219 
2220  line = ++p;
2221  if ((*p != ' ') && (*p != '\t'))
2222  {
2223  if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
2224  goto out;
2225  last = p;
2226  max = 0;
2227  }
2228  }
2229 
2230  if (last && *last)
2231  if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
2232  goto out;
2233 
2234  rc = 0;
2235 
2236 out:
2237  FREE(&v);
2238  return rc;
2239 }
2240 
2249 static int userhdrs_override_cmp(const void *a, const void *b)
2250 {
2251  const char *ca = a;
2252  const char *cb = *(const char **) b;
2253  return mutt_istrn_cmp(ca, cb, strlen(cb));
2254 }
2255 
2263 static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs, bool privacy)
2264 {
2265  struct UserHdrsOverride overrides = { { 0 } };
2266 
2267  struct ListNode *tmp = NULL;
2268  STAILQ_FOREACH(tmp, userhdrs, entries)
2269  {
2270  char *const colon = strchr(tmp->data, ':');
2271  if (!colon)
2272  {
2273  continue;
2274  }
2275 
2276  const char *const value = mutt_str_skip_email_wsp(colon + 1);
2277  if (*value == '\0')
2278  {
2279  continue; /* don't emit empty fields. */
2280  }
2281 
2282  /* check whether the current user-header is an override */
2283  size_t curr_override = (size_t) -1;
2284  const char *const *idx = bsearch(tmp->data, userhdrs_override_headers,
2286  sizeof(char *), userhdrs_override_cmp);
2287  if (idx != NULL)
2288  {
2289  curr_override = idx - userhdrs_override_headers;
2290  overrides.is_overridden[curr_override] = true;
2291  }
2292 
2293  if (privacy && (curr_override == USERHDRS_OVERRIDE_USER_AGENT))
2294  {
2295  continue;
2296  }
2297 
2298  *colon = '\0';
2299  mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS);
2300  *colon = ':';
2301  }
2302 
2303  return overrides;
2304 }
2305 
2330 int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
2331  struct Body *attach, enum MuttWriteHeaderMode mode,
2332  bool privacy, bool hide_protected_subject)
2333 {
2334  char buf[1024];
2335 
2336  if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy)
2337  fputs(mutt_date_make_date(buf, sizeof(buf)), fp);
2338 
2339  /* UseFrom is not consulted here so that we can still write a From:
2340  * field if the user sets it with the 'my_hdr' command */
2341  if (!TAILQ_EMPTY(&env->from) && !privacy)
2342  {
2343  buf[0] = '\0';
2344  mutt_addrlist_write(&env->from, buf, sizeof(buf), false);
2345  fprintf(fp, "From: %s\n", buf);
2346  }
2347 
2348  if (!TAILQ_EMPTY(&env->sender) && !privacy)
2349  {
2350  buf[0] = '\0';
2351  mutt_addrlist_write(&env->sender, buf, sizeof(buf), false);
2352  fprintf(fp, "Sender: %s\n", buf);
2353  }
2354 
2355  if (!TAILQ_EMPTY(&env->to))
2356  {
2357  fputs("To: ", fp);
2358  mutt_write_addrlist(&env->to, fp, 4, 0);
2359  }
2360  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2361 #ifdef USE_NNTP
2362  if (!OptNewsSend)
2363 #endif
2364  fputs("To:\n", fp);
2365 
2366  if (!TAILQ_EMPTY(&env->cc))
2367  {
2368  fputs("Cc: ", fp);
2369  mutt_write_addrlist(&env->cc, fp, 4, 0);
2370  }
2371  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2372 #ifdef USE_NNTP
2373  if (!OptNewsSend)
2374 #endif
2375  fputs("Cc:\n", fp);
2376 
2377  if (!TAILQ_EMPTY(&env->bcc))
2378  {
2379  if ((mode == MUTT_WRITE_HEADER_POSTPONE) ||
2380  (mode == MUTT_WRITE_HEADER_EDITHDRS) || (mode == MUTT_WRITE_HEADER_FCC) ||
2381  ((mode == MUTT_WRITE_HEADER_NORMAL) && C_WriteBcc))
2382  {
2383  fputs("Bcc: ", fp);
2384  mutt_write_addrlist(&env->bcc, fp, 5, 0);
2385  }
2386  }
2387  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2388 #ifdef USE_NNTP
2389  if (!OptNewsSend)
2390 #endif
2391  fputs("Bcc:\n", fp);
2392 
2393 #ifdef USE_NNTP
2394  if (env->newsgroups)
2395  fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
2396  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
2397  fputs("Newsgroups:\n", fp);
2398 
2399  if (env->followup_to)
2400  fprintf(fp, "Followup-To: %s\n", env->followup_to);
2401  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
2402  fputs("Followup-To:\n", fp);
2403 
2404  if (env->x_comment_to)
2405  fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
2406  else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && C_XCommentTo)
2407  fputs("X-Comment-To:\n", fp);
2408 #endif
2409 
2410  if (env->subject)
2411  {
2412  if (hide_protected_subject &&
2413  ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
2414  (mode == MUTT_WRITE_HEADER_POSTPONE)))
2416  else
2417  mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, CH_NO_FLAGS);
2418  }
2419  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2420  fputs("Subject:\n", fp);
2421 
2422  /* save message id if the user has set it */
2423  if (env->message_id && !privacy)
2424  fprintf(fp, "Message-ID: %s\n", env->message_id);
2425 
2426  if (!TAILQ_EMPTY(&env->reply_to))
2427  {
2428  fputs("Reply-To: ", fp);
2429  mutt_write_addrlist(&env->reply_to, fp, 10, 0);
2430  }
2431  else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2432  fputs("Reply-To:\n", fp);
2433 
2434  if (!TAILQ_EMPTY(&env->mail_followup_to))
2435  {
2436 #ifdef USE_NNTP
2437  if (!OptNewsSend)
2438 #endif
2439  {
2440  fputs("Mail-Followup-To: ", fp);
2441  mutt_write_addrlist(&env->mail_followup_to, fp, 18, 0);
2442  }
2443  }
2444 
2445  /* Add any user defined headers */
2446  struct UserHdrsOverride userhdrs_overrides = write_userhdrs(fp, &env->userhdrs, privacy);
2447 
2448  if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC) ||
2449  (mode == MUTT_WRITE_HEADER_POSTPONE))
2450  {
2451  if (!STAILQ_EMPTY(&env->references))
2452  {
2453  fputs("References:", fp);
2454  mutt_write_references(&env->references, fp, 10);
2455  fputc('\n', fp);
2456  }
2457 
2458  /* Add the MIME headers */
2459  if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
2460  {
2461  fputs("MIME-Version: 1.0\n", fp);
2462  mutt_write_mime_header(attach, fp);
2463  }
2464  }
2465 
2466  if (!STAILQ_EMPTY(&env->in_reply_to))
2467  {
2468  fputs("In-Reply-To:", fp);
2469  mutt_write_references(&env->in_reply_to, fp, 0);
2470  fputc('\n', fp);
2471  }
2472 
2473 #ifdef USE_AUTOCRYPT
2474  if (C_Autocrypt)
2475  {
2476  if (mode == MUTT_WRITE_HEADER_NORMAL || mode == MUTT_WRITE_HEADER_FCC)
2478  if (mode == MUTT_WRITE_HEADER_MIME)
2480  }
2481 #endif
2482 
2483  if (((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_FCC)) && !privacy &&
2484  C_UserAgent && !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
2485  {
2486  /* Add a vanity header */
2487  fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
2488  }
2489 
2490  return (ferror(fp) == 0) ? 0 : -1;
2491 }
2492 
2499 static void encode_headers(struct ListHead *h)
2500 {
2501  char *tmp = NULL;
2502  char *p = NULL;
2503  int i;
2504 
2505  struct ListNode *np = NULL;
2506  STAILQ_FOREACH(np, h, entries)
2507  {
2508  p = strchr(np->data, ':');
2509  if (!p)
2510  continue;
2511 
2512  i = p - np->data;
2513  p = mutt_str_skip_email_wsp(p + 1);
2514  tmp = mutt_str_dup(p);
2515 
2516  if (!tmp)
2517  continue;
2518 
2519  rfc2047_encode(&tmp, NULL, i + 2, C_SendCharset);
2520  mutt_mem_realloc(&np->data, i + 2 + mutt_str_len(tmp) + 1);
2521 
2522  sprintf(np->data + i + 2, "%s", tmp);
2523 
2524  FREE(&tmp);
2525  }
2526 }
2527 
2536 const char *mutt_fqdn(bool may_hide_host)
2537 {
2538  if (!C_Hostname || (C_Hostname[0] == '@'))
2539  return NULL;
2540 
2541  char *p = C_Hostname;
2542 
2543  if (may_hide_host && C_HiddenHost)
2544  {
2545  p = strchr(C_Hostname, '.');
2546  if (p)
2547  p++;
2548 
2549  // sanity check: don't hide the host if the fqdn is something like example.com
2550  if (!p || !strchr(p, '.'))
2551  p = C_Hostname;
2552  }
2553 
2554  return p;
2555 }
2556 
2563 static char *gen_msgid(void)
2564 {
2565  char buf[128];
2566  unsigned char rndid[MUTT_RANDTAG_LEN + 1];
2567 
2568  mutt_rand_base32(rndid, sizeof(rndid) - 1);
2569  rndid[MUTT_RANDTAG_LEN] = 0;
2570  const char *fqdn = mutt_fqdn(false);
2571  if (!fqdn)
2572  fqdn = NONULL(ShortHostname);
2573 
2574  struct tm tm = mutt_date_gmtime(MUTT_DATE_NOW);
2575  snprintf(buf, sizeof(buf), "<%d%02d%02d%02d%02d%02d.%s@%s>", tm.tm_year + 1900,
2576  tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, rndid, fqdn);
2577  return mutt_str_dup(buf);
2578 }
2579 
2584 static void alarm_handler(int sig)
2585 {
2586  SigAlrm = 1;
2587 }
2588 
2601 static int send_msg(const char *path, char **args, const char *msg, char **tempfile)
2602 {
2603  sigset_t set;
2604  int st;
2605 
2607 
2608  sigemptyset(&set);
2609  /* we also don't want to be stopped right now */
2610  sigaddset(&set, SIGTSTP);
2611  sigprocmask(SIG_BLOCK, &set, NULL);
2612 
2613  if ((C_SendmailWait >= 0) && tempfile)
2614  {
2615  struct Buffer *tmp = mutt_buffer_pool_get();
2616  mutt_buffer_mktemp(tmp);
2617  *tempfile = mutt_buffer_strdup(tmp);
2619  }
2620 
2621  pid_t pid = fork();
2622  if (pid == 0)
2623  {
2624  struct sigaction act, oldalrm;
2625 
2626  /* save parent's ID before setsid() */
2627  pid_t ppid = getppid();
2628 
2629  /* we want the delivery to continue even after the main process dies,
2630  * so we put ourselves into another session right away */
2631  setsid();
2632 
2633  /* next we close all open files */
2634  close(0);
2635 #ifdef OPEN_MAX
2636  for (int fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2637  close(fd);
2638 #elif defined(_POSIX_OPEN_MAX)
2639  for (int fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2640  close(fd);
2641 #else
2642  if (tempfile)
2643  {
2644  close(1);
2645  close(2);
2646  }
2647 #endif
2648 
2649  /* now the second fork() */
2650  pid = fork();
2651  if (pid == 0)
2652  {
2653  /* "msg" will be opened as stdin */
2654  if (open(msg, O_RDONLY, 0) < 0)
2655  {
2656  unlink(msg);
2657  _exit(S_ERR);
2658  }
2659  unlink(msg);
2660 
2661  if ((C_SendmailWait >= 0) && tempfile && *tempfile)
2662  {
2663  /* *tempfile will be opened as stdout */
2664  if (open(*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2665  _exit(S_ERR);
2666  /* redirect stderr to *tempfile too */
2667  if (dup(1) < 0)
2668  _exit(S_ERR);
2669  }
2670  else if (tempfile)
2671  {
2672  if (open("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2673  _exit(S_ERR);
2674  if (open("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2675  _exit(S_ERR);
2676  }
2677 
2678  /* execvpe is a glibc extension */
2679  /* execvpe (path, args, mutt_envlist_getlist()); */
2680  execvp(path, args);
2681  _exit(S_ERR);
2682  }
2683  else if (pid == -1)
2684  {
2685  unlink(msg);
2686  FREE(tempfile);
2687  _exit(S_ERR);
2688  }
2689 
2690  /* C_SendmailWait > 0: interrupt waitpid() after C_SendmailWait seconds
2691  * C_SendmailWait = 0: wait forever
2692  * C_SendmailWait < 0: don't wait */
2693  if (C_SendmailWait > 0)
2694  {
2695  SigAlrm = 0;
2696  act.sa_handler = alarm_handler;
2697 #ifdef SA_INTERRUPT
2698  /* need to make sure waitpid() is interrupted on SIGALRM */
2699  act.sa_flags = SA_INTERRUPT;
2700 #else
2701  act.sa_flags = 0;
2702 #endif
2703  sigemptyset(&act.sa_mask);
2704  sigaction(SIGALRM, &act, &oldalrm);
2705  alarm(C_SendmailWait);
2706  }
2707  else if (C_SendmailWait < 0)
2708  _exit(0xff & EX_OK);
2709 
2710  if (waitpid(pid, &st, 0) > 0)
2711  {
2712  st = WIFEXITED(st) ? WEXITSTATUS(st) : S_ERR;
2713  if (C_SendmailWait && (st == (0xff & EX_OK)) && tempfile && *tempfile)
2714  {
2715  unlink(*tempfile); /* no longer needed */
2716  FREE(tempfile);
2717  }
2718  }
2719  else
2720  {
2721  st = ((C_SendmailWait > 0) && (errno == EINTR) && SigAlrm) ? S_BKG : S_ERR;
2722  if ((C_SendmailWait > 0) && tempfile && *tempfile)
2723  {
2724  unlink(*tempfile);
2725  FREE(tempfile);
2726  }
2727  }
2728 
2729  if (C_SendmailWait > 0)
2730  {
2731  /* reset alarm; not really needed, but... */
2732  alarm(0);
2733  sigaction(SIGALRM, &oldalrm, NULL);
2734  }
2735 
2736  if ((kill(ppid, 0) == -1) && (errno == ESRCH) && tempfile && *tempfile)
2737  {
2738  /* the parent is already dead */
2739  unlink(*tempfile);
2740  FREE(tempfile);
2741  }
2742 
2743  _exit(st);
2744  }
2745 
2746  sigprocmask(SIG_UNBLOCK, &set, NULL);
2747 
2748  if ((pid != -1) && (waitpid(pid, &st, 0) > 0))
2749  st = WIFEXITED(st) ? WEXITSTATUS(st) : S_ERR; /* return child status */
2750  else
2751  st = S_ERR; /* error */
2752 
2754 
2755  return st;
2756 }
2757 
2766 static char **add_args_one(char **args, size_t *argslen, size_t *argsmax, struct Address *addr)
2767 {
2768  /* weed out group mailboxes, since those are for display only */
2769  if (addr->mailbox && !addr->group)
2770  {
2771  if (*argslen == *argsmax)
2772  mutt_mem_realloc(&args, (*argsmax += 5) * sizeof(char *));
2773  args[(*argslen)++] = addr->mailbox;
2774  }
2775  return args;
2776 }
2777 
2786 static char **add_args(char **args, size_t *argslen, size_t *argsmax, struct AddressList *al)
2787 {
2788  if (!al)
2789  return args;
2790 
2791  struct Address *a = NULL;
2792  TAILQ_FOREACH(a, al, entries)
2793  {
2794  args = add_args_one(args, argslen, argsmax, a);
2795  }
2796  return args;
2797 }
2798 
2809 static char **add_option(char **args, size_t *argslen, size_t *argsmax, char *s)
2810 {
2811  if (*argslen == *argsmax)
2812  mutt_mem_realloc(&args, (*argsmax += 5) * sizeof(char *));
2813  args[(*argslen)++] = s;
2814  return args;
2815 }
2816 
2828 int mutt_invoke_sendmail(struct AddressList *from, struct AddressList *to,
2829  struct AddressList *cc, struct AddressList *bcc,
2830  const char *msg, bool eightbit)
2831 {
2832  char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2833  char **args = NULL;
2834  size_t argslen = 0, argsmax = 0;
2835  char **extra_args = NULL;
2836  int i;
2837 
2838 #ifdef USE_NNTP
2839  if (OptNewsSend)
2840  {
2841  char cmd[1024];
2842 
2843  mutt_expando_format(cmd, sizeof(cmd), 0, sizeof(cmd), NONULL(C_Inews),
2845  if (*cmd == '\0')
2846  {
2847  i = nntp_post(Context->mailbox, msg);
2848  unlink(msg);
2849  return i;
2850  }
2851 
2852  s = mutt_str_dup(cmd);
2853  }
2854  else
2855 #endif
2856  s = mutt_str_dup(C_Sendmail);
2857 
2858  /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
2859  if (!s)
2860  {
2861  mutt_error(_("$sendmail must be set in order to send mail"));
2862  return -1;
2863  }
2864 
2865  ps = s;
2866  i = 0;
2867  while ((ps = strtok(ps, " ")))
2868  {
2869  if (argslen == argsmax)
2870  mutt_mem_realloc(&args, sizeof(char *) * (argsmax += 5));
2871 
2872  if (i)
2873  {
2874  if (mutt_str_equal(ps, "--"))
2875  break;
2876  args[argslen++] = ps;
2877  }
2878  else
2879  {
2880  path = mutt_str_dup(ps);
2881  ps = strrchr(ps, '/');
2882  if (ps)
2883  ps++;
2884  else
2885  ps = path;
2886  args[argslen++] = ps;
2887  }
2888  ps = NULL;
2889  i++;
2890  }
2891 
2892 #ifdef USE_NNTP
2893  if (!OptNewsSend)
2894  {
2895 #endif
2896  size_t extra_argslen = 0;
2897  /* If C_Sendmail contained a "--", we save the recipients to append to
2898  * args after other possible options added below. */
2899  if (ps)
2900  {
2901  ps = NULL;
2902  size_t extra_argsmax = 0;
2903  while ((ps = strtok(ps, " ")))
2904  {
2905  if (extra_argslen == extra_argsmax)
2906  mutt_mem_realloc(&extra_args, sizeof(char *) * (extra_argsmax += 5));
2907 
2908  extra_args[extra_argslen++] = ps;
2909  ps = NULL;
2910  }
2911  }
2912 
2913  if (eightbit && C_Use8bitmime)
2914  args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
2915 
2916  if (C_UseEnvelopeFrom)
2917  {
2919  {
2920  args = add_option(args, &argslen, &argsmax, "-f");
2921  args = add_args_one(args, &argslen, &argsmax, C_EnvelopeFromAddress);
2922  }
2923  else if (!TAILQ_EMPTY(from) && !TAILQ_NEXT(TAILQ_FIRST(from), entries))
2924  {
2925  args = add_option(args, &argslen, &argsmax, "-f");
2926  args = add_args(args, &argslen, &argsmax, from);
2927  }
2928  }
2929 
2930  if (C_DsnNotify)
2931  {
2932  args = add_option(args, &argslen, &argsmax, "-N");
2933  args = add_option(args, &argslen, &argsmax, C_DsnNotify);
2934  }
2935  if (C_DsnReturn)
2936  {
2937  args = add_option(args, &argslen, &argsmax, "-R");
2938  args = add_option(args, &argslen, &argsmax, C_DsnReturn);
2939  }
2940  args = add_option(args, &argslen, &argsmax, "--");
2941  for (i = 0; i < extra_argslen; i++)
2942  args = add_option(args, &argslen, &argsmax, extra_args[i]);
2943  args = add_args(args, &argslen, &argsmax, to);
2944  args = add_args(args, &argslen, &argsmax, cc);
2945  args = add_args(args, &argslen, &argsmax, bcc);
2946 #ifdef USE_NNTP
2947  }
2948 #endif
2949 
2950  if (argslen == argsmax)
2951  mutt_mem_realloc(&args, sizeof(char *) * (++argsmax));
2952 
2953  args[argslen++] = NULL;
2954 
2955  /* Some user's $sendmail command uses gpg for password decryption,
2956  * and is set up to prompt using ncurses pinentry. If we
2957  * mutt_endwin() it leaves other users staring at a blank screen.
2958  * So instead, just force a hard redraw on the next refresh. */
2959  if (!OptNoCurses)
2961 
2962  i = send_msg(path, args, msg, OptNoCurses ? NULL : &childout);
2963  if (i != (EX_OK & 0xff))
2964  {
2965  if (i != S_BKG)
2966  {
2967  const char *e = mutt_str_sysexit(i);
2968  mutt_error(_("Error sending message, child exited %d (%s)"), i, NONULL(e));
2969  if (childout)
2970  {
2971  struct stat st;
2972 
2973  if ((stat(childout, &st) == 0) && (st.st_size > 0))
2974  {
2975  mutt_do_pager(_("Output of the delivery process"), childout,
2976  MUTT_PAGER_NO_FLAGS, NULL);
2977  }
2978  }
2979  }
2980  }
2981  else if (childout)
2982  unlink(childout);
2983 
2984  FREE(&childout);
2985  FREE(&path);
2986  FREE(&s);
2987  FREE(&args);
2988  FREE(&extra_args);
2989 
2990  if (i == (EX_OK & 0xff))
2991  i = 0;
2992  else if (i == S_BKG)
2993  i = 1;
2994  else
2995  i = -1;
2996  return i;
2997 }
2998 
3008 void mutt_prepare_envelope(struct Envelope *env, bool final)
3009 {
3010  if (final)
3011  {
3012  if (!TAILQ_EMPTY(&env->bcc) && TAILQ_EMPTY(&env->to) && TAILQ_EMPTY(&env->cc))
3013  {
3014  /* some MTA's will put an Apparently-To: header field showing the Bcc:
3015  * recipients if there is no To: or Cc: field, so attempt to suppress
3016  * it by using an empty To: field. */
3017  struct Address *to = mutt_addr_new();
3018  to->group = true;
3019  mutt_addrlist_append(&env->to, to);
3021 
3022  char buf[1024];
3023  buf[0] = '\0';
3024  mutt_addr_cat(buf, sizeof(buf), "undisclosed-recipients", AddressSpecials);
3025 
3026  to->mailbox = mutt_str_dup(buf);
3027  }
3028 
3029  mutt_set_followup_to(env);
3030 
3031  if (!env->message_id)
3032  env->message_id = gen_msgid();
3033  }
3034 
3035  /* Take care of 8-bit => 7-bit conversion. */
3037  encode_headers(&env->userhdrs);
3038 }
3039 
3048 {
3049  struct ListNode *item = NULL;
3050  STAILQ_FOREACH(item, &env->userhdrs, entries)
3051  {
3052  rfc2047_decode(&item->data);
3053  }
3054 
3056 
3057  /* back conversions */
3059 }
3060 
3071 static int bounce_message(FILE *fp, struct Email *e, struct AddressList *to,
3072  const char *resent_from, struct AddressList *env_from)
3073 {
3074  if (!e)
3075  return -1;
3076 
3077  int rc = 0;
3078 
3079  struct Buffer *tempfile = mutt_buffer_pool_get();
3080  mutt_buffer_mktemp(tempfile);
3081  FILE *fp_tmp = mutt_file_fopen(mutt_b2s(tempfile), "w");
3082  if (fp_tmp)
3083  {
3084  char date[128];
3086 
3087  if (!C_BounceDelivered)
3088  chflags |= CH_WEED_DELIVERED;
3089 
3090  fseeko(fp, e->offset, SEEK_SET);
3091  fprintf(fp_tmp, "Resent-From: %s", resent_from);
3092  fprintf(fp_tmp, "\nResent-%s", mutt_date_make_date(date, sizeof(date)));
3093  char *msgid_str = gen_msgid();
3094  fprintf(fp_tmp, "Resent-Message-ID: %s\n", msgid_str);
3095  FREE(&msgid_str);
3096  fputs("Resent-To: ", fp_tmp);
3097  mutt_write_addrlist(to, fp_tmp, 11, 0);
3098  mutt_copy_header(fp, e, fp_tmp, chflags, NULL, 0);
3099  fputc('\n', fp_tmp);
3100  mutt_file_copy_bytes(fp, fp_tmp, e->content->length);
3101  if (mutt_file_fclose(&fp_tmp) != 0)
3102  {
3103  mutt_perror(mutt_b2s(tempfile));
3104  unlink(mutt_b2s(tempfile));
3105  return -1;
3106  }
3107 #ifdef USE_SMTP
3108  if (C_SmtpUrl)
3109  {
3110  rc = mutt_smtp_send(env_from, to, NULL, NULL, mutt_b2s(tempfile),
3111  e->content->encoding == ENC_8BIT);
3112  }
3113  else
3114 #endif
3115  {
3116  rc = mutt_invoke_sendmail(env_from, to, NULL, NULL, mutt_b2s(tempfile),
3117  (e->content->encoding == ENC_8BIT));
3118  }
3119  }
3120 
3121  mutt_buffer_pool_release(&tempfile);
3122  return rc;
3123 }
3124 
3133 int mutt_bounce_message(FILE *fp, struct Email *e, struct AddressList *to)
3134 {
3135  if (!fp || !e || !to || TAILQ_EMPTY(to))
3136  return -1;
3137 
3138  const char *fqdn = mutt_fqdn(true);
3139  char resent_from[256];
3140  char *err = NULL;
3141 
3142  resent_from[0] = '\0';
3143  struct Address *from = mutt_default_from();
3144  struct AddressList from_list = TAILQ_HEAD_INITIALIZER(from_list);
3145  mutt_addrlist_append(&from_list, from);
3146 
3147  /* mutt_default_from() does not use $realname if the real name is not set
3148  * in $from, so we add it here. The reason it is not added in
3149  * mutt_default_from() is that during normal sending, we execute
3150  * send-hooks and set the realname last so that it can be changed based
3151  * upon message criteria. */
3152  if (!from->personal)
3153  from->personal = mutt_str_dup(C_Realname);
3154 
3155  mutt_addrlist_qualify(&from_list, fqdn);
3156 
3157  rfc2047_encode_addrlist(&from_list, "Resent-From");
3158  if (mutt_addrlist_to_intl(&from_list, &err))
3159  {
3160  mutt_error(_("Bad IDN %s while preparing resent-from"), err);
3161  FREE(&err);
3162  mutt_addrlist_clear(&from_list);
3163  return -1;
3164  }
3165  mutt_addrlist_write(&from_list, resent_from, sizeof(resent_from), false);
3166 
3167 #ifdef USE_NNTP
3168  OptNewsSend = false;
3169 #endif
3170 
3171  /* prepare recipient list. idna conversion appears to happen before this
3172  * function is called, since the user receives confirmation of the address
3173  * list being bounced to. */
3174  struct AddressList resent_to = TAILQ_HEAD_INITIALIZER(resent_to);
3175  mutt_addrlist_copy(&resent_to, to, false);
3176  rfc2047_encode_addrlist(&resent_to, "Resent-To");
3177  int rc = bounce_message(fp, e, &resent_to, resent_from, &from_list);
3178  mutt_addrlist_clear(&resent_to);
3179  mutt_addrlist_clear(&from_list);
3180 
3181  return rc;
3182 }
3183 
3189 static void set_noconv_flags(struct Body *b, bool flag)
3190 {
3191  for (; b; b = b->next)
3192  {
3193  if ((b->type == TYPE_MESSAGE) || (b->type == TYPE_MULTIPART))
3194  set_noconv_flags(b->parts, flag);
3195  else if ((b->type == TYPE_TEXT) && b->noconv)
3196  {
3197  if (flag)
3198  mutt_param_set(&b->parameter, "x-mutt-noconv", "yes");
3199  else
3200  mutt_param_delete(&b->parameter, "x-mutt-noconv");
3201  }
3202  }
3203 }
3204 
3216 int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid,
3217  bool post, char *fcc, char **finalpath)
3218 {
3219  char fcc_tok[PATH_MAX];
3220  char fcc_expanded[PATH_MAX];
3221 
3222  mutt_str_copy(fcc_tok, path, sizeof(fcc_tok));
3223 
3224  char *tok = strtok(fcc_tok, ",");
3225  if (!tok)
3226  return -1;
3227 
3228  mutt_debug(LL_DEBUG1, "Fcc: initial mailbox = '%s'\n", tok);
3229  /* mutt_expand_path already called above for the first token */
3230  int status = mutt_write_fcc(tok, e, msgid, post, fcc, finalpath);
3231  if (status != 0)
3232  return status;
3233 
3234  while ((tok = strtok(NULL, ",")))
3235  {
3236  if (*tok == '\0')
3237  continue;
3238 
3239  /* Only call mutt_expand_path if tok has some data */
3240  mutt_debug(LL_DEBUG1, "Fcc: additional mailbox token = '%s'\n", tok);
3241  mutt_str_copy(fcc_expanded, tok, sizeof(fcc_expanded));
3242  mutt_expand_path(fcc_expanded, sizeof(fcc_expanded));
3243  mutt_debug(LL_DEBUG1, " Additional mailbox expanded = '%s'\n", fcc_expanded);
3244  status = mutt_write_fcc(fcc_expanded, e, msgid, post, fcc, finalpath);
3245  if (status != 0)
3246  return status;
3247  }
3248 
3249  return 0;
3250 }
3251 
3263 int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
3264  bool post, const char *fcc, char **finalpath)
3265 {
3266  struct Message *msg = NULL;
3267  struct Buffer *tempfile = NULL;
3268  FILE *fp_tmp = NULL;
3269  int rc = -1;
3270  bool need_mailbox_cleanup = false;
3271  struct stat st;
3272  char buf[128];
3273  MsgOpenFlags onm_flags;
3274 
3275  if (post)
3276  set_noconv_flags(e->content, true);
3277 
3278 #ifdef RECORD_FOLDER_HOOK
3279  mutt_folder_hook(path, NULL);
3280 #endif
3281  struct Mailbox *m_fcc = mx_path_resolve(path);
3282  bool old_append = m_fcc->append;
3283  struct Context *ctx_fcc = mx_mbox_open(m_fcc, MUTT_APPEND | MUTT_QUIET);
3284  if (!ctx_fcc)
3285  {
3286  mutt_debug(LL_DEBUG1, "unable to open mailbox %s in append-mode, aborting\n", path);
3287  mailbox_free(&m_fcc);
3288  goto done;
3289  }
3290 
3291  /* We need to add a Content-Length field to avoid problems where a line in
3292  * the message body begins with "From " */
3293  if ((ctx_fcc->mailbox->type == MUTT_MMDF) || (ctx_fcc->mailbox->type == MUTT_MBOX))
3294  {
3295  tempfile = mutt_buffer_pool_get();
3296  mutt_buffer_mktemp(tempfile);
3297  fp_tmp = mutt_file_fopen(mutt_b2s(tempfile), "w+");
3298  if (!fp_tmp)
3299  {
3300  mutt_perror(mutt_b2s(tempfile));
3301  mx_mbox_close(&ctx_fcc);
3302  goto done;
3303  }
3304  /* remember new mail status before appending message */
3305  need_mailbox_cleanup = true;
3306  stat(path, &st);
3307  }
3308 
3309  e->read = !post; /* make sure to put it in the 'cur' directory (maildir) */
3310  onm_flags = MUTT_ADD_FROM;
3311  if (post)
3312  onm_flags |= MUTT_SET_DRAFT;
3313  msg = mx_msg_open_new(ctx_fcc->mailbox, e, onm_flags);
3314  if (!msg)
3315  {
3316  mutt_file_fclose(&fp_tmp);
3317  mx_mbox_close(&ctx_fcc);
3318  goto done;
3319  }
3320 
3321  /* post == 1 => postpone message.
3322  * post == 0 => Normal mode. */
3326 
3327  /* (postponement) if this was a reply of some sort, <msgid> contains the
3328  * Message-ID: of message replied to. Save it using a special X-Mutt-
3329  * header so it can be picked up if the message is recalled at a later
3330  * point in time. This will allow the message to be marked as replied if
3331  * the same mailbox is still open. */
3332  if (post && msgid)
3333  fprintf(msg->fp, "X-Mutt-References: %s\n", msgid);
3334 
3335  /* (postponement) save the Fcc: using a special X-Mutt- header so that
3336  * it can be picked up when the message is recalled */
3337  if (post && fcc)
3338  fprintf(msg->fp, "X-Mutt-Fcc: %s\n", fcc);
3339 
3340  if ((ctx_fcc->mailbox->type == MUTT_MMDF) || (ctx_fcc->mailbox->type == MUTT_MBOX))
3341  fprintf(msg->fp, "Status: RO\n");
3342 
3343  /* mutt_rfc822_write_header() only writes out a Date: header with
3344  * mode == 0, i.e. _not_ postponement; so write out one ourself */
3345  if (post)
3346  fprintf(msg->fp, "%s", mutt_date_make_date(buf, sizeof(buf)));
3347 
3348  /* (postponement) if the mail is to be signed or encrypted, save this info */
3349  if (((WithCrypto & APPLICATION_PGP) != 0) && post && (e->security & APPLICATION_PGP))
3350  {
3351  fputs("X-Mutt-PGP: ", msg->fp);
3352  if (e->security & SEC_ENCRYPT)
3353  fputc('E', msg->fp);
3354  if (e->security & SEC_OPPENCRYPT)
3355  fputc('O', msg->fp);
3356  if (e->security & SEC_SIGN)
3357  {
3358  fputc('S', msg->fp);
3359  if (C_PgpSignAs)
3360  fprintf(msg->fp, "<%s>", C_PgpSignAs);
3361  }
3362  if (e->security & SEC_INLINE)
3363  fputc('I', msg->fp);
3364 #ifdef USE_AUTOCRYPT
3365  if (e->security & SEC_AUTOCRYPT)
3366  fputc('A', msg->fp);
3368  fputc('Z', msg->fp);
3369 #endif
3370  fputc('\n', msg->fp);
3371  }
3372 
3373  /* (postponement) if the mail is to be signed or encrypted, save this info */
3374  if (((WithCrypto & APPLICATION_SMIME) != 0) && post && (e->security & APPLICATION_SMIME))
3375  {
3376  fputs("X-Mutt-SMIME: ", msg->fp);
3377  if (e->security & SEC_ENCRYPT)
3378  {
3379  fputc('E', msg->fp);
3380  if (C_SmimeEncryptWith)
3381  fprintf(msg->fp, "C<%s>", C_SmimeEncryptWith);
3382  }
3383  if (e->security & SEC_OPPENCRYPT)
3384  fputc('O', msg->fp);
3385  if (e->security & SEC_SIGN)
3386  {
3387  fputc('S', msg->fp);
3388  if (C_SmimeSignAs)
3389  fprintf(msg->fp, "<%s>", C_SmimeSignAs);
3390  }
3391  if (e->security & SEC_INLINE)
3392  fputc('I', msg->fp);
3393  fputc('\n', msg->fp);
3394  }
3395 
3396 #ifdef MIXMASTER
3397  /* (postponement) if the mail is to be sent through a mixmaster
3398  * chain, save that information */
3399 
3400  if (post && !STAILQ_EMPTY(&e->chain))
3401  {
3402  fputs("X-Mutt-Mix:", msg->fp);
3403  struct ListNode *p = NULL;
3404  STAILQ_FOREACH(p, &e->chain, entries)
3405  {
3406  fprintf(msg->fp, " %s", (char *) p->data);
3407  }
3408 
3409  fputc('\n', msg->fp);
3410  }
3411 #endif
3412 
3413  if (fp_tmp)
3414  {
3415  mutt_write_mime_body(e->content, fp_tmp);
3416 
3417  /* make sure the last line ends with a newline. Emacs doesn't ensure this
3418  * will happen, and it can cause problems parsing the mailbox later. */
3419  fseek(fp_tmp, -1, SEEK_END);
3420  if (fgetc(fp_tmp) != '\n')
3421  {
3422  fseek(fp_tmp, 0, SEEK_END);
3423  fputc('\n', fp_tmp);
3424  }
3425 
3426  fflush(fp_tmp);
3427  if (ferror(fp_tmp))
3428  {
3429  mutt_debug(LL_DEBUG1, "%s: write failed\n", mutt_b2s(tempfile));
3430  mutt_file_fclose(&fp_tmp);
3431  unlink(mutt_b2s(tempfile));
3432  mx_msg_commit(ctx_fcc->mailbox, msg); /* XXX really? */
3433  mx_msg_close(ctx_fcc->mailbox, &msg);
3434  mx_mbox_close(&ctx_fcc);
3435  goto done;
3436  }
3437 
3438  /* count the number of lines */
3439  int lines = 0;
3440  char line_buf[1024];
3441  rewind(fp_tmp);
3442  while (fgets(line_buf, sizeof(line_buf), fp_tmp))
3443  lines++;
3444  fprintf(msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello(fp_tmp));
3445  fprintf(msg->fp, "Lines: %d\n\n", lines);
3446 
3447  /* copy the body and clean up */
3448  rewind(fp_tmp);
3449  rc = mutt_file_copy_stream(fp_tmp, msg->fp);
3450  if (mutt_file_fclose(&fp_tmp) != 0)
3451  rc = -1;
3452  /* if there was an error, leave the temp version */
3453  if (rc >= 0)
3454  {
3455  unlink(mutt_b2s(tempfile));
3456  rc = 0;
3457  }
3458  }
3459  else
3460  {
3461  fputc('\n', msg->fp); /* finish off the header */
3462  rc = mutt_write_mime_body(e->content, msg->fp);
3463  }
3464 
3465  if (mx_msg_commit(ctx_fcc->mailbox, msg) != 0)
3466  rc = -1;
3467  else if (finalpath)
3468  *finalpath = mutt_str_dup(msg->committed_path);
3469  mx_msg_close(ctx_fcc->mailbox, &msg);
3470  mx_mbox_close(&ctx_fcc);
3471 
3472  if (!post && need_mailbox_cleanup)
3473  mutt_mailbox_cleanup(path, &st);
3474 
3475  if (post)
3476  set_noconv_flags(e->content, false);
3477 
3478 done:
3479  if (m_fcc)
3480  m_fcc->append = old_append;
3481 #ifdef RECORD_FOLDER_HOOK
3482  /* We ran a folder hook for the destination mailbox,
3483  * now we run it for the user's current mailbox */
3484  if (Context && Context->mailbox->path)
3486 #endif
3487 
3488  if (fp_tmp)
3489  {
3490  mutt_file_fclose(&fp_tmp);
3491  unlink(mutt_b2s(tempfile));
3492  }
3493  mutt_buffer_pool_release(&tempfile);
3494 
3495  return rc;
3496 }
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:54
Info about the body of an email.
Definition: sendlib.c:113
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:879
char * attribute
Parameter name.
Definition: parameter.h:34
Convenience wrapper for the gui headers.
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:414
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:659
The "current" mailbox.
Definition: context.h:37
void rfc2047_encode_envelope(struct Envelope *env)
Encode the fields of an Envelope.
Definition: rfc2047.c:798
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email&#39;s attachment.
Definition: handler.c:1835
WHERE char * C_SmtpUrl
Config: (smtp) Url of the SMTP server.
Definition: globals.h:138
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg)
Create a message attachment.
Definition: sendlib.c:1517
int whitespace
Definition: sendlib.c:116
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:1449
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:81
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:60
Unknown Content-Type.
Definition: mime.h:31
char * C_Sendmail
Config: External command to send email.
Definition: sendlib.c:91
short C_WrapHeaders
Config: Width to wrap headers in outgoing messages.
Definition: sendlib.c:96
void mutt_message_to_7bit(struct Body *a, FILE *fp)
Convert an email&#39;s MIME parts to 7-bit.
Definition: sendlib.c:1307
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
#define NONULL(x)
Definition: string2.h:37
WHERE bool C_Autocrypt
Config: Enables the Autocrypt feature.
Definition: globals.h:195
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
Base64-encode one character.
Definition: sendlib.c:337
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:63
static int send_msg(const char *path, char **args, const char *msg, char **tempfile)
invoke sendmail in a subshell
Definition: sendlib.c:2601
Miscellaneous email parsing routines.
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition: sendlib.c:1777
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
#define WithCrypto
Definition: lib.h:163
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1360
The envelope/body of an email.
Definition: email.h:37
const char * nntp_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Expand the newsrc filename - Implements format_t.
Definition: newsrc.c:922
static const char *const userhdrs_override_headers[]
The next array/enum pair is used to to keep track of user headers that override pre-defined headers N...
Definition: sendlib.c:127
WHERE bool C_WriteBcc
Config: Write out the &#39;Bcc&#39; field when preparing to send a mail.
Definition: globals.h:254
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
static int bounce_message(FILE *fp, struct Email *e, struct AddressList *to, const char *resent_from, struct AddressList *env_from)
Bounce an email message.
Definition: sendlib.c:3071
#define TAILQ_FIRST(head)
Definition: queue.h:716
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
UserHdrsOverrideIdx
Headers that the user may override.
Definition: sendlib.c:135
bool C_MimeForwardDecode
Config: Decode the forwarded message before attaching it.
Definition: sendlib.c:87
struct Content * content
Detailed info about the content of the attachment.
Definition: body.h:51
bool dot
Has a line consisting of a single dot?
Definition: content.h:44
Override the "Content-Type".
Definition: sendlib.c:137
struct AddressList mail_followup_to
Email&#39;s &#39;mail-followup-to&#39;.
Definition: envelope.h:63
long linemax
Length of the longest line in the file.
Definition: content.h:40
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
Which headers have been overridden.
Definition: sendlib.c:144
#define S_ERR
Definition: string2.h:41
int mutt_ch_fgetconv(struct FgetConv *fc)
Convert a file&#39;s character set.
Definition: charset.c:899
int mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:603
Structs that make up an email.
char bufi[512]
Definition: charset.h:45
bool C_EncodeFrom
Config: Encode &#39;From &#39; as &#39;quote-printable&#39; at the beginning of lines.
Definition: sendlib.c:83
The "currently-open" mailbox.
void mutt_sig_block_system(void)
Block signals before calling exec()
Definition: signal.c:183
struct AddressList reply_to
Email&#39;s &#39;reply-to&#39;.
Definition: envelope.h:62
Autocrypt end-to-end encryption.
#define SEC_NO_FLAGS
No flags are set.
Definition: lib.h:124
void mutt_mailbox_cleanup(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Definition: mutt_mailbox.c:368
#define MUTT_RANDTAG_LEN
Definition: sendlib.c:98
static char * gen_msgid(void)
Generate a unique Message ID.
Definition: sendlib.c:2563
7-bit text
Definition: mime.h:49
static int get_toplevel_encoding(struct Body *a)
Find the most restrictive encoding type.
Definition: sendlib.c:1707
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:42
struct AddressList bcc
Email&#39;s &#39;Bcc&#39; list.
Definition: envelope.h:60
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1414
static void run_mime_type_query(struct Body *att)
Run an external command to determine the MIME type.
Definition: sendlib.c:1617
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.h:79
WHERE char * C_CryptProtectedHeadersSubject
Config: Use this as the subject for encrypted emails.
Definition: globals.h:105
bool C_UserAgent
Config: Add a &#39;User-Agent&#39; head to outgoing mail.
Definition: sendlib.c:95
static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
Base64-encode some data.
Definition: sendlib.c:351
#define SMIME_ENCRYPT
Definition: lib.h:149
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:125
WHERE SIG_ATOMIC_VOLATILE_T SigAlrm
true after SIGALRM is received
Definition: globals.h:78
A normal Email, write full header + MIME headers.
Definition: sendlib.h:60
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition: crypt.c:1083
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
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
struct Body * content
List of MIME parts.
Definition: email.h:90
bool noconv
Don&#39;t do character set conversion.
Definition: body.h:73
static int fold_one_header(FILE *fp, const char *tag, const char *value, size_t vlen, const char *pfx, int wraplen, CopyHeaderFlags chflags)
Fold one header line.
Definition: sendlib.c:1937
static char ** add_args(char **args, size_t *argslen, size_t *argsmax, struct AddressList *al)
Add a list of Addresses to a dynamic array.
Definition: sendlib.c:2786
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
char * mutt_buffer_strdup(struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
#define PGP_ENCRYPT
Definition: lib.h:143
A postponed Email, just the envelope info.
Definition: sendlib.h:62
String manipulation buffer.
Definition: buffer.h:33
void mutt_parse_mime_message(struct Mailbox *m, struct Email *e)
Parse a MIME email.
Definition: mutt_parse.c:48
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
int mutt_copy_message(FILE *fp_out, struct Mailbox *m, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:818
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
struct ListHead userhdrs
user defined headers
Definition: envelope.h:83
WHERE bool C_CryptProtectedHeadersWrite
Config: Generate protected header (Memory Hole) for signed and encrypted emails.
Definition: globals.h:267
#define _(a)
Definition: message.h:28
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:667
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
static char * unfold_header(char *s)
Unfold a wrapped email header.
Definition: sendlib.c:2042
struct Body * next
next attachment in the list
Definition: body.h:53
An email address.
Definition: address.h:34
8-bit text
Definition: mime.h:50
#define MUTT_DATE_NOW
Constant representing the &#39;current time&#39;, see: mutt_date_gmtime(), mutt_date_localtime() ...
Definition: date.h:37
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:265
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:48
FILE * fp_out
File to write to.
Definition: state.h:47
char * mailbox
Mailbox and host address.
Definition: address.h:37
bool dot
Definition: sendlib.c:117
static int b64_init(struct B64Context *bctx)
Set up the base64 conversion.
Definition: sendlib.c:288
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:134
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:123
#define S_BKG
Definition: string2.h:42
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition: copy.h:31
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:778
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone. ...
Definition: date.c:659
long hibin
8-bit characters
Definition: content.h:35
MuttWriteHeaderMode
Modes for mutt_rfc822_write_header()
Definition: sendlib.h:58
bool was_cr
Definition: sendlib.c:119
Flags to control mutt_expando_format()
static int userhdrs_override_cmp(const void *a, const void *b)
Compare a user-defined header with an element of the userhdrs_override_headers list.
Definition: sendlib.c:2249
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:34
static void transform_to_7bit(struct Body *a, FILE *fp_in)
Convert MIME parts to 7-bit.
Definition: sendlib.c:1245
static bool write_as_text_part(struct Body *b)
Should the Body be written as a text MIME part.
Definition: sendlib.c:547
void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
Encode any RFC2047 headers, where required, in an Address list.
Definition: rfc2047.c:737
FILE * fp_in
File to read from.
Definition: state.h:46
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath)
Write email to FCC mailbox.
Definition: sendlib.c:3263
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
int mutt_mb_width(const char *str, int col, bool display)
Measure a string&#39;s display width (in screen columns)
Definition: mbyte.c:139
char * mutt_date_make_date(char *buf, size_t buflen)
Write a date in RFC822 format to a buffer.
Definition: date.c:375
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
The body of an email.
Definition: body.h:34
void mutt_prepare_envelope(struct Envelope *env, bool final)
Prepare an email header.
Definition: sendlib.c:3008
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
struct Content * mutt_get_content_info(const char *fname, struct Body *b)
Analyze file to determine MIME encoding to use.
Definition: sendlib.c:1038
#define MUTT_SET_DRAFT
set the message draft flag
Definition: mx.h:67
Prepare and send an email.
Hundreds of global variables to back the user variables.
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *attach, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject)
Write out one RFC822 header line.
Definition: sendlib.c:2330
char * HomeDir
User&#39;s home directory.
Definition: globals.h:50
Email Address Handling.
bool C_Use8bitmime
Config: Use 8-bit messages and ESMTP to send messages.
Definition: sendlib.c:93
#define SEC_INLINE
Email has an inline signature.
Definition: lib.h:132
Some miscellaneous functions.
#define CH_WEED_DELIVERED
Weed eventual Delivered-To headers.
Definition: copy.h:64
#define mutt_array_size(x)
Definition: memory.h:33
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1181
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:146
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:504
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:132
bool read
Email is read.
Definition: email.h:51
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t *callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:862
char * C_SendCharset
Config: Character sets for outgoing mail.
Definition: email_globals.c:38
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
parse a Message/RFC822 body
Definition: parse.c:1542
struct Mailbox * mailbox
Definition: context.h:50
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:457
Many unsorted constants and some structs.
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
void mutt_generate_boundary(struct ParameterList *pl)
Create a unique boundary id for a MIME part.
Definition: sendlib.c:643
struct Body * mutt_make_file_attach(const char *path)
Create a file attachment.
Definition: sendlib.c:1655
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:60
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:638
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:318
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:551
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition: signal.c:207
void mutt_update_encoding(struct Body *a)
Update the encoding type.
Definition: sendlib.c:1486
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
bool force_charset
Send mode: don&#39;t adjust the character set when in send-mode.
Definition: body.h:74
char * C_Inews
Config: (nntp) External command to post news articles.
Definition: sendlib.c:86
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
static bool check_boundary(const char *boundary, struct Body *b)
check for duplicate boundary
Definition: sendlib.c:1728
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition: charset.c:343
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to null-terminated base64 string.
Definition: base64.c:88
#define ENCODING(x)
Definition: mime.h:85
#define SKIPWS(ch)
Definition: string2.h:46
struct AddressList cc
Email&#39;s &#39;Cc&#39; list.
Definition: envelope.h:59
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
int mutt_istrn_cmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:615
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
struct Message * mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1046
size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition: address.c:1025
int mutt_invoke_sendmail(struct AddressList *from, struct AddressList *to, struct AddressList *cc, struct AddressList *bcc, const char *msg, bool eightbit)
Run sendmail.
Definition: sendlib.c:2828
#define mutt_ch_is_us_ascii(str)
Definition: charset.h:107
Base-64 encoded text.
Definition: mime.h:52
static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
Encode text as quoted printable.
Definition: sendlib.c:155
static void alarm_handler(int sig)
Async notification of an alarm signal.
Definition: sendlib.c:2584
Usenet network mailbox type; talk to an NNTP server.
static char * chs
Definition: gnupgparse.c:72
int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid, bool post, char *fcc, char **finalpath)
Handle FCC with multiple, comma separated entries.
Definition: sendlib.c:3216
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition: sendlib.c:1751
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:531
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:891
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
char * subtype
content-type subtype
Definition: body.h:37
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:117
static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs, bool privacy)
Write user-defined headers and keep track of the interesting ones.
Definition: sendlib.c:2263
#define CH_TXTPLAIN
Generate text/plain MIME headers.
Definition: copy.h:62
Info about an attachment.
Definition: content.h:33
void mutt_param_delete(struct ParameterList *pl, const char *attribute)
Delete a matching Parameter.
Definition: parameter.c:142
#define mutt_b2s(buf)
Definition: buffer.h:41
static char ** add_option(char **args, size_t *argslen, size_t *argsmax, char *s)
Add a string to a dynamic array.
Definition: sendlib.c:2809
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:138
char * x_comment_to
List of &#39;X-comment-to&#39; fields.
Definition: envelope.h:78
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:562
struct ListHead chain
Mixmaster chain.
Definition: email.h:99
bool C_HiddenHost
Config: Don&#39;t use the hostname, just the domain, when generating the message id.
Definition: sendlib.c:85
void mutt_set_followup_to(struct Envelope *env)
Set followup-to field.
Definition: send.c:1151
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition: sendlib.c:3047
bool from
Definition: sendlib.c:115
struct Address * mutt_default_from(void)
Get a default &#39;from&#39; Address.
Definition: send.c:1271
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition: sendlib.c:1847
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:137
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:165
bool C_UseEnvelopeFrom
Config: Set the envelope sender of the message.
Definition: sendlib.c:94
int mutt_bounce_message(FILE *fp, struct Email *e, struct AddressList *to)
Bounce an email message.
Definition: sendlib.c:3133
A local copy of an email.
Definition: mx.h:83
bool binary
Long lines, or CR not in CRLF pair.
Definition: content.h:42
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
iconv_t cd
Definition: charset.h:44
const char * GitVer
A mailbox.
Definition: mailbox.h:81
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1338
#define PATH_MAX
Definition: mutt.h:44
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
char buffer[3]
Definition: sendlib.c:105
"light" mode (used for edit_hdrs)
Definition: sendlib.h:63
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:37
long ascii
Number of ascii chars.
Definition: content.h:39
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition: lib.h:135
Type: &#39;text/*&#39;.
Definition: mime.h:38
bool C_ForwardDecrypt
Config: Decrypt the message when forwarding it.
Definition: sendlib.c:84
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
int nntp_post(struct Mailbox *m, const char *msg)
Post article.
Definition: nntp.c:1977
int mutt_write_mime_body(struct Body *a, FILE *fp)
Write a MIME part.
Definition: sendlib.c:560
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:41
static void set_encoding(struct Body *b, struct Content *info)
determine which Content-Transfer-Encoding to use
Definition: sendlib.c:1393
char * xtype
content-type if x-unknown
Definition: body.h:36
char * C_AttachCharset
Config: When attaching files, use one of these character sets.
Definition: sendlib.c:81
static const char encoded[]
#define CH_XMIT
Transmitting this message? (Ignore Lines: and Content-Length:)
Definition: copy.h:54
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
WHERE char * C_SmimeEncryptWith
Config: Algorithm for encryption.
Definition: globals.h:161
short C_SendmailWait
Config: Time to wait for sendmail to finish.
Definition: sendlib.c:92
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:49
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, int flags)
Set up iconv for conversions.
Definition: charset.c:556
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:49
API for encryption/signing of emails.
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:751
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body&#39;s character set.
Definition: sendlib.c:1462
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:620
char * p
Definition: charset.h:47
fcc mode, like normal mode but for Bcc header
Definition: sendlib.h:61
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:177
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:56
int linelen
Definition: sendlib.c:118
size_t rfc2231_encode_string(struct ParameterList *head, const char *attribute, char *value)
Encode a string to be suitable for an RFC2231 header.
Definition: rfc2231.c:328
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
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
char * description
content-description
Definition: body.h:40
static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
Write the data as raw 8-bit data.
Definition: sendlib.c:379
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:437
static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char *pfx, const char *start, const char *end, CopyHeaderFlags chflags)
Write out one header line.
Definition: sendlib.c:2084
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
WHERE bool C_CryptProtectedHeadersRead
Config: Display protected headers (Memory Hole) in the pager.
Definition: globals.h:265
Override the "User-Agent".
Definition: sendlib.c:138
uint8_t MsgOpenFlags
Flags for mx_msg_open_new(), e.g. MUTT_ADD_FROM.
Definition: mx.h:64
int mutt_strnwidth(const char *s, size_t n)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1351
char bufo[512]
Definition: charset.h:46
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:450
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:133
void mutt_write_addrlist(struct AddressList *al, FILE *fp, int linelen, bool display)
Wrapper for mutt_write_address()
Definition: sendlib.c:1801
const char * mutt_fqdn(bool may_hide_host)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:2536
bool C_MimeSubject
Config: (nntp) Encode the article subject in base64.
Definition: sendlib.c:88
WHERE char * C_DsnReturn
Config: What to send as a notification of message delivery or delay.
Definition: globals.h:108
void rfc2047_encode(char **pd, const char *specials, int col, const char *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:614
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:83
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:48
bool from
Has a line beginning with "From "?
Definition: content.h:43
bool C_BounceDelivered
Config: Add &#39;Delivered-To&#39; to bounced messages.
Definition: sendlib.c:82
Type: &#39;message/*&#39;.
Definition: mime.h:35
#define SEC_SIGN
Email is signed.
Definition: lib.h:126
char * committed_path
the final path generated by mx_msg_commit()
Definition: mx.h:87
#define IS_SPACE(ch)
Definition: string2.h:38
char * personal
Real name of address.
Definition: address.h:36
Keep track when processing files.
#define CH_NOQFROM
Ignore ">From " line.
Definition: copy.h:66
short size
Definition: sendlib.c:106
bool is_overridden[mutt_array_size(userhdrs_override_headers)]
Definition: sendlib.c:146
#define TYPE(body)
Definition: mime.h:83
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:419
GUI display a file/email/help in a viewport with paging.
void mutt_ch_fgetconv_close(struct FgetConv **fc)
Close an fgetconv handle.
Definition: charset.c:879
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:639
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:650
Send email to an SMTP server.
long lobin
Unprintable 7-bit chars (eg., control chars)
Definition: content.h:36
char * data
String.
Definition: list.h:36
char * subject
Email&#39;s subject.
Definition: envelope.h:66
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
char * value
Parameter value.
Definition: parameter.h:35
Duplicate the structure of an entire email.
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1250
Log at debug level 1.
Definition: logging.h:40
bool group
Group mailbox?
Definition: address.h:38
int n
Definition: acutest.h:492
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
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:724
int mutt_do_pager(const char *banner, const char *tempfile, PagerFlags do_color, struct Pager *info)
Display some page-able text to the user.
Definition: curs_lib.c:665
No preferred disposition.
Definition: mime.h:65
enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:1126
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:68
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
WHERE bool C_XCommentTo
Config: (nntp) Add &#39;X-Comment-To&#39; header that contains article author.
Definition: globals.h:278
char * C_MimeTypeQueryCommand
Config: External command to determine the MIME type of an attachment.
Definition: sendlib.c:89
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:49
time_t stamp
Time stamp of last encoding update.
Definition: body.h:61
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:398
short linelen
Definition: sendlib.c:107
#define mutt_error(...)
Definition: logging.h:84
WHERE struct Address * C_EnvelopeFromAddress
Config: Manually set the sender for outgoing messages.
Definition: globals.h:91
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:69
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1160
Binary.
Definition: mime.h:53
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:77
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
Quoted-printable text.
Definition: mime.h:51
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:1425
FILE * fp
pointer to the message data
Definition: mx.h:85
long crlf
\r and \n characters
Definition: content.h:38
Attribute associated with a MIME part.
Definition: parameter.h:32
WHERE char * ShortHostname
Short version of the hostname.
Definition: globals.h:51
#define MUTT_CM_DECODE_PGP
Used for decoding PGP messages.
Definition: copy.h:44
#define FREE(x)
Definition: memory.h:40
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1671
WHERE char * C_Hostname
Config: Fully-qualified domain name of this machine.
Definition: globals.h:111
bool C_Allow8bit
Config: Allow 8-bit messages, don&#39;t use quoted-printable or base64.
Definition: sendlib.c:80
int mutt_write_mime_header(struct Body *a, FILE *fp)
Create a MIME header.
Definition: sendlib.c:401
bool space
Whitespace at the end of lines?
Definition: content.h:41
static void set_noconv_flags(struct Body *b, bool flag)
Set/reset the "x-mutt-noconv" flag.
Definition: sendlib.c:3189
char * language
content-language (RFC8255)
Definition: body.h:38
Keep track when processing files.
Definition: state.h:44
#define STAILQ_EMPTY(head)
Definition: queue.h:345
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:53
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct AddressList sender
Email&#39;s sender.
Definition: envelope.h:61
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:806
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)
Send a message using SMTP.
Definition: smtp.c:765
struct Email * email_new(void)
Create a new Email.
Definition: email.c:68
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a &#39;From&#39; header line?
Definition: from.c:48
char * d_filename
filename to be used for the content-disposition header.
Definition: body.h:47
#define TAILQ_NEXT(elm, field)
Definition: queue.h:825
WHERE char * C_Realname
Config: Real name of the user.
Definition: globals.h:134
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Miscellaneous functions for sending an email.
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
Write protected headers.
Definition: sendlib.h:64
static void b64_flush(struct B64Context *bctx, FILE *fp_out)
Save the bytes to the file.
Definition: sendlib.c:302
#define TAILQ_EMPTY(head)
Definition: queue.h:714
va_list args
Definition: acutest.h:715
int const char int line
Definition: acutest.h:617
Cursor for the Base64 conversion.
Definition: sendlib.c:103
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:46
Log at debug level 5.
Definition: logging.h:44
WHERE char * C_PgpSignAs
Config: Use this alternative key for signing messages.
Definition: globals.h:156
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
static int print_val(FILE *fp, const char *pfx, const char *value, CopyHeaderFlags chflags, size_t col)
Add pieces to an email header, wrapping where necessary.
Definition: sendlib.c:1890
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
struct FgetConv * mutt_ch_fgetconv_open(FILE *fp, const char *from, const char *to, int flags)
Prepare a file for charset conversion.
Definition: charset.c:849
WHERE char * C_SmimeSignAs
Config: Use this alternative key for signing messages.
Definition: globals.h:160
A List node for strings.
Definition: list.h:34
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:66
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:630
Decide how to display email content.
Content is inline.
Definition: mime.h:62
void mutt_need_hard_redraw(void)
Force a hard refresh.
Definition: curs_lib.c:128
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1105
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
struct Email * email
header information for message/rfc822
Definition: body.h:55
int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp)
Write the Autocrypt gossip headers to a file.
Definition: autocrypt.c:762
long nulbin
Null characters (0x0)
Definition: content.h:37
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:1147
bool cr
Has CR, even when in a CRLF pair.
Definition: content.h:45
Cursor for converting a file&#39;s encoding.
Definition: charset.h:41
void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
Copy a string and wrap it in quotes if it contains special characters.
Definition: address.c:681
int mutt_write_one_header(FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, CopyHeaderFlags chflags)
Write one header line to a file.
Definition: sendlib.c:2159
void mutt_rand_base32(void *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition: muttlib.c:513
WHERE char * C_DsnNotify
Config: Request notification for message delivery or delay.
Definition: globals.h:107
Type: &#39;application/*&#39;.
Definition: mime.h:33
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:979
static char ** add_args_one(char **args, size_t *argslen, size_t *argsmax, struct Address *addr)
Add an Address to a dynamic array.
Definition: sendlib.c:2766
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:959
#define EX_OK
Definition: sendlib.c:73
#define CH_NONEWLINE
Don&#39;t output terminating newline after the header.
Definition: copy.h:59
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition: pager.h:43
static void encode_headers(struct ListHead *h)
RFC2047-encode a list of headers.
Definition: sendlib.c:2499
The header of an Email.
Definition: envelope.h:54
bool C_Weed
Config: Filter headers when displaying/forwarding/printing/replying.
Definition: email_globals.c:40
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1434
ContentType
Content-Type.
Definition: mime.h:29
bool C_MimeTypeQueryFirst
Config: Run the C_MimeTypeQueryCommand before the mime.types lookup.
Definition: sendlib.c:90
int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp)
Write the Autocrypt header to a file.
Definition: autocrypt.c:725