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