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