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