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