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