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