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