mutt stable branch with some hacks
at master 962 lines 24 kB view raw
1/* 2 * Copyright (C) 1996-2000,2002,2014 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#if HAVE_CONFIG_H 20# include "config.h" 21#endif 22 23#include "mutt.h" 24#include "mailbox.h" 25#include "mx.h" 26#include "copy.h" 27#include "rfc2047.h" 28#include "mime.h" 29#include "mutt_crypt.h" 30#include "mutt_idna.h" 31#include "mutt_curses.h" 32 33#include <string.h> 34#include <stdlib.h> 35#include <ctype.h> 36#include <unistd.h> /* needed for SEEK_SET under SunOS 4.1.4 */ 37 38static int address_header_decode (char **str); 39static int copy_delete_attach (BODY *b, FILE *fpin, FILE *fpout, char *date); 40 41/* Ok, the only reason for not merging this with mutt_copy_header() 42 * below is to avoid creating a HEADER structure in message_handler(). 43 * Also, this one will wrap headers much more aggressively than the other one. 44 */ 45int 46mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags, 47 const char *prefix) 48{ 49 int from = 0; 50 int this_is_from; 51 int ignore = 0; 52 char buf[LONG_STRING]; /* should be long enough to get most fields in one pass */ 53 char *nl; 54 LIST *t; 55 char **headers; 56 int hdr_count; 57 int x; 58 char *this_one = NULL; 59 size_t this_one_len = 0; 60 int error; 61 62 if (ftello (in) != off_start) 63 fseeko (in, off_start, 0); 64 65 buf[0] = '\n'; 66 buf[1] = 0; 67 68 if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0) 69 { 70 /* Without these flags to complicate things 71 * we can do a more efficient line to line copying 72 */ 73 while (ftello (in) < off_end) 74 { 75 nl = strchr (buf, '\n'); 76 77 if ((fgets (buf, sizeof (buf), in)) == NULL) 78 break; 79 80 /* Is it the beginning of a header? */ 81 if (nl && buf[0] != ' ' && buf[0] != '\t') 82 { 83 ignore = 1; 84 if (!from && mutt_strncmp ("From ", buf, 5) == 0) 85 { 86 if ((flags & CH_FROM) == 0) 87 continue; 88 from = 1; 89 } 90 else if (flags & (CH_NOQFROM) && 91 ascii_strncasecmp (">From ", buf, 6) == 0) 92 continue; 93 94 else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) 95 break; /* end of header */ 96 97 if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && 98 (ascii_strncasecmp ("Status:", buf, 7) == 0 || 99 ascii_strncasecmp ("X-Status:", buf, 9) == 0)) 100 continue; 101 if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && 102 (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 || 103 ascii_strncasecmp ("Lines:", buf, 6) == 0)) 104 continue; 105 if ((flags & CH_UPDATE_REFS) && 106 ascii_strncasecmp ("References:", buf, 11) == 0) 107 continue; 108 if ((flags & CH_UPDATE_IRT) && 109 ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) 110 continue; 111 ignore = 0; 112 } 113 114 if (!ignore && fputs (buf, out) == EOF) 115 return (-1); 116 } 117 return 0; 118 } 119 120 hdr_count = 1; 121 x = 0; 122 error = FALSE; 123 124 /* We are going to read and collect the headers in an array 125 * so we are able to do re-ordering. 126 * First count the number of entries in the array 127 */ 128 if (flags & CH_REORDER) 129 { 130 for (t = HeaderOrderList; t; t = t->next) 131 { 132 dprint(3, (debugfile, "Reorder list: %s\n", t->data)); 133 hdr_count++; 134 } 135 } 136 137 dprint (1, (debugfile, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not")); 138 139 headers = safe_calloc (hdr_count, sizeof (char *)); 140 141 /* Read all the headers into the array */ 142 while (ftello (in) < off_end) 143 { 144 nl = strchr (buf, '\n'); 145 146 /* Read a line */ 147 if ((fgets (buf, sizeof (buf), in)) == NULL) 148 break; 149 150 /* Is it the beginning of a header? */ 151 if (nl && buf[0] != ' ' && buf[0] != '\t') 152 { 153 /* Do we have anything pending? */ 154 if (this_one) 155 { 156 if (flags & CH_DECODE) 157 { 158 if (!address_header_decode (&this_one)) 159 rfc2047_decode (&this_one); 160 this_one_len = mutt_strlen (this_one); 161 } 162 163 if (!headers[x]) 164 headers[x] = this_one; 165 else 166 { 167 int hlen = mutt_strlen (headers[x]); 168 169 safe_realloc (&headers[x], hlen + this_one_len + sizeof (char)); 170 strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */ 171 FREE (&this_one); 172 } 173 174 this_one = NULL; 175 } 176 177 ignore = 1; 178 this_is_from = 0; 179 if (!from && mutt_strncmp ("From ", buf, 5) == 0) 180 { 181 if ((flags & CH_FROM) == 0) 182 continue; 183 this_is_from = from = 1; 184 } 185 else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) 186 break; /* end of header */ 187 188 /* note: CH_FROM takes precedence over header weeding. */ 189 if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) && 190 (flags & CH_WEED) && 191 mutt_matches_ignore (buf, Ignore) && 192 !mutt_matches_ignore (buf, UnIgnore)) 193 continue; 194 if ((flags & CH_WEED_DELIVERED) && 195 ascii_strncasecmp ("Delivered-To:", buf, 13) == 0) 196 continue; 197 if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && 198 (ascii_strncasecmp ("Status:", buf, 7) == 0 || 199 ascii_strncasecmp ("X-Status:", buf, 9) == 0)) 200 continue; 201 if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && 202 (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 || 203 ascii_strncasecmp ("Lines:", buf, 6) == 0)) 204 continue; 205 if ((flags & CH_MIME) && 206 ((ascii_strncasecmp ("content-", buf, 8) == 0 && 207 (ascii_strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 || 208 ascii_strncasecmp ("type:", buf + 8, 5) == 0)) || 209 ascii_strncasecmp ("mime-version:", buf, 13) == 0)) 210 continue; 211 if ((flags & CH_UPDATE_REFS) && 212 ascii_strncasecmp ("References:", buf, 11) == 0) 213 continue; 214 if ((flags & CH_UPDATE_IRT) && 215 ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) 216 continue; 217 218 /* Find x -- the array entry where this header is to be saved */ 219 if (flags & CH_REORDER) 220 { 221 for (t = HeaderOrderList, x = 0 ; (t) ; t = t->next, x++) 222 { 223 if (!ascii_strncasecmp (buf, t->data, mutt_strlen (t->data))) 224 { 225 dprint(2, (debugfile, "Reorder: %s matches %s\n", t->data, buf)); 226 break; 227 } 228 } 229 } 230 231 ignore = 0; 232 } /* If beginning of header */ 233 234 if (!ignore) 235 { 236 dprint (2, (debugfile, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count)); 237 if (!this_one) { 238 this_one = safe_strdup (buf); 239 this_one_len = mutt_strlen (this_one); 240 } else { 241 int blen = mutt_strlen (buf); 242 243 safe_realloc (&this_one, this_one_len + blen + sizeof (char)); 244 strcat (this_one + this_one_len, buf); /* __STRCAT_CHECKED__ */ 245 this_one_len += blen; 246 } 247 } 248 } /* while (ftello (in) < off_end) */ 249 250 /* Do we have anything pending? -- XXX, same code as in above in the loop. */ 251 if (this_one) 252 { 253 if (flags & CH_DECODE) 254 { 255 if (!address_header_decode (&this_one)) 256 rfc2047_decode (&this_one); 257 this_one_len = mutt_strlen (this_one); 258 } 259 260 if (!headers[x]) 261 headers[x] = this_one; 262 else 263 { 264 int hlen = mutt_strlen (headers[x]); 265 266 safe_realloc (&headers[x], hlen + this_one_len + sizeof (char)); 267 strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */ 268 FREE (&this_one); 269 } 270 271 this_one = NULL; 272 } 273 274 /* Now output the headers in order */ 275 for (x = 0; x < hdr_count; x++) 276 { 277 if (headers[x]) 278 { 279#if 0 280 if (flags & CH_DECODE) 281 rfc2047_decode (&headers[x]); 282#endif 283 284 /* We couldn't do the prefixing when reading because RFC 2047 285 * decoding may have concatenated lines. 286 */ 287 288 if (flags & (CH_DECODE|CH_PREFIX)) 289 { 290 if (mutt_write_one_header (out, 0, headers[x], 291 flags & CH_PREFIX ? prefix : 0, 292 mutt_window_wrap_cols (MuttIndexWindow, Wrap), flags) == -1) 293 { 294 error = TRUE; 295 break; 296 } 297 } 298 else 299 { 300 if (fputs (headers[x], out) == EOF) 301 { 302 error = TRUE; 303 break; 304 } 305 } 306 } 307 } 308 309 /* Free in a separate loop to be sure that all headers are freed 310 * in case of error. */ 311 for (x = 0; x < hdr_count; x++) 312 FREE (&headers[x]); 313 FREE (&headers); 314 315 if (error) 316 return (-1); 317 return (0); 318} 319 320/* flags 321 CH_DECODE RFC2047 header decoding 322 CH_FROM retain the "From " message separator 323 CH_FORCE_FROM give CH_FROM precedence over CH_WEED 324 CH_MIME ignore MIME fields 325 CH_NOLEN don't write Content-Length: and Lines: 326 CH_NONEWLINE don't output a newline after the header 327 CH_NOSTATUS ignore the Status: and X-Status: 328 CH_PREFIX quote header with $indent_str 329 CH_REORDER output header in order specified by `hdr_order' 330 CH_TXTPLAIN generate text/plain MIME headers [hack alert.] 331 CH_UPDATE write new Status: and X-Status: 332 CH_UPDATE_LEN write new Content-Length: and Lines: 333 CH_XMIT ignore Lines: and Content-Length: 334 CH_WEED do header weeding 335 CH_NOQFROM ignore ">From " line 336 CH_UPDATE_IRT update the In-Reply-To: header 337 CH_UPDATE_REFS update the References: header 338 339 prefix 340 string to use if CH_PREFIX is set 341 */ 342 343int 344mutt_copy_header (FILE *in, HEADER *h, FILE *out, int flags, const char *prefix) 345{ 346 char buffer[SHORT_STRING]; 347 348 if (h->env) 349 flags |= (h->env->irt_changed ? CH_UPDATE_IRT : 0) 350 | (h->env->refs_changed ? CH_UPDATE_REFS : 0); 351 352 if (mutt_copy_hdr (in, out, h->offset, h->content->offset, flags, prefix) == -1) 353 return -1; 354 355 if (flags & CH_TXTPLAIN) 356 { 357 char chsbuf[SHORT_STRING]; 358 fputs ("MIME-Version: 1.0\n", out); 359 fputs ("Content-Transfer-Encoding: 8bit\n", out); 360 fputs ("Content-Type: text/plain; charset=", out); 361 mutt_canonical_charset (chsbuf, sizeof (chsbuf), Charset ? Charset : "us-ascii"); 362 rfc822_cat(buffer, sizeof(buffer), chsbuf, MimeSpecials); 363 fputs(buffer, out); 364 fputc('\n', out); 365 } 366 367 if ((flags & CH_UPDATE_IRT) && h->env->in_reply_to) 368 { 369 LIST *listp = h->env->in_reply_to; 370 fputs ("In-Reply-To:", out); 371 for (; listp; listp = listp->next) 372 { 373 fputc (' ', out); 374 fputs (listp->data, out); 375 } 376 fputc ('\n', out); 377 } 378 379 if ((flags & CH_UPDATE_REFS) && h->env->references) 380 { 381 fputs ("References:", out); 382 mutt_write_references (h->env->references, out, 0); 383 fputc ('\n', out); 384 } 385 386 if ((flags & CH_UPDATE) && (flags & CH_NOSTATUS) == 0) 387 { 388 if (h->old || h->read) 389 { 390 fputs ("Status: ", out); 391 if (h->read) 392 fputs ("RO", out); 393 else if (h->old) 394 fputc ('O', out); 395 fputc ('\n', out); 396 } 397 398 if (h->flagged || h->replied) 399 { 400 fputs ("X-Status: ", out); 401 if (h->replied) 402 fputc ('A', out); 403 if (h->flagged) 404 fputc ('F', out); 405 fputc ('\n', out); 406 } 407 } 408 409 if (flags & CH_UPDATE_LEN && 410 (flags & CH_NOLEN) == 0) 411 { 412 fprintf (out, "Content-Length: " OFF_T_FMT "\n", h->content->length); 413 if (h->lines != 0 || h->content->length == 0) 414 fprintf (out, "Lines: %d\n", h->lines); 415 } 416 417 if ((flags & CH_NONEWLINE) == 0) 418 { 419 if (flags & CH_PREFIX) 420 fputs(prefix, out); 421 fputc ('\n', out); /* add header terminator */ 422 } 423 424 if (ferror (out) || feof (out)) 425 return -1; 426 427 return 0; 428} 429 430/* Count the number of lines and bytes to be deleted in this body*/ 431static int count_delete_lines (FILE *fp, BODY *b, LOFF_T *length, size_t datelen) 432{ 433 int dellines = 0; 434 long l; 435 int ch; 436 437 if (b->deleted) 438 { 439 fseeko (fp, b->offset, SEEK_SET); 440 for (l = b->length ; l ; l --) 441 { 442 ch = getc (fp); 443 if (ch == EOF) 444 break; 445 if (ch == '\n') 446 dellines ++; 447 } 448 dellines -= 3; 449 *length -= b->length - (84 + datelen); 450 /* Count the number of digits exceeding the first one to write the size */ 451 for (l = 10 ; b->length >= l ; l *= 10) 452 (*length) ++; 453 } 454 else 455 { 456 for (b = b->parts ; b ; b = b->next) 457 dellines += count_delete_lines (fp, b, length, datelen); 458 } 459 return dellines; 460} 461 462/* make a copy of a message 463 * 464 * fpout where to write output 465 * fpin where to get input 466 * hdr header of message being copied 467 * body structure of message being copied 468 * flags 469 * MUTT_CM_NOHEADER don't copy header 470 * MUTT_CM_PREFIX quote header and body 471 * MUTT_CM_DECODE decode message body to text/plain 472 * MUTT_CM_DISPLAY displaying output to the user 473 * MUTT_CM_PRINTING printing the message 474 * MUTT_CM_UPDATE update structures in memory after syncing 475 * MUTT_CM_DECODE_PGP used for decoding PGP messages 476 * MUTT_CM_CHARCONV perform character set conversion 477 * chflags flags to mutt_copy_header() 478 */ 479 480int 481_mutt_copy_message (FILE *fpout, FILE *fpin, HEADER *hdr, BODY *body, 482 int flags, int chflags) 483{ 484 char prefix[SHORT_STRING]; 485 STATE s; 486 LOFF_T new_offset = -1; 487 int rc = 0; 488 489 if (flags & MUTT_CM_PREFIX) 490 { 491 if (option (OPTTEXTFLOWED)) 492 strfcpy (prefix, ">", sizeof (prefix)); 493 else 494 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context, hdr, 0); 495 } 496 497 if ((flags & MUTT_CM_NOHEADER) == 0) 498 { 499 if (flags & MUTT_CM_PREFIX) 500 chflags |= CH_PREFIX; 501 502 else if (hdr->attach_del && (chflags & CH_UPDATE_LEN)) 503 { 504 int new_lines; 505 LOFF_T new_length = body->length; 506 char date[SHORT_STRING]; 507 508 mutt_make_date (date, sizeof (date)); 509 date[5] = date[mutt_strlen (date) - 1] = '\"'; 510 511 /* Count the number of lines and bytes to be deleted */ 512 fseeko (fpin, body->offset, SEEK_SET); 513 new_lines = hdr->lines - 514 count_delete_lines (fpin, body, &new_length, mutt_strlen (date)); 515 516 /* Copy the headers */ 517 if (mutt_copy_header (fpin, hdr, fpout, 518 chflags | CH_NOLEN | CH_NONEWLINE, NULL)) 519 return -1; 520 fprintf (fpout, "Content-Length: " OFF_T_FMT "\n", new_length); 521 if (new_lines <= 0) 522 new_lines = 0; 523 else 524 fprintf (fpout, "Lines: %d\n", new_lines); 525 526 putc ('\n', fpout); 527 if (ferror (fpout) || feof (fpout)) 528 return -1; 529 new_offset = ftello (fpout); 530 531 /* Copy the body */ 532 fseeko (fpin, body->offset, SEEK_SET); 533 if (copy_delete_attach (body, fpin, fpout, date)) 534 return -1; 535 536#ifdef DEBUG 537 { 538 LOFF_T fail = ((ftello (fpout) - new_offset) - new_length); 539 540 if (fail) 541 { 542 mutt_error ("The length calculation was wrong by %ld bytes", fail); 543 new_length += fail; 544 mutt_sleep (1); 545 } 546 } 547#endif 548 549 /* Update original message if we are sync'ing a mailfolder */ 550 if (flags & MUTT_CM_UPDATE) 551 { 552 hdr->attach_del = 0; 553 hdr->lines = new_lines; 554 body->offset = new_offset; 555 556 /* update the total size of the mailbox to reflect this deletion */ 557 Context->size -= body->length - new_length; 558 /* 559 * if the message is visible, update the visible size of the mailbox 560 * as well. 561 */ 562 if (Context->v2r[hdr->msgno] != -1) 563 Context->vsize -= body->length - new_length; 564 565 body->length = new_length; 566 mutt_free_body (&body->parts); 567 } 568 569 return 0; 570 } 571 572 if (mutt_copy_header (fpin, hdr, fpout, chflags, 573 (chflags & CH_PREFIX) ? prefix : NULL) == -1) 574 return -1; 575 576 new_offset = ftello (fpout); 577 } 578 579 if (flags & MUTT_CM_DECODE) 580 { 581 /* now make a text/plain version of the message */ 582 memset (&s, 0, sizeof (STATE)); 583 s.fpin = fpin; 584 s.fpout = fpout; 585 if (flags & MUTT_CM_PREFIX) 586 s.prefix = prefix; 587 if (flags & MUTT_CM_DISPLAY) 588 s.flags |= MUTT_DISPLAY; 589 if (flags & MUTT_CM_PRINTING) 590 s.flags |= MUTT_PRINTING; 591 if (flags & MUTT_CM_WEED) 592 s.flags |= MUTT_WEED; 593 if (flags & MUTT_CM_CHARCONV) 594 s.flags |= MUTT_CHARCONV; 595 if (flags & MUTT_CM_REPLYING) 596 s.flags |= MUTT_REPLYING; 597 598 if (WithCrypto && flags & MUTT_CM_VERIFY) 599 s.flags |= MUTT_VERIFY; 600 601 rc = mutt_body_handler (body, &s); 602 } 603 else if (WithCrypto 604 && (flags & MUTT_CM_DECODE_CRYPT) && (hdr->security & ENCRYPT)) 605 { 606 BODY *cur = NULL; 607 FILE *fp; 608 609 if ((WithCrypto & APPLICATION_PGP) 610 && (flags & MUTT_CM_DECODE_PGP) && (hdr->security & APPLICATION_PGP) && 611 hdr->content->type == TYPEMULTIPART) 612 { 613 if (crypt_pgp_decrypt_mime (fpin, &fp, hdr->content, &cur)) 614 return (-1); 615 fputs ("MIME-Version: 1.0\n", fpout); 616 } 617 618 if ((WithCrypto & APPLICATION_SMIME) 619 && (flags & MUTT_CM_DECODE_SMIME) && (hdr->security & APPLICATION_SMIME) 620 && hdr->content->type == TYPEAPPLICATION) 621 { 622 if (crypt_smime_decrypt_mime (fpin, &fp, hdr->content, &cur)) 623 return (-1); 624 } 625 626 if (!cur) 627 { 628 mutt_error (_("No decryption engine available for message")); 629 return -1; 630 } 631 632 mutt_write_mime_header (cur, fpout); 633 fputc ('\n', fpout); 634 635 fseeko (fp, cur->offset, 0); 636 if (mutt_copy_bytes (fp, fpout, cur->length) == -1) 637 { 638 safe_fclose (&fp); 639 mutt_free_body (&cur); 640 return (-1); 641 } 642 mutt_free_body (&cur); 643 safe_fclose (&fp); 644 } 645 else 646 { 647 fseeko (fpin, body->offset, 0); 648 if (flags & MUTT_CM_PREFIX) 649 { 650 int c; 651 size_t bytes = body->length; 652 653 fputs(prefix, fpout); 654 655 while((c = fgetc(fpin)) != EOF && bytes--) 656 { 657 fputc(c, fpout); 658 if(c == '\n') 659 { 660 fputs(prefix, fpout); 661 } 662 } 663 } 664 else if (mutt_copy_bytes (fpin, fpout, body->length) == -1) 665 return -1; 666 } 667 668 if ((flags & MUTT_CM_UPDATE) && (flags & MUTT_CM_NOHEADER) == 0 669 && new_offset != -1) 670 { 671 body->offset = new_offset; 672 mutt_free_body (&body->parts); 673 } 674 675 return rc; 676} 677 678/* should be made to return -1 on fatal errors, and 1 on non-fatal errors 679 * like partial decode, where it is worth displaying as much as possible */ 680int 681mutt_copy_message (FILE *fpout, CONTEXT *src, HEADER *hdr, int flags, 682 int chflags) 683{ 684 MESSAGE *msg; 685 int r; 686 687 if ((msg = mx_open_message (src, hdr->msgno)) == NULL) 688 return -1; 689 if ((r = _mutt_copy_message (fpout, msg->fp, hdr, hdr->content, flags, chflags)) == 0 690 && (ferror (fpout) || feof (fpout))) 691 { 692 dprint (1, (debugfile, "_mutt_copy_message failed to detect EOF!\n")); 693 r = -1; 694 } 695 mx_close_message (src, &msg); 696 return r; 697} 698 699/* appends a copy of the given message to a mailbox 700 * 701 * dest destination mailbox 702 * fpin where to get input 703 * src source mailbox 704 * hdr message being copied 705 * body structure of message being copied 706 * flags mutt_copy_message() flags 707 * chflags mutt_copy_header() flags 708 */ 709 710int 711_mutt_append_message (CONTEXT *dest, FILE *fpin, CONTEXT *src, HEADER *hdr, 712 BODY *body, int flags, int chflags) 713{ 714 char buf[STRING]; 715 MESSAGE *msg; 716 int r; 717 718 fseeko (fpin, hdr->offset, 0); 719 if (fgets (buf, sizeof (buf), fpin) == NULL) 720 return -1; 721 722 if ((msg = mx_open_new_message (dest, hdr, is_from (buf, NULL, 0, NULL) ? 0 : MUTT_ADD_FROM)) == NULL) 723 return -1; 724 if (dest->magic == MUTT_MBOX || dest->magic == MUTT_MMDF) 725 chflags |= CH_FROM | CH_FORCE_FROM; 726 chflags |= (dest->magic == MUTT_MAILDIR ? CH_NOSTATUS : CH_UPDATE); 727 r = _mutt_copy_message (msg->fp, fpin, hdr, body, flags, chflags); 728 if (mx_commit_message (msg, dest) != 0) 729 r = -1; 730 731 mx_close_message (dest, &msg); 732 return r; 733} 734 735int 736mutt_append_message (CONTEXT *dest, CONTEXT *src, HEADER *hdr, int cmflags, 737 int chflags) 738{ 739 MESSAGE *msg; 740 int r; 741 742 if ((msg = mx_open_message (src, hdr->msgno)) == NULL) 743 return -1; 744 r = _mutt_append_message (dest, msg->fp, src, hdr, hdr->content, cmflags, chflags); 745 mx_close_message (src, &msg); 746 return r; 747} 748 749/* 750 * This function copies a message body, while deleting _in_the_copy_ 751 * any attachments which are marked for deletion. 752 * Nothing is changed in the original message -- this is left to the caller. 753 * 754 * The function will return 0 on success and -1 on failure. 755 */ 756static int copy_delete_attach (BODY *b, FILE *fpin, FILE *fpout, char *date) 757{ 758 BODY *part; 759 760 for (part = b->parts ; part ; part = part->next) 761 { 762 if (part->deleted || part->parts) 763 { 764 /* Copy till start of this part */ 765 if (mutt_copy_bytes (fpin, fpout, part->hdr_offset - ftello (fpin))) 766 return -1; 767 768 if (part->deleted) 769 { 770 fprintf (fpout, 771 "Content-Type: message/external-body; access-type=x-mutt-deleted;\n" 772 "\texpiration=%s; length=" OFF_T_FMT "\n" 773 "\n", date + 5, part->length); 774 if (ferror (fpout)) 775 return -1; 776 777 /* Copy the original mime headers */ 778 if (mutt_copy_bytes (fpin, fpout, part->offset - ftello (fpin))) 779 return -1; 780 781 /* Skip the deleted body */ 782 fseeko (fpin, part->offset + part->length, SEEK_SET); 783 } 784 else 785 { 786 if (copy_delete_attach (part, fpin, fpout, date)) 787 return -1; 788 } 789 } 790 } 791 792 /* Copy the last parts */ 793 if (mutt_copy_bytes (fpin, fpout, b->offset + b->length - ftello (fpin))) 794 return -1; 795 796 return 0; 797} 798 799/* 800 * This function is the equivalent of mutt_write_address_list(), 801 * but writes to a buffer instead of writing to a stream. 802 * mutt_write_address_list could be re-used if we wouldn't store 803 * all the decoded headers in a huge array, first. 804 * 805 * XXX - fix that. 806 */ 807 808static void format_address_header (char **h, ADDRESS *a) 809{ 810 char buf[HUGE_STRING]; 811 char cbuf[STRING]; 812 char c2buf[STRING]; 813 char *p = NULL; 814 int l, linelen, buflen, count, cbuflen, c2buflen, plen; 815 816 linelen = mutt_strlen (*h); 817 plen = linelen; 818 buflen = linelen + 3; 819 820 safe_realloc (h, buflen); 821 for (count = 0; a; a = a->next, count++) 822 { 823 ADDRESS *tmp = a->next; 824 a->next = NULL; 825 *buf = *cbuf = *c2buf = '\0'; 826 l = rfc822_write_address (buf, sizeof (buf), a, 0); 827 a->next = tmp; 828 829 if (count && linelen + l > 74) 830 { 831 strcpy (cbuf, "\n\t"); /* __STRCPY_CHECKED__ */ 832 linelen = l + 8; 833 } 834 else 835 { 836 if (a->mailbox) 837 { 838 strcpy (cbuf, " "); /* __STRCPY_CHECKED__ */ 839 linelen++; 840 } 841 linelen += l; 842 } 843 if (!a->group && a->next && a->next->mailbox) 844 { 845 linelen++; 846 buflen++; 847 strcpy (c2buf, ","); /* __STRCPY_CHECKED__ */ 848 } 849 850 cbuflen = mutt_strlen (cbuf); 851 c2buflen = mutt_strlen (c2buf); 852 buflen += l + cbuflen + c2buflen; 853 safe_realloc (h, buflen); 854 p = *h; 855 strcat (p + plen, cbuf); /* __STRCAT_CHECKED__ */ 856 plen += cbuflen; 857 strcat (p + plen, buf); /* __STRCAT_CHECKED__ */ 858 plen += l; 859 strcat (p + plen, c2buf); /* __STRCAT_CHECKED__ */ 860 plen += c2buflen; 861 } 862 863 /* Space for this was allocated in the beginning of this function. */ 864 strcat (p + plen, "\n"); /* __STRCAT_CHECKED__ */ 865} 866 867static int address_header_decode (char **h) 868{ 869 char *s = *h; 870 int l, rp = 0; 871 872 ADDRESS *a = NULL; 873 ADDRESS *cur = NULL; 874 875 switch (tolower ((unsigned char) *s)) 876 { 877 case 'r': 878 { 879 if (ascii_strncasecmp (s, "return-path:", 12) == 0) 880 { 881 l = 12; 882 rp = 1; 883 break; 884 } 885 else if (ascii_strncasecmp (s, "reply-to:", 9) == 0) 886 { 887 l = 9; 888 break; 889 } 890 return 0; 891 } 892 case 'f': 893 { 894 if (ascii_strncasecmp (s, "from:", 5)) 895 return 0; 896 l = 5; 897 break; 898 } 899 case 'c': 900 { 901 if (ascii_strncasecmp (s, "cc:", 3)) 902 return 0; 903 l = 3; 904 break; 905 906 } 907 case 'b': 908 { 909 if (ascii_strncasecmp (s, "bcc:", 4)) 910 return 0; 911 l = 4; 912 break; 913 } 914 case 's': 915 { 916 if (ascii_strncasecmp (s, "sender:", 7)) 917 return 0; 918 l = 7; 919 break; 920 } 921 case 't': 922 { 923 if (ascii_strncasecmp (s, "to:", 3)) 924 return 0; 925 l = 3; 926 break; 927 } 928 case 'm': 929 { 930 if (ascii_strncasecmp (s, "mail-followup-to:", 17)) 931 return 0; 932 l = 17; 933 break; 934 } 935 default: return 0; 936 } 937 938 if ((a = rfc822_parse_adrlist (a, s + l)) == NULL) 939 return 0; 940 941 mutt_addrlist_to_local (a); 942 rfc2047_decode_adrlist (a); 943 for (cur = a; cur; cur = cur->next) 944 if (cur->personal) 945 rfc822_dequote_comment (cur->personal); 946 947 /* angle brackets for return path are mandated by RfC5322, 948 * so leave Return-Path as-is */ 949 if (rp) 950 *h = safe_strdup (s); 951 else 952 { 953 *h = safe_calloc (1, l + 2); 954 strfcpy (*h, s, l + 1); 955 format_address_header (h, a); 956 } 957 958 rfc822_free_address (&a); 959 960 FREE (&s); 961 return 1; 962}