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