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