1 /*
2  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #define _SENDLIB_C 1
20 
21 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include "mutt.h"
26 #include "mutt_curses.h"
27 #include "rfc2047.h"
28 #include "rfc2231.h"
29 #include "mx.h"
30 #include "mime.h"
31 #include "mailbox.h"
32 #include "copy.h"
33 #include "pager.h"
34 #include "charset.h"
35 #include "mutt_crypt.h"
36 #include "mutt_idna.h"
37 #include "buffy.h"
38 
39 #include <string.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <ctype.h>
44 #include <sys/stat.h>
45 #include <signal.h>
46 #include <sys/wait.h>
47 #include <fcntl.h>
48 
49 #ifdef HAVE_SYSEXITS_H
50 #include <sysexits.h>
51 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
52 #define EX_OK 0
53 #endif
54 
55 /* If you are debugging this file, comment out the following line. */
56 /*#define NDEBUG*/
57 
58 #ifdef NDEBUG
59 #define assert(x)
60 #else
61 #include <assert.h>
62 #endif
63 
64 extern char RFC822Specials[];
65 
66 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
67 
68 char B64Chars[64] = {
69   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
70   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
71   'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
72   't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
73   '8', '9', '+', '/'
74 };
75 
76 static char MsgIdPfx = 'A';
77 
78 static void transform_to_7bit (BODY *a, FILE *fpin);
79 
encode_quoted(FGETCONV * fc,FILE * fout,int istext)80 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
81 {
82   int c, linelen = 0;
83   char line[77], savechar;
84 
85   while ((c = fgetconv (fc)) != EOF)
86   {
87     /* Wrap the line if needed. */
88     if (linelen == 76 && ((istext && c != '\n') || !istext))
89     {
90       /* If the last character is "quoted", then be sure to move all three
91        * characters to the next line.  Otherwise, just move the last
92        * character...
93        */
94       if (line[linelen-3] == '=')
95       {
96         line[linelen-3] = 0;
97         fputs (line, fout);
98         fputs ("=\n", fout);
99         line[linelen] = 0;
100         line[0] = '=';
101         line[1] = line[linelen-2];
102         line[2] = line[linelen-1];
103         linelen = 3;
104       }
105       else
106       {
107         savechar = line[linelen-1];
108         line[linelen-1] = '=';
109         line[linelen] = 0;
110         fputs (line, fout);
111         fputc ('\n', fout);
112         line[0] = savechar;
113         linelen = 1;
114       }
115     }
116 
117     /* Escape lines that begin with/only contain "the message separator". */
118     if (linelen == 4 && !mutt_strncmp ("From", line, 4))
119     {
120       strfcpy (line, "=46rom", sizeof (line));
121       linelen = 6;
122     }
123     else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
124     {
125       strfcpy (line, "=66rom", sizeof (line));
126       linelen = 6;
127     }
128     else if (linelen == 1 && line[0] == '.')
129     {
130       strfcpy (line, "=2E", sizeof (line));
131       linelen = 3;
132     }
133 
134 
135     if (c == '\n' && istext)
136     {
137       /* Check to make sure there is no trailing space on this line. */
138       if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
139       {
140         if (linelen < 74)
141 	{
142           sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
143           fputs (line, fout);
144         }
145         else
146 	{
147           int savechar = line[linelen-1];
148 
149           line[linelen-1] = '=';
150           line[linelen] = 0;
151           fputs (line, fout);
152           fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
153         }
154       }
155       else
156       {
157         line[linelen] = 0;
158         fputs (line, fout);
159       }
160       fputc ('\n', fout);
161       linelen = 0;
162     }
163     else if (c != 9 && (c < 32 || c > 126 || c == '='))
164     {
165       /* Check to make sure there is enough room for the quoted character.
166        * If not, wrap to the next line.
167        */
168       if (linelen > 73)
169       {
170         line[linelen++] = '=';
171         line[linelen] = 0;
172         fputs (line, fout);
173         fputc ('\n', fout);
174         linelen = 0;
175       }
176       sprintf (line+linelen,"=%2.2X", (unsigned char) c);
177       linelen += 3;
178     }
179     else
180     {
181       /* Don't worry about wrapping the line here.  That will happen during
182        * the next iteration when I'll also know what the next character is.
183        */
184       line[linelen++] = c;
185     }
186   }
187 
188   /* Take care of anything left in the buffer */
189   if (linelen > 0)
190   {
191     if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
192     {
193       /* take care of trailing whitespace */
194       if (linelen < 74)
195         sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
196       else
197       {
198         savechar = line[linelen-1];
199         line[linelen-1] = '=';
200         line[linelen] = 0;
201         fputs (line, fout);
202         fputc ('\n', fout);
203         sprintf (line, "=%2.2X", (unsigned char) savechar);
204       }
205     }
206     else
207       line[linelen] = 0;
208     fputs (line, fout);
209   }
210 }
211 
212 static char b64_buffer[3];
213 static short b64_num;
214 static short b64_linelen;
215 
b64_flush(FILE * fout)216 static void b64_flush(FILE *fout)
217 {
218   short i;
219 
220   if(!b64_num)
221     return;
222 
223   if(b64_linelen >= 72)
224   {
225     fputc('\n', fout);
226     b64_linelen = 0;
227   }
228 
229   for(i = b64_num; i < 3; i++)
230     b64_buffer[i] = '\0';
231 
232   fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
233   b64_linelen++;
234   fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
235   b64_linelen++;
236 
237   if(b64_num > 1)
238   {
239     fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
240     b64_linelen++;
241     if(b64_num > 2)
242     {
243       fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
244       b64_linelen++;
245     }
246   }
247 
248   while(b64_linelen % 4)
249   {
250     fputc('=', fout);
251     b64_linelen++;
252   }
253 
254   b64_num = 0;
255 }
256 
257 
b64_putc(char c,FILE * fout)258 static void b64_putc(char c, FILE *fout)
259 {
260   if(b64_num == 3)
261     b64_flush(fout);
262 
263   b64_buffer[b64_num++] = c;
264 }
265 
266 
encode_base64(FGETCONV * fc,FILE * fout,int istext)267 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
268 {
269   int ch, ch1 = EOF;
270 
271   b64_num = b64_linelen = 0;
272 
273   while ((ch = fgetconv (fc)) != EOF)
274   {
275     if (istext && ch == '\n' && ch1 != '\r')
276       b64_putc('\r', fout);
277     b64_putc(ch, fout);
278     ch1 = ch;
279   }
280   b64_flush(fout);
281   fputc('\n', fout);
282 }
283 
encode_8bit(FGETCONV * fc,FILE * fout,int istext)284 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
285 {
286   int ch;
287 
288   while ((ch = fgetconv (fc)) != EOF)
289     fputc (ch, fout);
290 }
291 
292 
mutt_write_mime_header(BODY * a,FILE * f)293 int mutt_write_mime_header (BODY *a, FILE *f)
294 {
295   PARAMETER *p;
296   char buffer[STRING];
297   char *t;
298   char *fn;
299   int len;
300   int tmplen;
301   int encode;
302 
303   fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
304 
305   if (a->parameter)
306   {
307     len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
308 
309     for(p = a->parameter; p; p = p->next)
310     {
311       char *tmp;
312 
313       if(!p->value)
314 	continue;
315 
316       fputc (';', f);
317 
318       buffer[0] = 0;
319       tmp = safe_strdup (p->value);
320       encode = rfc2231_encode_string (&tmp);
321       rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
322 
323       /* Dirty hack to make messages readable by Outlook Express
324        * for the Mac: force quotes around the boundary parameter
325        * even when they aren't needed.
326        */
327 
328       if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
329 	snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
330 
331       FREE (&tmp);
332 
333       tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
334 
335       if (len + tmplen + 2 > 76)
336       {
337 	fputs ("\n\t", f);
338 	len = tmplen + 8;
339       }
340       else
341       {
342 	fputc (' ', f);
343 	len += tmplen + 1;
344       }
345 
346       fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
347 
348     }
349   }
350 
351   if (a->use_disp && option (OPTCREATERFC2047PARAMS))
352   {
353     if(!(fn = a->d_filename))
354       fn = a->filename;
355 
356     if (fn)
357     {
358       char *tmp;
359 
360       /* Strip off the leading path... */
361       if ((t = strrchr (fn, '/')))
362 	t++;
363       else
364         t = fn;
365 
366       buffer[0] = 0;
367       tmp = safe_strdup (t);
368       rfc2047_encode_string (&tmp);
369       rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
370       FREE (&tmp);
371       fprintf (f, ";\n\tname=%s", buffer);
372     }
373   }
374 
375   fputc ('\n', f);
376 
377   if (a->description)
378     fprintf(f, "Content-Description: %s\n", a->description);
379 
380   if (a->disposition != DISPNONE)
381   {
382     const char *dispstr[] = {
383       "inline",
384       "attachment",
385       "form-data"
386     };
387 
388     if (a->disposition < sizeof(dispstr)/sizeof(char*))
389     {
390       fprintf (f, "Content-Disposition: %s", dispstr[a->disposition]);
391 
392       if (a->use_disp)
393       {
394 	if (!(fn = a->d_filename))
395 	  fn = a->filename;
396 
397 	if (fn)
398 	{
399 	  char *tmp;
400 
401 	  /* Strip off the leading path... */
402 	  if ((t = strrchr (fn, '/')))
403 	    t++;
404 	  else
405 	    t = fn;
406 
407 	  buffer[0] = 0;
408 	  tmp = safe_strdup (t);
409 	  encode = rfc2231_encode_string (&tmp);
410 	  rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
411 	  FREE (&tmp);
412 	  fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
413 	}
414       }
415 
416       fputc ('\n', f);
417     }
418     else
419     {
420       dprint(1, (debugfile, "ERROR: invalid content-disposition %d\n", a->disposition));
421     }
422   }
423 
424   if (a->encoding != ENC7BIT)
425     fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
426 
427   /* Do NOT add the terminator here!!! */
428   return (ferror (f) ? -1 : 0);
429 }
430 
431 # define write_as_text_part(a)  (mutt_is_text_part(a) \
432                                  || ((WithCrypto & APPLICATION_PGP)\
433                                       && mutt_is_application_pgp(a)))
434 
mutt_write_mime_body(BODY * a,FILE * f)435 int mutt_write_mime_body (BODY *a, FILE *f)
436 {
437   char *p, boundary[SHORT_STRING];
438   char send_charset[SHORT_STRING];
439   FILE *fpin;
440   BODY *t;
441   FGETCONV *fc;
442 
443   if (a->type == TYPEMULTIPART)
444   {
445     /* First, find the boundary to use */
446     if (!(p = mutt_get_parameter ("boundary", a->parameter)))
447     {
448       dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
449       mutt_error _("No boundary parameter found! [report this error]");
450       return (-1);
451     }
452     strfcpy (boundary, p, sizeof (boundary));
453 
454     for (t = a->parts; t ; t = t->next)
455     {
456       fprintf (f, "\n--%s\n", boundary);
457       if (mutt_write_mime_header (t, f) == -1)
458 	return -1;
459       fputc ('\n', f);
460       if (mutt_write_mime_body (t, f) == -1)
461 	return -1;
462     }
463     fprintf (f, "\n--%s--\n", boundary);
464     return (ferror (f) ? -1 : 0);
465   }
466 
467   /* This is pretty gross, but it's the best solution for now... */
468   if ((WithCrypto & APPLICATION_PGP)
469       && a->type == TYPEAPPLICATION
470       && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
471   {
472     fputs ("Version: 1\n", f);
473     return 0;
474   }
475 
476   if ((fpin = fopen (a->filename, "r")) == NULL)
477   {
478     dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
479     mutt_error (_("%s no longer exists!"), a->filename);
480     return -1;
481   }
482 
483   if (a->type == TYPETEXT && (!a->noconv))
484     fc = fgetconv_open (fpin, a->charset,
485 			mutt_get_body_charset (send_charset, sizeof (send_charset), a),
486 			0);
487   else
488     fc = fgetconv_open (fpin, 0, 0, 0);
489 
490   if (a->encoding == ENCQUOTEDPRINTABLE)
491     encode_quoted (fc, f, write_as_text_part (a));
492   else if (a->encoding == ENCBASE64)
493     encode_base64 (fc, f, write_as_text_part (a));
494   else if (a->type == TYPETEXT && (!a->noconv))
495     encode_8bit (fc, f, write_as_text_part (a));
496   else
497     mutt_copy_stream (fpin, f);
498 
499   fgetconv_close (&fc);
500   safe_fclose (&fpin);
501 
502   return (ferror (f) ? -1 : 0);
503 }
504 
505 #undef write_as_text_part
506 
507 #define BOUNDARYLEN 16
mutt_generate_boundary(PARAMETER ** parm)508 void mutt_generate_boundary (PARAMETER **parm)
509 {
510   char rs[BOUNDARYLEN + 1];
511   char *p = rs;
512   int i;
513 
514   rs[BOUNDARYLEN] = 0;
515   for (i=0;i<BOUNDARYLEN;i++)
516     *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
517   *p = 0;
518 
519   mutt_set_parameter ("boundary", rs, parm);
520 }
521 
522 typedef struct
523 {
524   int from;
525   int whitespace;
526   int dot;
527   int linelen;
528   int was_cr;
529 }
530 CONTENT_STATE;
531 
532 
update_content_info(CONTENT * info,CONTENT_STATE * s,char * d,size_t dlen)533 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
534 {
535   int from = s->from;
536   int whitespace = s->whitespace;
537   int dot = s->dot;
538   int linelen = s->linelen;
539   int was_cr = s->was_cr;
540 
541   if (!d) /* This signals EOF */
542   {
543     if (was_cr)
544       info->binary = 1;
545     if (linelen > info->linemax)
546       info->linemax = linelen;
547 
548     return;
549   }
550 
551   for (; dlen; d++, dlen--)
552   {
553     char ch = *d;
554 
555     if (was_cr)
556     {
557       was_cr = 0;
558       if (ch != '\n')
559       {
560         info->binary = 1;
561       }
562       else
563       {
564         if (whitespace) info->space = 1;
565 	if (dot) info->dot = 1;
566         if (linelen > info->linemax) info->linemax = linelen;
567         whitespace = 0;
568 	dot = 0;
569         linelen = 0;
570 	continue;
571       }
572     }
573 
574     linelen++;
575     if (ch == '\n')
576     {
577       info->crlf++;
578       if (whitespace) info->space = 1;
579       if (dot) info->dot = 1;
580       if (linelen > info->linemax) info->linemax = linelen;
581       whitespace = 0;
582       linelen = 0;
583       dot = 0;
584     }
585     else if (ch == '\r')
586     {
587       info->crlf++;
588       info->cr = 1;
589       was_cr = 1;
590       continue;
591     }
592     else if (ch & 0x80)
593       info->hibin++;
594     else if (ch == '\t' || ch == '\f')
595     {
596       info->ascii++;
597       whitespace++;
598     }
599     else if (ch < 32 || ch == 127)
600       info->lobin++;
601     else
602     {
603       if (linelen == 1)
604       {
605         if ((ch == 'F') || (ch == 'f'))
606           from = 1;
607         else
608           from = 0;
609         if (ch == '.')
610           dot = 1;
611         else
612           dot = 0;
613       }
614       else if (from)
615       {
616         if (linelen == 2 && ch != 'r') from = 0;
617         else if (linelen == 3 && ch != 'o') from = 0;
618         else if (linelen == 4)
619 	{
620           if (ch == 'm') info->from = 1;
621           from = 0;
622         }
623       }
624       if (ch == ' ') whitespace++;
625       info->ascii++;
626     }
627 
628     if (linelen > 1) dot = 0;
629     if (ch != ' ' && ch != '\t') whitespace = 0;
630   }
631 
632   s->from = from;
633   s->whitespace = whitespace;
634   s->dot = dot;
635   s->linelen = linelen;
636   s->was_cr = was_cr;
637 
638 }
639 
640 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
641 #define BUGGY_ICONV 1
642 
643 /*
644  * Find the best charset conversion of the file from fromcode into one
645  * of the tocodes. If successful, set *tocode and CONTENT *info and
646  * return the number of characters converted inexactly. If no
647  * conversion was possible, return -1.
648  *
649  * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
650  * which would otherwise prevent us from knowing the number of inexact
651  * conversions. Where the candidate target charset is UTF-8 we avoid
652  * doing the second conversion because iconv_open("UTF-8", "UTF-8")
653  * fails with some libraries.
654  *
655  * We assume that the output from iconv is never more than 4 times as
656  * long as the input for any pair of charsets we might be interested
657  * in.
658  */
convert_file_to(FILE * file,const char * fromcode,int ncodes,const char ** tocodes,int * tocode,CONTENT * info)659 static size_t convert_file_to (FILE *file, const char *fromcode,
660 			       int ncodes, const char **tocodes,
661 			       int *tocode, CONTENT *info)
662 {
663 #ifdef HAVE_ICONV
664   iconv_t cd1, *cd;
665   char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
666   ICONV_CONST char *ib, *ub;
667   char *ob;
668   size_t ibl, obl, ubl, ubl1, n, ret;
669   int i;
670   CONTENT *infos;
671   CONTENT_STATE *states;
672   size_t *score;
673 
674   cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
675   if (cd1 == (iconv_t)(-1))
676     return -1;
677 
678   cd     = safe_calloc (ncodes, sizeof (iconv_t));
679   score  = safe_calloc (ncodes, sizeof (size_t));
680   states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
681   infos  = safe_calloc (ncodes, sizeof (CONTENT));
682 
683   for (i = 0; i < ncodes; i++)
684     if (ascii_strcasecmp (tocodes[i], "utf-8"))
685       cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
686     else
687       /* Special case for conversion to UTF-8 */
688       cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
689 
690   rewind (file);
691   ibl = 0;
692   for (;;)
693   {
694 
695     /* Try to fill input buffer */
696     n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
697     ibl += n;
698 
699     /* Convert to UTF-8 */
700     ib = bufi;
701     ob = bufu, obl = sizeof (bufu);
702     n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
703     assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
704     if (n == (size_t)(-1) &&
705 	((errno != EINVAL && errno != E2BIG) || ib == bufi))
706     {
707       assert (errno == EILSEQ ||
708 	      (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
709       ret = (size_t)(-1);
710       break;
711     }
712     ubl1 = ob - bufu;
713 
714     /* Convert from UTF-8 */
715     for (i = 0; i < ncodes; i++)
716       if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
717       {
718 	ub = bufu, ubl = ubl1;
719 	ob = bufo, obl = sizeof (bufo);
720 	n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
721 	if (n == (size_t)(-1))
722 	{
723 	  assert (errno == E2BIG ||
724 		  (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
725 	  score[i] = (size_t)(-1);
726 	}
727 	else
728 	{
729 	  score[i] += n;
730 	  update_content_info (&infos[i], &states[i], bufo, ob - bufo);
731 	}
732       }
733       else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
734 	/* Special case for conversion to UTF-8 */
735 	update_content_info (&infos[i], &states[i], bufu, ubl1);
736 
737     if (ibl)
738       /* Save unused input */
739       memmove (bufi, ib, ibl);
740     else if (!ubl1 && ib < bufi + sizeof (bufi))
741     {
742       ret = 0;
743       break;
744     }
745   }
746 
747   if (!ret)
748   {
749     /* Find best score */
750     ret = (size_t)(-1);
751     for (i = 0; i < ncodes; i++)
752     {
753       if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
754       {
755 	/* Special case for conversion to UTF-8 */
756 	*tocode = i;
757 	ret = 0;
758 	break;
759       }
760       else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
761 	continue;
762       else if (ret == (size_t)(-1) || score[i] < ret)
763       {
764 	*tocode = i;
765 	ret = score[i];
766 	if (!ret)
767 	  break;
768       }
769     }
770     if (ret != (size_t)(-1))
771     {
772       memcpy (info, &infos[*tocode], sizeof(CONTENT));
773       update_content_info (info, &states[*tocode], 0, 0); /* EOF */
774     }
775   }
776 
777   for (i = 0; i < ncodes; i++)
778     if (cd[i] != (iconv_t)(-1))
779       iconv_close (cd[i]);
780 
781   iconv_close (cd1);
782   FREE (&cd);
783   FREE (&infos);
784   FREE (&score);
785   FREE (&states);
786 
787   return ret;
788 #else
789   return -1;
790 #endif /* !HAVE_ICONV */
791 }
792 
793 /*
794  * Find the first of the fromcodes that gives a valid conversion and
795  * the best charset conversion of the file into one of the tocodes. If
796  * successful, set *fromcode and *tocode to dynamically allocated
797  * strings, set CONTENT *info, and return the number of characters
798  * converted inexactly. If no conversion was possible, return -1.
799  *
800  * Both fromcodes and tocodes may be colon-separated lists of charsets.
801  * However, if fromcode is zero then fromcodes is assumed to be the
802  * name of a single charset even if it contains a colon.
803  */
convert_file_from_to(FILE * file,const char * fromcodes,const char * tocodes,char ** fromcode,char ** tocode,CONTENT * info)804 static size_t convert_file_from_to (FILE *file,
805 				    const char *fromcodes, const char *tocodes,
806 				    char **fromcode, char **tocode, CONTENT *info)
807 {
808   char *fcode = NULL;
809   char **tcode;
810   const char *c, *c1;
811   size_t ret;
812   int ncodes, i, cn;
813 
814   /* Count the tocodes */
815   ncodes = 0;
816   for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
817   {
818     if ((c1 = strchr (c, ':')) == c)
819       continue;
820     ++ncodes;
821   }
822 
823   /* Copy them */
824   tcode = safe_malloc (ncodes * sizeof (char *));
825   for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
826   {
827     if ((c1 = strchr (c, ':')) == c)
828       continue;
829     tcode[i] = mutt_substrdup (c, c1);
830   }
831 
832   ret = (size_t)(-1);
833   if (fromcode)
834   {
835     /* Try each fromcode in turn */
836     for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
837     {
838       if ((c1 = strchr (c, ':')) == c)
839 	continue;
840       fcode = mutt_substrdup (c, c1);
841 
842       ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
843 			     &cn, info);
844       if (ret != (size_t)(-1))
845       {
846 	*fromcode = fcode;
847 	*tocode = tcode[cn];
848 	tcode[cn] = 0;
849 	break;
850       }
851       FREE (&fcode);
852     }
853   }
854   else
855   {
856     /* There is only one fromcode */
857     ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
858 			   &cn, info);
859     if (ret != (size_t)(-1))
860     {
861       *tocode = tcode[cn];
862       tcode[cn] = 0;
863     }
864   }
865 
866   /* Free memory */
867   for (i = 0; i < ncodes; i++)
868     FREE (&tcode[i]);
869 
870   FREE (&tcode);
871 
872   return ret;
873 }
874 
875 /*
876  * Analyze the contents of a file to determine which MIME encoding to use.
877  * Also set the body charset, sometimes, or not.
878  */
mutt_get_content_info(const char * fname,BODY * b)879 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
880 {
881   CONTENT *info;
882   CONTENT_STATE state;
883   FILE *fp = NULL;
884   char *fromcode = NULL;
885   char *tocode;
886   char buffer[100];
887   char chsbuf[STRING];
888   size_t r;
889 
890   struct stat sb;
891 
892   if(b && !fname) fname = b->filename;
893 
894   if (stat (fname, &sb) == -1)
895   {
896     mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
897     return NULL;
898   }
899 
900   if (!S_ISREG(sb.st_mode))
901   {
902     mutt_error (_("%s isn't a regular file."), fname);
903     return NULL;
904   }
905 
906   if ((fp = fopen (fname, "r")) == NULL)
907   {
908     dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
909 		fname, strerror (errno), errno));
910     return (NULL);
911   }
912 
913   info = safe_calloc (1, sizeof (CONTENT));
914   memset (&state, 0, sizeof (state));
915 
916   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
917   {
918     char *chs = mutt_get_parameter ("charset", b->parameter);
919     char *fchs = b->use_disp ? ((AttachCharset && *AttachCharset) ?
920                                 AttachCharset : Charset) : Charset;
921     if (Charset && (chs || SendCharset) &&
922         convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
923                               &fromcode, &tocode, info) != (size_t)(-1))
924     {
925       if (!chs)
926       {
927 	mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
928 	mutt_set_parameter ("charset", chsbuf, &b->parameter);
929       }
930       b->charset = fromcode;
931       FREE (&tocode);
932       safe_fclose (&fp);
933       return info;
934     }
935   }
936 
937   rewind (fp);
938   while ((r = fread (buffer, 1, sizeof(buffer), fp)))
939     update_content_info (info, &state, buffer, r);
940   update_content_info (info, &state, 0, 0);
941 
942   safe_fclose (&fp);
943 
944   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
945     mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
946 				    Charset  && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
947 			&b->parameter);
948 
949   return info;
950 }
951 
952 /* Given a file with path ``s'', see if there is a registered MIME type.
953  * returns the major MIME type, and copies the subtype to ``d''.  First look
954  * for ~/.mime.types, then look in a system mime.types if we can find one.
955  * The longest match is used so that we can match `ps.gz' when `gz' also
956  * exists.
957  */
958 
mutt_lookup_mime_type(BODY * att,const char * path)959 int mutt_lookup_mime_type (BODY *att, const char *path)
960 {
961   FILE *f;
962   char *p, *q, *ct;
963   char buf[LONG_STRING];
964   char subtype[STRING], xtype[STRING];
965   int count;
966   int szf, sze, cur_sze;
967   int type;
968 
969   *subtype = '\0';
970   *xtype   = '\0';
971   type     = TYPEOTHER;
972   cur_sze  = 0;
973 
974   szf      = mutt_strlen (path);
975 
976   for (count = 0 ; count < 3 ; count++)
977   {
978     /*
979      * can't use strtok() because we use it in an inner loop below, so use
980      * a switch statement here instead.
981      */
982     switch (count)
983     {
984       case 0:
985 	snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
986 	break;
987       case 1:
988 	strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
989 	break;
990       case 2:
991 	strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
992 	break;
993       default:
994         dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
995 	goto bye;	/* shouldn't happen */
996     }
997 
998     if ((f = fopen (buf, "r")) != NULL)
999     {
1000       while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1001       {
1002 	/* weed out any comments */
1003 	if ((p = strchr (buf, '#')))
1004 	  *p = 0;
1005 
1006 	/* remove any leading space. */
1007 	ct = buf;
1008 	SKIPWS (ct);
1009 
1010 	/* position on the next field in this line */
1011 	if ((p = strpbrk (ct, " \t")) == NULL)
1012 	  continue;
1013 	*p++ = 0;
1014 	SKIPWS (p);
1015 
1016 	/* cycle through the file extensions */
1017 	while ((p = strtok (p, " \t\n")))
1018 	{
1019 	  sze = mutt_strlen (p);
1020 	  if ((sze > cur_sze) && (szf >= sze) &&
1021 	      (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1022 	      (szf == sze || path[szf - sze - 1] == '.'))
1023 	  {
1024 	    /* get the content-type */
1025 
1026 	    if ((p = strchr (ct, '/')) == NULL)
1027 	    {
1028 	      /* malformed line, just skip it. */
1029 	      break;
1030 	    }
1031 	    *p++ = 0;
1032 
1033 	    for (q = p; *q && !ISSPACE (*q); q++)
1034 	      ;
1035 
1036 	    mutt_substrcpy (subtype, p, q, sizeof (subtype));
1037 
1038 	    if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1039 	      strfcpy (xtype, ct, sizeof (xtype));
1040 
1041 	    cur_sze = sze;
1042 	  }
1043 	  p = NULL;
1044 	}
1045       }
1046       safe_fclose (&f);
1047     }
1048   }
1049 
1050  bye:
1051 
1052   if (type != TYPEOTHER || *xtype != '\0')
1053   {
1054     att->type = type;
1055     mutt_str_replace (&att->subtype, subtype);
1056     mutt_str_replace (&att->xtype, xtype);
1057   }
1058 
1059   return (type);
1060 }
1061 
mutt_message_to_7bit(BODY * a,FILE * fp)1062 void mutt_message_to_7bit (BODY *a, FILE *fp)
1063 {
1064   char temp[_POSIX_PATH_MAX];
1065   char *line = NULL;
1066   FILE *fpin = NULL;
1067   FILE *fpout = NULL;
1068   struct stat sb;
1069 
1070   if (!a->filename && fp)
1071     fpin = fp;
1072   else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1073   {
1074     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1075     return;
1076   }
1077   else
1078   {
1079     a->offset = 0;
1080     if (stat (a->filename, &sb) == -1)
1081     {
1082       mutt_perror ("stat");
1083       safe_fclose (&fpin);
1084     }
1085     a->length = sb.st_size;
1086   }
1087 
1088   mutt_mktemp (temp, sizeof (temp));
1089   if (!(fpout = safe_fopen (temp, "w+")))
1090   {
1091     mutt_perror ("fopen");
1092     goto cleanup;
1093   }
1094 
1095   fseeko (fpin, a->offset, 0);
1096   a->parts = mutt_parse_messageRFC822 (fpin, a);
1097 
1098   transform_to_7bit (a->parts, fpin);
1099 
1100   mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1101 		 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1102 
1103   fputs ("MIME-Version: 1.0\n", fpout);
1104   mutt_write_mime_header (a->parts, fpout);
1105   fputc ('\n', fpout);
1106   mutt_write_mime_body (a->parts, fpout);
1107 
1108  cleanup:
1109   FREE (&line);
1110 
1111   if (fpin && fpin != fp)
1112     safe_fclose (&fpin);
1113   if (fpout)
1114     safe_fclose (&fpout);
1115   else
1116     return;
1117 
1118   a->encoding = ENC7BIT;
1119   a->d_filename = a->filename;
1120   if (a->filename && a->unlink)
1121     unlink (a->filename);
1122   a->filename = safe_strdup (temp);
1123   a->unlink = 1;
1124   if(stat (a->filename, &sb) == -1)
1125   {
1126     mutt_perror ("stat");
1127     return;
1128   }
1129   a->length = sb.st_size;
1130   mutt_free_body (&a->parts);
1131   a->hdr->content = NULL;
1132 }
1133 
transform_to_7bit(BODY * a,FILE * fpin)1134 static void transform_to_7bit (BODY *a, FILE *fpin)
1135 {
1136   char buff[_POSIX_PATH_MAX];
1137   STATE s;
1138   struct stat sb;
1139 
1140   memset (&s, 0, sizeof (s));
1141   for (; a; a = a->next)
1142   {
1143     if (a->type == TYPEMULTIPART)
1144     {
1145       if (a->encoding != ENC7BIT)
1146 	a->encoding = ENC7BIT;
1147 
1148       transform_to_7bit (a->parts, fpin);
1149     }
1150     else if (mutt_is_message_type(a->type, a->subtype))
1151     {
1152       mutt_message_to_7bit (a, fpin);
1153     }
1154     else
1155     {
1156       a->noconv = 1;
1157       a->force_charset = 1;
1158 
1159       mutt_mktemp (buff, sizeof (buff));
1160       if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1161       {
1162 	mutt_perror ("fopen");
1163 	return;
1164       }
1165       s.fpin = fpin;
1166       mutt_decode_attachment (a, &s);
1167       safe_fclose (&s.fpout);
1168       a->d_filename = a->filename;
1169       a->filename = safe_strdup (buff);
1170       a->unlink = 1;
1171       if (stat (a->filename, &sb) == -1)
1172       {
1173 	mutt_perror ("stat");
1174 	return;
1175       }
1176       a->length = sb.st_size;
1177 
1178       mutt_update_encoding (a);
1179       if (a->encoding == ENC8BIT)
1180 	a->encoding = ENCQUOTEDPRINTABLE;
1181       else if(a->encoding == ENCBINARY)
1182 	a->encoding = ENCBASE64;
1183     }
1184   }
1185 }
1186 
1187 /* determine which Content-Transfer-Encoding to use */
mutt_set_encoding(BODY * b,CONTENT * info)1188 static void mutt_set_encoding (BODY *b, CONTENT *info)
1189 {
1190   char send_charset[SHORT_STRING];
1191 
1192   if (b->type == TYPETEXT)
1193   {
1194     char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1195     if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1196       b->encoding = ENCQUOTEDPRINTABLE;
1197     else if (info->hibin)
1198       b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1199     else
1200       b->encoding = ENC7BIT;
1201   }
1202   else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1203   {
1204     if (info->lobin || info->hibin)
1205     {
1206       if (option (OPTALLOW8BIT) && !info->lobin)
1207 	b->encoding = ENC8BIT;
1208       else
1209 	mutt_message_to_7bit (b, NULL);
1210     }
1211     else
1212       b->encoding = ENC7BIT;
1213   }
1214   else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1215     b->encoding = ENC7BIT;
1216   else
1217 #if 0
1218     if (info->lobin || info->hibin || info->binary || info->linemax > 990
1219 	   || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1220 #endif
1221   {
1222     /* Determine which encoding is smaller  */
1223     if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1224 	 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1225       b->encoding = ENCBASE64;
1226     else
1227       b->encoding = ENCQUOTEDPRINTABLE;
1228   }
1229 #if 0
1230   else
1231     b->encoding = ENC7BIT;
1232 #endif
1233 }
1234 
mutt_stamp_attachment(BODY * a)1235 void mutt_stamp_attachment(BODY *a)
1236 {
1237   a->stamp = time(NULL);
1238 }
1239 
1240 /* Get a body's character set */
1241 
mutt_get_body_charset(char * d,size_t dlen,BODY * b)1242 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1243 {
1244   char *p = NULL;
1245 
1246   if (b && b->type != TYPETEXT)
1247     return NULL;
1248 
1249   if (b)
1250     p = mutt_get_parameter ("charset", b->parameter);
1251 
1252   if (p)
1253     mutt_canonical_charset (d, dlen, NONULL(p));
1254   else
1255     strfcpy (d, "us-ascii", dlen);
1256 
1257   return d;
1258 }
1259 
1260 
1261 /* Assumes called from send mode where BODY->filename points to actual file */
mutt_update_encoding(BODY * a)1262 void mutt_update_encoding (BODY *a)
1263 {
1264   CONTENT *info;
1265   char chsbuff[STRING];
1266 
1267   /* override noconv when it's us-ascii */
1268   if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1269     a->noconv = 0;
1270 
1271   if (!a->force_charset && !a->noconv)
1272     mutt_delete_parameter ("charset", &a->parameter);
1273 
1274   if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1275     return;
1276 
1277   mutt_set_encoding (a, info);
1278   mutt_stamp_attachment(a);
1279 
1280   FREE (&a->content);
1281   a->content = info;
1282 
1283 }
1284 
mutt_make_message_attach(CONTEXT * ctx,HEADER * hdr,int attach_msg)1285 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1286 {
1287   char buffer[LONG_STRING];
1288   BODY *body;
1289   FILE *fp;
1290   int cmflags, chflags;
1291   int pgp = WithCrypto? hdr->security : 0;
1292 
1293   if (WithCrypto)
1294   {
1295     if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1296         (hdr->security & ENCRYPT)) {
1297       if (!crypt_valid_passphrase(hdr->security))
1298         return (NULL);
1299     }
1300   }
1301 
1302   mutt_mktemp (buffer, sizeof (buffer));
1303   if ((fp = safe_fopen (buffer, "w+")) == NULL)
1304     return NULL;
1305 
1306   body = mutt_new_body ();
1307   body->type = TYPEMESSAGE;
1308   body->subtype = safe_strdup ("rfc822");
1309   body->filename = safe_strdup (buffer);
1310   body->unlink = 1;
1311   body->use_disp = 0;
1312   body->disposition = DISPINLINE;
1313   body->noconv = 1;
1314 
1315   mutt_parse_mime_message (ctx, hdr);
1316 
1317   chflags = CH_XMIT;
1318   cmflags = 0;
1319 
1320   /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1321   if (!attach_msg && option (OPTMIMEFORWDECODE))
1322   {
1323     chflags |= CH_MIME | CH_TXTPLAIN;
1324     cmflags = M_CM_DECODE | M_CM_CHARCONV;
1325     if ((WithCrypto & APPLICATION_PGP))
1326       pgp &= ~PGPENCRYPT;
1327     if ((WithCrypto & APPLICATION_SMIME))
1328       pgp &= ~SMIMEENCRYPT;
1329   }
1330   else if (WithCrypto
1331            && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1332   {
1333     if ((WithCrypto & APPLICATION_PGP)
1334         && mutt_is_multipart_encrypted (hdr->content))
1335     {
1336       chflags |= CH_MIME | CH_NONEWLINE;
1337       cmflags = M_CM_DECODE_PGP;
1338       pgp &= ~PGPENCRYPT;
1339     }
1340     else if ((WithCrypto & APPLICATION_PGP)
1341              && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1342     {
1343       chflags |= CH_MIME | CH_TXTPLAIN;
1344       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1345       pgp &= ~PGPENCRYPT;
1346     }
1347     else if ((WithCrypto & APPLICATION_SMIME)
1348               && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1349     {
1350       chflags |= CH_MIME | CH_TXTPLAIN;
1351       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1352       pgp &= ~SMIMEENCRYPT;
1353     }
1354   }
1355 
1356   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1357 
1358   fflush(fp);
1359   rewind(fp);
1360 
1361   body->hdr = mutt_new_header();
1362   body->hdr->offset = 0;
1363   /* we don't need the user headers here */
1364   body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1365   if (WithCrypto)
1366     body->hdr->security = pgp;
1367   mutt_update_encoding (body);
1368   body->parts = body->hdr->content;
1369 
1370   safe_fclose (&fp);
1371 
1372   return (body);
1373 }
1374 
mutt_make_file_attach(const char * path)1375 BODY *mutt_make_file_attach (const char *path)
1376 {
1377   BODY *att;
1378   CONTENT *info;
1379 
1380   att = mutt_new_body ();
1381   att->filename = safe_strdup (path);
1382 
1383   /* Attempt to determine the appropriate content-type based on the filename
1384    * suffix.
1385    */
1386 
1387 #if 0
1388 
1389   if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1390       || *xbuf != '\0')
1391   {
1392     att->type = n;
1393     att->subtype = safe_strdup (buf);
1394     att->xtype = safe_strdup (xbuf);
1395   }
1396 
1397 #else
1398 
1399   mutt_lookup_mime_type (att, path);
1400 
1401 #endif
1402 
1403   if ((info = mutt_get_content_info (path, att)) == NULL)
1404   {
1405     mutt_free_body (&att);
1406     return NULL;
1407   }
1408 
1409   if (!att->subtype)
1410   {
1411     if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1412     {
1413       /*
1414        * Statistically speaking, there should be more than 10% "lobin"
1415        * chars if this is really a binary file...
1416        */
1417       att->type = TYPETEXT;
1418       att->subtype = safe_strdup ("plain");
1419     }
1420     else
1421     {
1422       att->type = TYPEAPPLICATION;
1423       att->subtype = safe_strdup ("octet-stream");
1424     }
1425   }
1426 
1427   FREE(&info);
1428   mutt_update_encoding (att);
1429   return (att);
1430 }
1431 
get_toplevel_encoding(BODY * a)1432 static int get_toplevel_encoding (BODY *a)
1433 {
1434   int e = ENC7BIT;
1435 
1436   for (; a; a = a->next)
1437   {
1438     if (a->encoding == ENCBINARY)
1439       return (ENCBINARY);
1440     else if (a->encoding == ENC8BIT)
1441       e = ENC8BIT;
1442   }
1443 
1444   return (e);
1445 }
1446 
1447 /* check for duplicate boundary. return 1 if duplicate */
mutt_check_boundary(const char * boundary,BODY * b)1448 static int mutt_check_boundary (const char* boundary, BODY *b)
1449 {
1450   char* p;
1451 
1452   if (b->parts && mutt_check_boundary (boundary, b->parts))
1453     return 1;
1454 
1455   if (b->next && mutt_check_boundary (boundary, b->next))
1456     return 1;
1457 
1458   if ((p = mutt_get_parameter ("boundary", b->parameter))
1459       && !ascii_strcmp (p, boundary))
1460     return 1;
1461   return 0;
1462 }
1463 
mutt_make_multipart(BODY * b)1464 BODY *mutt_make_multipart (BODY *b)
1465 {
1466   BODY *new;
1467 
1468   new = mutt_new_body ();
1469   new->type = TYPEMULTIPART;
1470   new->subtype = safe_strdup ("mixed");
1471   new->encoding = get_toplevel_encoding (b);
1472   do
1473   {
1474     mutt_generate_boundary (&new->parameter);
1475     if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1476 			     b))
1477       mutt_delete_parameter ("boundary", &new->parameter);
1478   }
1479   while (!mutt_get_parameter ("boundary", new->parameter));
1480   new->use_disp = 0;
1481   new->disposition = DISPINLINE;
1482   new->parts = b;
1483 
1484   return new;
1485 }
1486 
1487 /* remove the multipart body if it exists */
mutt_remove_multipart(BODY * b)1488 BODY *mutt_remove_multipart (BODY *b)
1489 {
1490   BODY *t;
1491 
1492   if (b->parts)
1493   {
1494     t = b;
1495     b = b->parts;
1496     t->parts = NULL;
1497     mutt_free_body (&t);
1498   }
1499   return b;
1500 }
1501 
mutt_make_date(char * s,size_t len)1502 char *mutt_make_date (char *s, size_t len)
1503 {
1504   time_t t = time (NULL);
1505   struct tm *l = localtime (&t);
1506   time_t tz = mutt_local_tz (t);
1507 
1508   tz /= 60;
1509 
1510   snprintf (s, len,  "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1511 	    Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1512 	    l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1513 	    (int) tz / 60, (int) abs (tz) % 60);
1514   return (s);
1515 }
1516 
1517 /* wrapper around mutt_write_address() so we can handle very large
1518    recipient lists without needing a huge temporary buffer in memory */
mutt_write_address_list(ADDRESS * adr,FILE * fp,int linelen,int display)1519 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1520 {
1521   ADDRESS *tmp;
1522   char buf[LONG_STRING];
1523   int count = 0;
1524   int len;
1525 
1526   while (adr)
1527   {
1528     tmp = adr->next;
1529     adr->next = NULL;
1530     buf[0] = 0;
1531     rfc822_write_address (buf, sizeof (buf), adr, display);
1532     len = mutt_strlen (buf);
1533     if (count && linelen + len > 74)
1534     {
1535       fputs ("\n\t", fp);
1536       linelen = len + 8; /* tab is usually about 8 spaces... */
1537     }
1538     else
1539     {
1540       if (count && adr->mailbox)
1541       {
1542 	fputc (' ', fp);
1543 	linelen++;
1544       }
1545       linelen += len;
1546     }
1547     fputs (buf, fp);
1548     adr->next = tmp;
1549     if (!adr->group && adr->next && adr->next->mailbox)
1550     {
1551       linelen++;
1552       fputc (',', fp);
1553     }
1554     adr = adr->next;
1555     count++;
1556   }
1557   fputc ('\n', fp);
1558 }
1559 
1560 /* arbitrary number of elements to grow the array by */
1561 #define REF_INC 16
1562 
1563 /* need to write the list in reverse because they are stored in reverse order
1564  * when parsed to speed up threading
1565  */
mutt_write_references(LIST * r,FILE * f,int trim)1566 void mutt_write_references (LIST *r, FILE *f, int trim)
1567 {
1568   LIST **ref = NULL;
1569   int refcnt = 0, refmax = 0;
1570 
1571   for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
1572   {
1573     if (refcnt == refmax)
1574       safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1575     ref[refcnt++] = r;
1576   }
1577 
1578   while (refcnt-- > 0)
1579   {
1580     fputc (' ', f);
1581     fputs (ref[refcnt]->data, f);
1582     if (refcnt >= 1)
1583       fputc ('\n', f);
1584   }
1585 
1586   FREE (&ref);
1587 }
1588 
find_word(const char * src)1589 static const char *find_word (const char *src)
1590 {
1591   const char *p = src;
1592 
1593   while (p && *p && strchr (" \t\n", *p))
1594     p++;
1595   while (p && *p && !strchr (" \t\n", *p))
1596     p++;
1597   return p;
1598 }
1599 
1600 /* like wcwidth(), but gets const char* not wchar_t* */
my_width(const char * str,int col,int flags)1601 static int my_width (const char *str, int col, int flags)
1602 {
1603   wchar_t wc;
1604   int l, w = 0, nl = 0;
1605   const char *p = str;
1606 
1607   while (p && *p)
1608   {
1609     if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
1610     {
1611       l = wcwidth (wc);
1612       if (l < 0)
1613 	l = 1;
1614       /* correctly calc tab stop, even for sending as the
1615        * line should look pretty on the receiving end */
1616       if (wc == L'\t' || (nl && wc == L' '))
1617       {
1618 	nl = 0;
1619 	l = 8 - (col % 8);
1620       }
1621       /* track newlines for display-case: if we have a space
1622        * after a newline, assume 8 spaces as for display we
1623        * always tab-fold */
1624       else if ((flags & CH_DISPLAY) && wc == '\n')
1625 	nl = 1;
1626     }
1627     else
1628       l = 1;
1629     w += l;
1630     p++;
1631   }
1632   return w;
1633 }
1634 
print_val(FILE * fp,const char * pfx,const char * value,int flags,size_t col)1635 static int print_val (FILE *fp, const char *pfx, const char *value,
1636 		      int flags, size_t col)
1637 {
1638   while (value && *value)
1639   {
1640     if (fputc (*value, fp) == EOF)
1641       return -1;
1642     /* corner-case: break words longer than 998 chars by force,
1643      * mandated by RfC5322 */
1644     if (!(flags & CH_DISPLAY) && ++col >= 998)
1645     {
1646       if (fputs ("\n ", fp) < 0)
1647 	return -1;
1648       col = 1;
1649     }
1650     if (*value == '\n')
1651     {
1652       if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
1653 	return -1;
1654       /* for display, turn folding spaces into folding tabs */
1655       if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
1656       {
1657 	value++;
1658 	while (*value && (*value == ' ' || *value == '\t'))
1659 	  value++;
1660 	if (fputc ('\t', fp) == EOF)
1661 	  return -1;
1662 	continue;
1663       }
1664     }
1665     value++;
1666   }
1667   return 0;
1668 }
1669 
fold_one_header(FILE * fp,const char * tag,const char * value,const char * pfx,int wraplen,int flags)1670 static int fold_one_header (FILE *fp, const char *tag, const char *value,
1671 			      const char *pfx, int wraplen, int flags)
1672 {
1673   const char *p = value, *next, *sp;
1674   char buf[HUGE_STRING] = "";
1675   int first = 1, enc, col = 0, w, l = 0, fold;
1676 
1677   dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
1678 	    pfx, tag, flags, value));
1679 
1680   if (tag && *tag && fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1681     return -1;
1682   col = mutt_strlen (tag) + (tag && *tag ? 2 : 0) + mutt_strlen (pfx);
1683 
1684   while (p && *p)
1685   {
1686     fold = 0;
1687 
1688     /* find the next word and place it in `buf'. it may start with
1689      * whitespace we can fold before */
1690     next = find_word (p);
1691     l = MIN(sizeof (buf), next - p);
1692     memcpy (buf, p, l);
1693     buf[l] = 0;
1694 
1695     /* determine width: character cells for display, bytes for sending
1696      * (we get pure ascii only) */
1697     w = my_width (buf, col, flags);
1698     enc = mutt_strncmp (buf, "=?", 2) == 0;
1699 
1700     dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
1701 	      buf, col, w, *next));
1702 
1703     /* insert a folding \n before the current word's lwsp except for
1704      * header name, first word on a line (word longer than wrap width)
1705      * and encoded words */
1706     if (!first && !enc && col && col + w >= wraplen)
1707     {
1708       col = mutt_strlen (pfx);
1709       fold = 1;
1710       if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
1711 	return -1;
1712     }
1713 
1714     /* print the actual word; for display, ignore leading ws for word
1715      * and fold with tab for readability */
1716     if ((flags & CH_DISPLAY) && fold)
1717     {
1718       char *p = buf;
1719       while (*p && (*p == ' ' || *p == '\t'))
1720       {
1721 	p++;
1722 	col--;
1723       }
1724       if (fputc ('\t', fp) == EOF)
1725 	return -1;
1726       if (print_val (fp, pfx, p, flags, col) < 0)
1727 	return -1;
1728       col += 8;
1729     }
1730     else if (print_val (fp, pfx, buf, flags, col) < 0)
1731       return -1;
1732     col += w;
1733 
1734     /* if the current word ends in \n, ignore all its trailing spaces
1735      * and reset column; this prevents us from putting only spaces (or
1736      * even none) on a line if the trailing spaces are located at our
1737      * current line width
1738      * XXX this covers ASCII space only, for display we probably
1739      * XXX want something like iswspace() here */
1740     sp = next;
1741     while (*sp && (*sp == ' ' || *sp == '\t'))
1742       sp++;
1743     if (*sp == '\n')
1744     {
1745       next = sp;
1746       col = 0;
1747     }
1748 
1749     p = next;
1750     first = 0;
1751   }
1752 
1753   /* if we have printed something but didn't \n-terminate it, do it
1754    * except the last word we printed ended in \n already */
1755   if (col && buf[l - 1] != '\n')
1756     if (putc ('\n', fp) == EOF)
1757       return -1;
1758 
1759   return 0;
1760 }
1761 
unfold_header(char * s)1762 static char *unfold_header (char *s)
1763 {
1764   char *p = s, *q = s;
1765 
1766   while (p && *p)
1767   {
1768     /* remove CRLF prior to FWSP, turn \t into ' ' */
1769     if (*p == '\r' && *(p + 1) && *(p + 1) == '\n' && *(p + 2) &&
1770 	(*(p + 2) == ' ' || *(p + 2) == '\t'))
1771     {
1772       *q++ = ' ';
1773       p += 3;
1774       continue;
1775     }
1776     /* remove LF prior to FWSP, turn \t into ' ' */
1777     else if (*p == '\n' && *(p + 1) && (*(p + 1) == ' ' || *(p + 1) == '\t'))
1778     {
1779       *q++ = ' ';
1780       p += 2;
1781       continue;
1782     }
1783     *q++ = *p++;
1784   }
1785   if (q)
1786     *q = 0;
1787 
1788   return s;
1789 }
1790 
write_one_header(FILE * fp,int pfxw,int max,int wraplen,const char * pfx,const char * start,const char * end,int flags)1791 static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
1792 			     const char *pfx, const char *start, const char *end,
1793 			     int flags)
1794 {
1795   char *tagbuf, *valbuf, *t;
1796   int is_from = ((end - start) > 5 &&
1797 		 ascii_strncasecmp (start, "from ", 5) == 0);
1798 
1799   /* only pass through folding machinery if necessary for sending,
1800      never wrap From_ headers on sending */
1801   if (!(flags & CH_DISPLAY) && (pfxw + max <= wraplen || is_from))
1802   {
1803     valbuf = mutt_substrdup (start, end);
1804     dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
1805 	      "max width = %d <= %d\n",
1806 	      NONULL(pfx), valbuf, max, wraplen));
1807     if (pfx && *pfx)
1808       if (fputs (pfx, fp) == EOF)
1809 	return -1;
1810     if (!(t = strchr (valbuf, ':')))
1811     {
1812       dprint (1, (debugfile, "mwoh: warning: header not in "
1813 		  "'key: value' format!\n"));
1814       return 0;
1815     }
1816     if (print_val (fp, pfx, valbuf, flags, mutt_strlen (pfx)) < 0)
1817     {
1818       FREE(&valbuf);
1819       return -1;
1820     }
1821     FREE(&valbuf);
1822   }
1823   else
1824   {
1825     t = strchr (start, ':');
1826     if (t > end)
1827     {
1828       dprint (1, (debugfile, "mwoh: warning: header not in "
1829 		  "'key: value' format!\n"));
1830       return 0;
1831     }
1832     if (is_from)
1833     {
1834       tagbuf = NULL;
1835       valbuf = mutt_substrdup (start, end);
1836     }
1837     else
1838     {
1839       tagbuf = mutt_substrdup (start, t);
1840       ++t; /* skip over the colon separating the header field name and value */
1841       SKIPWS(t); /* skip over any leading whitespace */
1842       valbuf = mutt_substrdup (t, end);
1843     }
1844     dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
1845 	      "max width = %d > %d\n",
1846 	      NONULL(pfx), valbuf, max, wraplen));
1847     if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
1848       return -1;
1849     FREE (&tagbuf);
1850     FREE (&valbuf);
1851   }
1852   return 0;
1853 }
1854 
1855 /* split several headers into individual ones and call write_one_header
1856  * for each one */
mutt_write_one_header(FILE * fp,const char * tag,const char * value,const char * pfx,int wraplen,int flags)1857 int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
1858 			   const char *pfx, int wraplen, int flags)
1859 {
1860   char *p = (char *)value, *last, *line;
1861   int max = 0, w, rc = -1;
1862   int pfxw = mutt_strwidth (pfx);
1863   char *v = safe_strdup (value);
1864 
1865   if (!(flags & CH_DISPLAY) || option (OPTWEED))
1866     v = unfold_header (v);
1867 
1868   /* when not displaying, use sane wrap value */
1869   if (!(flags & CH_DISPLAY))
1870   {
1871     if (WrapHeaders < 78 || WrapHeaders > 998)
1872       wraplen = 78;
1873     else
1874       wraplen = WrapHeaders;
1875   }
1876   else if (wraplen <= 0 || wraplen > COLS)
1877     wraplen = COLS;
1878 
1879   if (tag)
1880   {
1881     /* if header is short enough, simply print it */
1882     if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
1883 	mutt_strwidth (v) <= wraplen)
1884     {
1885       dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
1886 		NONULL(pfx), tag, v));
1887       if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
1888 	goto out;
1889       rc = 0;
1890       goto out;
1891     }
1892     else
1893     {
1894       rc = fold_one_header (fp, tag, v, pfx, wraplen, flags);
1895       goto out;
1896     }
1897   }
1898 
1899   p = last = line = (char *)v;
1900   while (p && *p)
1901   {
1902     p = strchr (p, '\n');
1903 
1904     /* find maximum line width in current header */
1905     if (p)
1906       *p = 0;
1907     if ((w = my_width (line, 0, flags)) > max)
1908       max = w;
1909     if (p)
1910       *p = '\n';
1911 
1912     if (!p)
1913       break;
1914 
1915     line = ++p;
1916     if (*p != ' ' && *p != '\t')
1917     {
1918       if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1919 	goto out;
1920       last = p;
1921       max = 0;
1922     }
1923   }
1924 
1925   if (last && *last)
1926     if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1927       goto out;
1928 
1929   rc = 0;
1930 
1931 out:
1932   FREE (&v);
1933   return rc;
1934 }
1935 
1936 
1937 /* Note: all RFC2047 encoding should be done outside of this routine, except
1938  * for the "real name."  This will allow this routine to be used more than
1939  * once, if necessary.
1940  *
1941  * Likewise, all IDN processing should happen outside of this routine.
1942  *
1943  * mode == 1  => "lite" mode (used for edit_hdrs)
1944  * mode == 0  => normal mode.  write full header + MIME headers
1945  * mode == -1 => write just the envelope info (used for postponing messages)
1946  *
1947  * privacy != 0 => will omit any headers which may identify the user.
1948  *               Output generated is suitable for being sent through
1949  * 		 anonymous remailer chains.
1950  *
1951  */
1952 
1953 
1954 
mutt_write_rfc822_header(FILE * fp,ENVELOPE * env,BODY * attach,int mode,int privacy)1955 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1956 			      int mode, int privacy)
1957 {
1958   char buffer[LONG_STRING];
1959   char *p, *q;
1960   LIST *tmp = env->userhdrs;
1961   int has_agent = 0; /* user defined user-agent header field exists */
1962 
1963   if (mode == 0 && !privacy)
1964     fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1965 
1966   /* OPTUSEFROM is not consulted here so that we can still write a From:
1967    * field if the user sets it with the `my_hdr' command
1968    */
1969   if (env->from && !privacy)
1970   {
1971     buffer[0] = 0;
1972     rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1973     fprintf (fp, "From: %s\n", buffer);
1974   }
1975 
1976   if (env->sender && !privacy)
1977   {
1978     buffer[0] = 0;
1979     rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
1980     fprintf (fp, "Sender: %s\n", buffer);
1981   }
1982 
1983   if (env->to)
1984   {
1985     fputs ("To: ", fp);
1986     mutt_write_address_list (env->to, fp, 4, 0);
1987   }
1988   else if (mode > 0)
1989     fputs ("To: \n", fp);
1990 
1991   if (env->cc)
1992   {
1993     fputs ("Cc: ", fp);
1994     mutt_write_address_list (env->cc, fp, 4, 0);
1995   }
1996   else if (mode > 0)
1997     fputs ("Cc: \n", fp);
1998 
1999   if (env->bcc)
2000   {
2001     if(mode != 0 || option(OPTWRITEBCC))
2002     {
2003       fputs ("Bcc: ", fp);
2004       mutt_write_address_list (env->bcc, fp, 5, 0);
2005     }
2006   }
2007   else if (mode > 0)
2008     fputs ("Bcc: \n", fp);
2009 
2010   if (env->subject)
2011     mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
2012   else if (mode == 1)
2013     fputs ("Subject: \n", fp);
2014 
2015   /* save message id if the user has set it */
2016   if (env->message_id && !privacy)
2017     fprintf (fp, "Message-ID: %s\n", env->message_id);
2018 
2019   if (env->reply_to)
2020   {
2021     fputs ("Reply-To: ", fp);
2022     mutt_write_address_list (env->reply_to, fp, 10, 0);
2023   }
2024   else if (mode > 0)
2025     fputs ("Reply-To: \n", fp);
2026 
2027   if (env->mail_followup_to)
2028   {
2029     fputs ("Mail-Followup-To: ", fp);
2030     mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
2031   }
2032 
2033   if (mode <= 0)
2034   {
2035     if (env->references)
2036     {
2037       fputs ("References:", fp);
2038       mutt_write_references (env->references, fp, 10);
2039       fputc('\n', fp);
2040     }
2041 
2042     /* Add the MIME headers */
2043     fputs ("MIME-Version: 1.0\n", fp);
2044     mutt_write_mime_header (attach, fp);
2045   }
2046 
2047   if (env->in_reply_to)
2048   {
2049     fputs ("In-Reply-To:", fp);
2050     mutt_write_references (env->in_reply_to, fp, 0);
2051     fputc ('\n', fp);
2052   }
2053 
2054   /* Add any user defined headers */
2055   for (; tmp; tmp = tmp->next)
2056   {
2057     if ((p = strchr (tmp->data, ':')))
2058     {
2059       q = p;
2060 
2061       *p = '\0';
2062 
2063       p++; SKIPWS (p);
2064       if (!*p)
2065       {
2066 	*q = ':';
2067 	continue;  /* don't emit empty fields. */
2068       }
2069 
2070       /* check to see if the user has overridden the user-agent field */
2071       if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
2072       {
2073 	has_agent = 1;
2074 	if (privacy)
2075 	{
2076 	  *q = ':';
2077 	  continue;
2078 	}
2079       }
2080 
2081       mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
2082       *q = ':';
2083     }
2084   }
2085 
2086   if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
2087   {
2088     /* Add a vanity header */
2089     fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
2090   }
2091 
2092   return (ferror (fp) == 0 ? 0 : -1);
2093 }
2094 
encode_headers(LIST * h)2095 static void encode_headers (LIST *h)
2096 {
2097   char *tmp;
2098   char *p;
2099   int i;
2100 
2101   for (; h; h = h->next)
2102   {
2103     if (!(p = strchr (h->data, ':')))
2104       continue;
2105 
2106     i = p - h->data;
2107     ++p; SKIPWS (p);
2108     tmp = safe_strdup (p);
2109 
2110     if (!tmp)
2111       continue;
2112 
2113     rfc2047_encode_string (&tmp);
2114     safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
2115 
2116     sprintf (h->data + i, ": %s", NONULL (tmp));  /* __SPRINTF_CHECKED__ */
2117 
2118     FREE (&tmp);
2119   }
2120 }
2121 
mutt_fqdn(short may_hide_host)2122 const char *mutt_fqdn(short may_hide_host)
2123 {
2124   char *p = NULL;
2125 
2126   if(Fqdn && Fqdn[0] != '@')
2127   {
2128     p = Fqdn;
2129 
2130     if(may_hide_host && option(OPTHIDDENHOST))
2131     {
2132       if((p = strchr(Fqdn, '.')))
2133 	p++;
2134 
2135       /* sanity check: don't hide the host if
2136        * the fqdn is something like detebe.org.
2137        */
2138 
2139       if(!p || !strchr(p, '.'))
2140 	p = Fqdn;
2141     }
2142   }
2143 
2144   return p;
2145 }
2146 
mutt_gen_msgid(void)2147 char *mutt_gen_msgid (void)
2148 {
2149   char buf[SHORT_STRING];
2150   time_t now;
2151   struct tm *tm;
2152   const char *fqdn;
2153 
2154   now = time (NULL);
2155   tm = gmtime (&now);
2156   if(!(fqdn = mutt_fqdn(0)))
2157     fqdn = NONULL(Hostname);
2158 
2159   snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
2160 	    tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
2161 	    tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
2162   MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
2163   return (safe_strdup (buf));
2164 }
2165 
alarm_handler(int sig)2166 static RETSIGTYPE alarm_handler (int sig)
2167 {
2168   SigAlrm = 1;
2169 }
2170 
2171 /* invoke sendmail in a subshell
2172    path	(in)		path to program to execute
2173    args	(in)		arguments to pass to program
2174    msg (in)		temp file containing message to send
2175    tempfile (out)	if sendmail is put in the background, this points
2176    			to the temporary file containing the stdout of the
2177 			child process. If it is NULL, stderr and stdout
2178                         are not redirected. */
2179 static int
send_msg(const char * path,char ** args,const char * msg,char ** tempfile)2180 send_msg (const char *path, char **args, const char *msg, char **tempfile)
2181 {
2182   sigset_t set;
2183   int fd, st;
2184   pid_t pid, ppid;
2185 
2186   mutt_block_signals_system ();
2187 
2188   sigemptyset (&set);
2189   /* we also don't want to be stopped right now */
2190   sigaddset (&set, SIGTSTP);
2191   sigprocmask (SIG_BLOCK, &set, NULL);
2192 
2193   if (SendmailWait >= 0 && tempfile)
2194   {
2195     char tmp[_POSIX_PATH_MAX];
2196 
2197     mutt_mktemp (tmp, sizeof (tmp));
2198     *tempfile = safe_strdup (tmp);
2199   }
2200 
2201   if ((pid = fork ()) == 0)
2202   {
2203     struct sigaction act, oldalrm;
2204 
2205     /* save parent's ID before setsid() */
2206     ppid = getppid ();
2207 
2208     /* we want the delivery to continue even after the main process dies,
2209      * so we put ourselves into another session right away
2210      */
2211     setsid ();
2212 
2213     /* next we close all open files */
2214     close (0);
2215 #if defined(OPEN_MAX)
2216     for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2217       close (fd);
2218 #elif defined(_POSIX_OPEN_MAX)
2219     for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2220       close (fd);
2221 #else
2222     if (tempfile)
2223     {
2224       close (1);
2225       close (2);
2226     }
2227 #endif
2228 
2229     /* now the second fork() */
2230     if ((pid = fork ()) == 0)
2231     {
2232       /* "msg" will be opened as stdin */
2233       if (open (msg, O_RDONLY, 0) < 0)
2234       {
2235 	unlink (msg);
2236 	_exit (S_ERR);
2237       }
2238       unlink (msg);
2239 
2240       if (SendmailWait >= 0 && tempfile && *tempfile)
2241       {
2242 	/* *tempfile will be opened as stdout */
2243 	if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2244 	  _exit (S_ERR);
2245 	/* redirect stderr to *tempfile too */
2246 	if (dup (1) < 0)
2247 	  _exit (S_ERR);
2248       }
2249       else if (tempfile)
2250       {
2251 	if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)	/* stdout */
2252 	  _exit (S_ERR);
2253 	if (open ("/dev/null", O_RDWR | O_APPEND) < 0)		/* stderr */
2254 	  _exit (S_ERR);
2255       }
2256 
2257       execvp (path, args);
2258       _exit (S_ERR);
2259     }
2260     else if (pid == -1)
2261     {
2262       unlink (msg);
2263       if (tempfile)
2264 	FREE (tempfile);		/* __FREE_CHECKED__ */
2265       _exit (S_ERR);
2266     }
2267 
2268     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2269      * SendmailWait = 0: wait forever
2270      * SendmailWait < 0: don't wait
2271      */
2272     if (SendmailWait > 0)
2273     {
2274       SigAlrm = 0;
2275       act.sa_handler = alarm_handler;
2276 #ifdef SA_INTERRUPT
2277       /* need to make sure waitpid() is interrupted on SIGALRM */
2278       act.sa_flags = SA_INTERRUPT;
2279 #else
2280       act.sa_flags = 0;
2281 #endif
2282       sigemptyset (&act.sa_mask);
2283       sigaction (SIGALRM, &act, &oldalrm);
2284       alarm (SendmailWait);
2285     }
2286     else if (SendmailWait < 0)
2287       _exit (0xff & EX_OK);
2288 
2289     if (waitpid (pid, &st, 0) > 0)
2290     {
2291       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2292       if (SendmailWait && st == (0xff & EX_OK) && tempfile && *tempfile)
2293       {
2294 	unlink (*tempfile); /* no longer needed */
2295 	FREE (tempfile);		/* __FREE_CHECKED__ */
2296       }
2297     }
2298     else
2299     {
2300       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2301 	      S_BKG : S_ERR;
2302       if (SendmailWait > 0 && tempfile && *tempfile)
2303       {
2304 	unlink (*tempfile);
2305 	FREE (tempfile);		/* __FREE_CHECKED__ */
2306       }
2307     }
2308 
2309     /* reset alarm; not really needed, but... */
2310     alarm (0);
2311     sigaction (SIGALRM, &oldalrm, NULL);
2312 
2313     if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile && *tempfile)
2314     {
2315       /* the parent is already dead */
2316       unlink (*tempfile);
2317       FREE (tempfile);		/* __FREE_CHECKED__ */
2318     }
2319 
2320     _exit (st);
2321   }
2322 
2323   sigprocmask (SIG_UNBLOCK, &set, NULL);
2324 
2325   if (pid != -1 && waitpid (pid, &st, 0) > 0)
2326     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2327   else
2328     st = S_ERR;	/* error */
2329 
2330   mutt_unblock_signals_system (1);
2331 
2332   return (st);
2333 }
2334 
2335 static char **
add_args(char ** args,size_t * argslen,size_t * argsmax,ADDRESS * addr)2336 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2337 {
2338   for (; addr; addr = addr->next)
2339   {
2340     /* weed out group mailboxes, since those are for display only */
2341     if (addr->mailbox && !addr->group)
2342     {
2343       if (*argslen == *argsmax)
2344 	safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2345       args[(*argslen)++] = addr->mailbox;
2346     }
2347   }
2348   return (args);
2349 }
2350 
2351 static char **
add_option(char ** args,size_t * argslen,size_t * argsmax,char * s)2352 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2353 {
2354   if (*argslen == *argsmax)
2355     safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2356   args[(*argslen)++] = s;
2357   return (args);
2358 }
2359 
2360 int
mutt_invoke_sendmail(ADDRESS * from,ADDRESS * to,ADDRESS * cc,ADDRESS * bcc,const char * msg,int eightbit)2361 mutt_invoke_sendmail (ADDRESS *from,	/* the sender */
2362 		 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2363 		 const char *msg, /* file containing message */
2364 		 int eightbit) /* message contains 8bit chars */
2365 {
2366   char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2367   char **args = NULL;
2368   size_t argslen = 0, argsmax = 0;
2369   int i;
2370 
2371   ps = s;
2372   i = 0;
2373   while ((ps = strtok (ps, " ")))
2374   {
2375     if (argslen == argsmax)
2376       safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2377 
2378     if (i)
2379       args[argslen++] = ps;
2380     else
2381     {
2382       path = safe_strdup (ps);
2383       ps = strrchr (ps, '/');
2384       if (ps)
2385 	ps++;
2386       else
2387 	ps = path;
2388       args[argslen++] = ps;
2389     }
2390     ps = NULL;
2391     i++;
2392   }
2393 
2394   if (eightbit && option (OPTUSE8BITMIME))
2395     args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2396 
2397   if (option (OPTENVFROM))
2398   {
2399     if (EnvFrom)
2400     {
2401       args = add_option (args, &argslen, &argsmax, "-f");
2402       args = add_args   (args, &argslen, &argsmax, EnvFrom);
2403     }
2404     else if (from && !from->next)
2405     {
2406       args = add_option (args, &argslen, &argsmax, "-f");
2407       args = add_args   (args, &argslen, &argsmax, from);
2408     }
2409   }
2410 
2411   if (DsnNotify)
2412   {
2413     args = add_option (args, &argslen, &argsmax, "-N");
2414     args = add_option (args, &argslen, &argsmax, DsnNotify);
2415   }
2416   if (DsnReturn)
2417   {
2418     args = add_option (args, &argslen, &argsmax, "-R");
2419     args = add_option (args, &argslen, &argsmax, DsnReturn);
2420   }
2421   args = add_option (args, &argslen, &argsmax, "--");
2422   args = add_args (args, &argslen, &argsmax, to);
2423   args = add_args (args, &argslen, &argsmax, cc);
2424   args = add_args (args, &argslen, &argsmax, bcc);
2425 
2426   if (argslen == argsmax)
2427     safe_realloc (&args, sizeof (char *) * (++argsmax));
2428 
2429   args[argslen++] = NULL;
2430 
2431   if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2432   {
2433     if (i != S_BKG)
2434     {
2435       const char *e = mutt_strsysexit (i);
2436 
2437       e = mutt_strsysexit (i);
2438       mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2439       if (childout)
2440       {
2441 	struct stat st;
2442 
2443 	if (stat (childout, &st) == 0 && st.st_size > 0)
2444 	  mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2445       }
2446     }
2447   }
2448   else if (childout)
2449     unlink (childout);
2450 
2451   FREE (&childout);
2452   FREE (&path);
2453   FREE (&s);
2454   FREE (&args);
2455 
2456   if (i == (EX_OK & 0xff))
2457     i = 0;
2458   else if (i == S_BKG)
2459     i = 1;
2460   else
2461     i = -1;
2462   return (i);
2463 }
2464 
2465 /* For postponing (!final) do the necessary encodings only */
mutt_prepare_envelope(ENVELOPE * env,int final)2466 void mutt_prepare_envelope (ENVELOPE *env, int final)
2467 {
2468   char buffer[LONG_STRING];
2469 
2470   if (final)
2471   {
2472     if (env->bcc && !(env->to || env->cc))
2473     {
2474       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2475        * recipients if there is no To: or Cc: field, so attempt to suppress
2476        * it by using an empty To: field.
2477        */
2478       env->to = rfc822_new_address ();
2479       env->to->group = 1;
2480       env->to->next = rfc822_new_address ();
2481 
2482       buffer[0] = 0;
2483       rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2484 		  RFC822Specials);
2485 
2486       env->to->mailbox = safe_strdup (buffer);
2487     }
2488 
2489     mutt_set_followup_to (env);
2490 
2491     if (!env->message_id)
2492       env->message_id = mutt_gen_msgid ();
2493   }
2494 
2495   /* Take care of 8-bit => 7-bit conversion. */
2496   rfc2047_encode_adrlist (env->to, "To");
2497   rfc2047_encode_adrlist (env->cc, "Cc");
2498   rfc2047_encode_adrlist (env->bcc, "Bcc");
2499   rfc2047_encode_adrlist (env->from, "From");
2500   rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2501   rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2502   rfc2047_encode_string (&env->x_label);
2503 
2504   if (env->subject)
2505   {
2506     rfc2047_encode_string (&env->subject);
2507   }
2508   encode_headers (env->userhdrs);
2509 }
2510 
mutt_unprepare_envelope(ENVELOPE * env)2511 void mutt_unprepare_envelope (ENVELOPE *env)
2512 {
2513   LIST *item;
2514 
2515   for (item = env->userhdrs; item; item = item->next)
2516     rfc2047_decode (&item->data);
2517 
2518   rfc822_free_address (&env->mail_followup_to);
2519 
2520   /* back conversions */
2521   rfc2047_decode_adrlist (env->to);
2522   rfc2047_decode_adrlist (env->cc);
2523   rfc2047_decode_adrlist (env->bcc);
2524   rfc2047_decode_adrlist (env->from);
2525   rfc2047_decode_adrlist (env->reply_to);
2526   rfc2047_decode (&env->subject);
2527   rfc2047_decode (&env->x_label);
2528 }
2529 
_mutt_bounce_message(FILE * fp,HEADER * h,ADDRESS * to,const char * resent_from,ADDRESS * env_from)2530 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2531 				  ADDRESS *env_from)
2532 {
2533   int i, ret = 0;
2534   FILE *f;
2535   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2536   MESSAGE *msg = NULL;
2537 
2538   if (!h)
2539   {
2540 	  /* Try to bounce each message out, aborting if we get any failures. */
2541     for (i=0; i<Context->msgcount; i++)
2542       if (Context->hdrs[i]->tagged)
2543         ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2544     return ret;
2545   }
2546 
2547   /* If we failed to open a message, return with error */
2548   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2549     return -1;
2550 
2551   if (!fp) fp = msg->fp;
2552 
2553   mutt_mktemp (tempfile, sizeof (tempfile));
2554   if ((f = safe_fopen (tempfile, "w")) != NULL)
2555   {
2556     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2557     char* msgid_str;
2558 
2559     if (!option (OPTBOUNCEDELIVERED))
2560       ch_flags |= CH_WEED_DELIVERED;
2561 
2562     fseeko (fp, h->offset, 0);
2563     fprintf (f, "Resent-From: %s", resent_from);
2564     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2565     msgid_str = mutt_gen_msgid();
2566     fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
2567     fputs ("Resent-To: ", f);
2568     mutt_write_address_list (to, f, 11, 0);
2569     mutt_copy_header (fp, h, f, ch_flags, NULL);
2570     fputc ('\n', f);
2571     mutt_copy_bytes (fp, f, h->content->length);
2572     safe_fclose (&f);
2573     FREE (&msgid_str);
2574 
2575 #if USE_SMTP
2576     if (SmtpUrl)
2577       ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2578                             h->content->encoding == ENC8BIT);
2579     else
2580 #endif /* USE_SMTP */
2581     ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2582 			  	h->content->encoding == ENC8BIT);
2583   }
2584 
2585   if (msg)
2586     mx_close_message (&msg);
2587 
2588   return ret;
2589 }
2590 
mutt_bounce_message(FILE * fp,HEADER * h,ADDRESS * to)2591 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2592 {
2593   ADDRESS *from;
2594   const char *fqdn = mutt_fqdn (1);
2595   char resent_from[STRING];
2596   int ret;
2597   char *err;
2598 
2599   resent_from[0] = '\0';
2600   from = mutt_default_from ();
2601 
2602   /*
2603    * mutt_default_from() does not use $realname if the real name is not set
2604    * in $from, so we add it here.  The reason it is not added in
2605    * mutt_default_from() is that during normal sending, we execute
2606    * send-hooks and set the realname last so that it can be changed based
2607    * upon message criteria.
2608    */
2609   if (! from->personal)
2610     from->personal = safe_strdup(Realname);
2611 
2612   if (fqdn)
2613     rfc822_qualify (from, fqdn);
2614 
2615   rfc2047_encode_adrlist (from, "Resent-From");
2616   if (mutt_addrlist_to_idna (from, &err))
2617   {
2618     mutt_error (_("Bad IDN %s while preparing resent-from."),
2619 		err);
2620     rfc822_free_address (&from);
2621     return -1;
2622   }
2623   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2624 
2625   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2626 
2627   rfc822_free_address (&from);
2628 
2629   return ret;
2630 }
2631 
2632 
2633 /* given a list of addresses, return a list of unique addresses */
mutt_remove_duplicates(ADDRESS * addr)2634 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2635 {
2636   ADDRESS *top = addr;
2637   ADDRESS **last = &top;
2638   ADDRESS *tmp;
2639   int dup;
2640 
2641   while (addr)
2642   {
2643     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2644     {
2645       if (tmp->mailbox && addr->mailbox &&
2646 	  !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2647       {
2648 	dup = 1;
2649 	break;
2650       }
2651     }
2652 
2653     if (dup)
2654     {
2655       dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2656 		  addr->mailbox));
2657 
2658       *last = addr->next;
2659 
2660       addr->next = NULL;
2661       rfc822_free_address(&addr);
2662 
2663       addr = *last;
2664     }
2665     else
2666     {
2667       last = &addr->next;
2668       addr = addr->next;
2669     }
2670   }
2671 
2672   return (top);
2673 }
2674 
set_noconv_flags(BODY * b,short flag)2675 static void set_noconv_flags (BODY *b, short flag)
2676 {
2677   for(; b; b = b->next)
2678   {
2679     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2680       set_noconv_flags (b->parts, flag);
2681     else if (b->type == TYPETEXT && b->noconv)
2682     {
2683       if (flag)
2684 	mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2685       else
2686 	mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2687     }
2688   }
2689 }
2690 
mutt_write_fcc(const char * path,HEADER * hdr,const char * msgid,int post,char * fcc)2691 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2692 {
2693   CONTEXT f;
2694   MESSAGE *msg;
2695   char tempfile[_POSIX_PATH_MAX];
2696   FILE *tempfp = NULL;
2697   int r, need_buffy_cleanup = 0;
2698   struct stat st;
2699   char buf[SHORT_STRING];
2700 
2701   if (post)
2702     set_noconv_flags (hdr->content, 1);
2703 
2704   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2705   {
2706     dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2707 		path));
2708     return (-1);
2709   }
2710 
2711   /* We need to add a Content-Length field to avoid problems where a line in
2712    * the message body begins with "From "
2713    */
2714   if (f.magic == M_MMDF || f.magic == M_MBOX)
2715   {
2716     mutt_mktemp (tempfile, sizeof (tempfile));
2717     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2718     {
2719       mutt_perror (tempfile);
2720       mx_close_mailbox (&f, NULL);
2721       return (-1);
2722     }
2723     /* remember new mail status before appending message */
2724     need_buffy_cleanup = 1;
2725     stat (path, &st);
2726   }
2727 
2728   hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2729   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2730   {
2731     mx_close_mailbox (&f, NULL);
2732     return (-1);
2733   }
2734 
2735   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2736    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2737    * */
2738   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2739 
2740   /* (postponment) if this was a reply of some sort, <msgid> contians the
2741    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2742    * header so it can be picked up if the message is recalled at a later
2743    * point in time.  This will allow the message to be marked as replied if
2744    * the same mailbox is still open.
2745    */
2746   if (post && msgid)
2747     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2748 
2749   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2750    * it can be picked up when the message is recalled
2751    */
2752   if (post && fcc)
2753     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2754 
2755   if (f.magic == M_MMDF || f.magic == M_MBOX)
2756     fprintf (msg->fp, "Status: RO\n");
2757 
2758   /* mutt_write_rfc822_header() only writes out a Date: header with
2759    * mode == 0, i.e. _not_ postponment; so write out one ourself */
2760   if (post)
2761     fprintf (msg->fp, "%s", mutt_make_date (buf, sizeof (buf)));
2762 
2763   /* (postponment) if the mail is to be signed or encrypted, save this info */
2764   if ((WithCrypto & APPLICATION_PGP)
2765       && post && (hdr->security & APPLICATION_PGP))
2766   {
2767     fputs ("X-Mutt-PGP: ", msg->fp);
2768     if (hdr->security & ENCRYPT)
2769       fputc ('E', msg->fp);
2770     if (hdr->security & SIGN)
2771     {
2772       fputc ('S', msg->fp);
2773       if (PgpSignAs && *PgpSignAs)
2774         fprintf (msg->fp, "<%s>", PgpSignAs);
2775     }
2776     if (hdr->security & INLINE)
2777       fputc ('I', msg->fp);
2778     fputc ('\n', msg->fp);
2779   }
2780 
2781   /* (postponment) if the mail is to be signed or encrypted, save this info */
2782   if ((WithCrypto & APPLICATION_SMIME)
2783       && post && (hdr->security & APPLICATION_SMIME))
2784   {
2785     fputs ("X-Mutt-SMIME: ", msg->fp);
2786     if (hdr->security & ENCRYPT) {
2787 	fputc ('E', msg->fp);
2788 	if (SmimeCryptAlg && *SmimeCryptAlg)
2789 	    fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2790     }
2791     if (hdr->security & SIGN) {
2792 	fputc ('S', msg->fp);
2793 	if (SmimeDefaultKey && *SmimeDefaultKey)
2794 	    fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2795     }
2796     if (hdr->security & INLINE)
2797       fputc ('I', msg->fp);
2798     fputc ('\n', msg->fp);
2799   }
2800 
2801 #ifdef MIXMASTER
2802   /* (postponement) if the mail is to be sent through a mixmaster
2803    * chain, save that information
2804    */
2805 
2806   if (post && hdr->chain && hdr->chain)
2807   {
2808     LIST *p;
2809 
2810     fputs ("X-Mutt-Mix:", msg->fp);
2811     for (p = hdr->chain; p; p = p->next)
2812       fprintf (msg->fp, " %s", (char *) p->data);
2813 
2814     fputc ('\n', msg->fp);
2815   }
2816 #endif
2817 
2818   if (tempfp)
2819   {
2820     char sasha[LONG_STRING];
2821     int lines = 0;
2822 
2823     mutt_write_mime_body (hdr->content, tempfp);
2824 
2825     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2826      * this will happen, and it can cause problems parsing the mailbox
2827      * later.
2828      */
2829     fseek (tempfp, -1, 2);
2830     if (fgetc (tempfp) != '\n')
2831     {
2832       fseek (tempfp, 0, 2);
2833       fputc ('\n', tempfp);
2834     }
2835 
2836     fflush (tempfp);
2837     if (ferror (tempfp))
2838     {
2839       dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2840       safe_fclose (&tempfp);
2841       unlink (tempfile);
2842       mx_commit_message (msg, &f);	/* XXX - really? */
2843       mx_close_message (&msg);
2844       mx_close_mailbox (&f, NULL);
2845       return -1;
2846     }
2847 
2848     /* count the number of lines */
2849     rewind (tempfp);
2850     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2851       lines++;
2852     fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
2853     fprintf (msg->fp, "Lines: %d\n\n", lines);
2854 
2855     /* copy the body and clean up */
2856     rewind (tempfp);
2857     r = mutt_copy_stream (tempfp, msg->fp);
2858     if (fclose (tempfp) != 0)
2859       r = -1;
2860     /* if there was an error, leave the temp version */
2861     if (!r)
2862       unlink (tempfile);
2863   }
2864   else
2865   {
2866     fputc ('\n', msg->fp); /* finish off the header */
2867     r = mutt_write_mime_body (hdr->content, msg->fp);
2868   }
2869 
2870   if (mx_commit_message (msg, &f) != 0)
2871     r = -1;
2872   mx_close_message (&msg);
2873   mx_close_mailbox (&f, NULL);
2874 
2875   if (!post && need_buffy_cleanup)
2876     mutt_buffy_cleanup (path, &st);
2877 
2878   if (post)
2879     set_noconv_flags (hdr->content, 0);
2880 
2881   return r;
2882 }
2883