[ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 3 require_once 'Horde/String.php'; 4 require_once dirname(__FILE__) . '/../MIME.php'; 5 6 /** 7 * The character(s) used internally for EOLs. 8 */ 9 define('MIME_PART_EOL', "\n"); 10 11 /** 12 * The character string designated by RFCs 822/2045 to designate EOLs in MIME 13 * messages. 14 */ 15 define('MIME_PART_RFC_EOL', "\r\n"); 16 17 /* Default MIME parameters. */ 18 19 /** 20 * The default MIME character set. 21 */ 22 define('MIME_DEFAULT_CHARSET', 'us-ascii'); 23 24 /** 25 * The default MIME description. 26 */ 27 define('MIME_DEFAULT_DESCRIPTION', _("unnamed")); 28 29 /** 30 * The default MIME disposition. 31 */ 32 define('MIME_DEFAULT_DISPOSITION', 'inline'); 33 34 /** 35 * The default MIME encoding. 36 */ 37 define('MIME_DEFAULT_ENCODING', '7bit'); 38 39 /** 40 * The MIME_Part:: class provides a wrapper around MIME parts and methods 41 * for dealing with them. 42 * 43 * $Horde: framework/MIME/MIME/Part.php,v 1.177.4.20 2006/03/09 21:59:31 chuck Exp $ 44 * 45 * Copyright 1999-2006 Chuck Hagenbuch <chuck@horde.org> 46 * Copyright 2002-2006 Michael Slusarz <slusarz@bigworm.colorado.edu> 47 * 48 * See the enclosed file COPYING for license information (LGPL). If you 49 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 50 * 51 * @author Chuck Hagenbuch <chuck@horde.org> 52 * @author Michael Slusarz <slusarz@bigworm.colorado.edu> 53 * @since Horde 1.3 54 * @package Horde_MIME 55 */ 56 class MIME_Part { 57 58 /** 59 * The type (ex.: text) of this part. 60 * Per RFC 2045, the default is 'application'. 61 * 62 * @var string 63 */ 64 var $_type = 'application'; 65 66 /** 67 * The subtype (ex.: plain) of this part. 68 * Per RFC 2045, the default is 'octet-stream'. 69 * 70 * @var string 71 */ 72 var $_subtype = 'octet-stream'; 73 74 /** 75 * The body of the part. 76 * 77 * @var string 78 */ 79 var $_contents = ''; 80 81 /** 82 * The desired transfer encoding of this part. 83 * 84 * @var string 85 */ 86 var $_transferEncoding = MIME_DEFAULT_ENCODING; 87 88 /** 89 * The current transfer encoding of the contents of this part. 90 * 91 * @var string 92 */ 93 var $_currentEncoding = null; 94 95 /** 96 * Should the message be encoded via 7-bit? 97 * 98 * @var boolean 99 */ 100 var $_encode7bit = true; 101 102 /** 103 * The description of this part. 104 * 105 * @var string 106 */ 107 var $_description = ''; 108 109 /** 110 * The disposition of this part (inline or attachment). 111 * 112 * @var string 113 */ 114 var $_disposition = MIME_DEFAULT_DISPOSITION; 115 116 /** 117 * The disposition parameters of this part. 118 * 119 * @var array 120 */ 121 var $_dispositionParameters = array(); 122 123 /** 124 * The content type parameters of this part. 125 * 126 * @var array 127 */ 128 var $_contentTypeParameters = array(); 129 130 /** 131 * The subparts of this part. 132 * 133 * @var array 134 */ 135 var $_parts = array(); 136 137 /** 138 * Information/Statistics on the subpart. 139 * 140 * @var array 141 */ 142 var $_information = array(); 143 144 /** 145 * The list of CIDs for this part. 146 * 147 * @var array 148 */ 149 var $_cids = array(); 150 151 /** 152 * The MIME ID of this part. 153 * 154 * @var string 155 */ 156 var $_mimeid = null; 157 158 /** 159 * The sequence to use as EOL for this part. 160 * The default is currently to output the EOL sequence internally as 161 * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045. 162 * To be RFC complaint, the full <CR><LF> EOL combination should be used 163 * when sending a message. 164 * It is not crucial here since the PHP/PEAR mailing functions will handle 165 * the EOL details. 166 * 167 * @var string 168 */ 169 var $_eol = MIME_PART_EOL; 170 171 /** 172 * Internal class flags. 173 * 174 * @var array 175 */ 176 var $_flags = array(); 177 178 /** 179 * Part -> ID mapping cache. 180 * 181 * @var array 182 */ 183 var $_idmap = array(); 184 185 /** 186 * Unique MIME_Part boundary string. 187 * 188 * @var string 189 */ 190 var $_boundary = null; 191 192 /** 193 * Default value for this Part's size. 194 * 195 * @var integer 196 */ 197 var $_bytes = 0; 198 199 /** 200 * The content-ID for this part. 201 * 202 * @var string 203 */ 204 var $_contentid = null; 205 206 /** 207 * MIME_Part constructor. 208 * 209 * @param string $mimetype The content type of the part. 210 * @param string $contents The body of the part. 211 * @param string $charset The character set of the part. 212 * @param string $disposition The content disposition of the part. 213 * @param string $encoding The content encoding of the contents. 214 */ 215 function MIME_Part($mimetype = null, $contents = null, 216 $charset = MIME_DEFAULT_CHARSET, $disposition = null, 217 $encoding = null) 218 { 219 /* Create the unique MIME_Part boundary string. */ 220 $this->_generateBoundary(); 221 222 /* The character set should always be set, even if we are dealing 223 * with Content-Types other than text/*. */ 224 $this->setCharset($charset); 225 226 if (!is_null($mimetype)) { 227 $this->setType($mimetype); 228 } 229 if (!is_null($contents)) { 230 $this->setContents($contents, $encoding); 231 } 232 if (!is_null($disposition)) { 233 $this->setDisposition($disposition); 234 } 235 } 236 237 /** 238 * Set the content-disposition of this part. 239 * 240 * @param string $disposition The content-disposition to set (inline or 241 * attachment). 242 */ 243 function setDisposition($disposition) 244 { 245 $disposition = String::lower($disposition); 246 247 if (($disposition == 'inline') || ($disposition == 'attachment')) { 248 $this->_disposition = $disposition; 249 } 250 } 251 252 /** 253 * Get the content-disposition of this part. 254 * 255 * @return string The part's content-disposition. 256 */ 257 function getDisposition() 258 { 259 return $this->_disposition; 260 } 261 262 /** 263 * Set the name of this part. 264 * 265 * @param string $name The name to set. 266 */ 267 function setName($name) 268 { 269 $this->setContentTypeParameter('name', $name); 270 } 271 272 /** 273 * Get the name of this part. 274 * 275 * @param boolean $decode MIME decode description? 276 * @param boolean $default If the name parameter doesn't exist, should we 277 * use the default name from the description 278 * parameter? 279 * 280 * @return string The name of the part. 281 */ 282 function getName($decode = false, $default = false) 283 { 284 $name = $this->getContentTypeParameter('name'); 285 286 if ($default && empty($name)) { 287 $name = preg_replace('|\W|', '_', $this->getDescription(false, true)); 288 } 289 290 if ($decode) { 291 return trim(MIME::decode($name)); 292 } else { 293 return $name; 294 } 295 } 296 297 /** 298 * Set the body contents of this part. 299 * 300 * @param string $contents The part body. 301 * @param string $encoding The current encoding of the contents. 302 */ 303 function setContents($contents, $encoding = null) 304 { 305 $this->_contents = $contents; 306 $this->_flags['contentsSet'] = true; 307 $this->_currentEncoding = (is_null($encoding)) ? $this->getCurrentEncoding() : MIME::encoding($encoding, MIME_STRING); 308 } 309 310 /** 311 * Add to the body contents of this part. 312 * 313 * @param string $contents The contents to append to the current part 314 * body. 315 * @param string $encoding The current encoding of the contents. If not 316 * specified, will try to auto determine the 317 * encoding. 318 */ 319 function appendContents($contents, $encoding = null) 320 { 321 $this->setContents($this->_contents . $contents, $encoding); 322 } 323 324 /** 325 * Clears the body contents of this part. 326 */ 327 function clearContents() 328 { 329 $this->_contents = ''; 330 $this->_flags['contentsSet'] = false; 331 $this->_currentEncoding = null; 332 } 333 334 /** 335 * Return the body of the part. 336 * 337 * @return string The raw body of the part. 338 */ 339 function getContents() 340 { 341 return $this->_contents; 342 } 343 344 /** 345 * Returns the contents in strict RFC 822 & 2045 output - namely, all 346 * newlines end with the canonical <CR><LF> sequence. 347 * 348 * @return string The entire MIME part. 349 */ 350 function getCanonicalContents() 351 { 352 return $this->replaceEOL($this->_contents, MIME_PART_RFC_EOL); 353 } 354 355 /** 356 * Transfer encode the contents (to the transfer encoding identified via 357 * getTransferEncoding()) and set as the part's new contents. 358 */ 359 function transferEncodeContents() 360 { 361 $contents = $this->transferEncode(); 362 $this->_currentEncoding = $this->_flags['lastTransferEncode']; 363 $this->setContents($contents, $this->_currentEncoding); 364 $this->setTransferEncoding($this->_currentEncoding); 365 } 366 367 /** 368 * Transfer decode the contents and set them as the new contents. 369 */ 370 function transferDecodeContents() 371 { 372 $contents = $this->transferDecode(); 373 $this->_currentEncoding = $this->_flags['lastTransferDecode']; 374 $this->setTransferEncoding($this->_currentEncoding); 375 376 /* Don't set contents if they are empty, because this will do stuff 377 like reset the internal bytes field, even though we shouldn't do 378 that (the user has their reasons to set the bytes field to a 379 non-zero value without putting the contents into this part. */ 380 if (strlen($contents)) { 381 $this->setContents($contents, $this->_currentEncoding); 382 } 383 } 384 385 /** 386 * Set the mimetype of this part. 387 * 388 * @param string $mimetype The mimetype to set (ex.: text/plain). 389 */ 390 function setType($mimetype) 391 { 392 /* RFC 2045: Any entity with unrecognized encoding must be treated 393 as if it has a Content-Type of "application/octet-stream" 394 regardless of what the Content-Type field actually says. */ 395 if ($this->_transferEncoding == 'x-unknown') { 396 return; 397 } 398 399 /* Set the 'setType' flag. */ 400 $this->_flags['setType'] = true; 401 402 list($this->_type, $this->_subtype) = explode('/', String::lower($mimetype)); 403 if (($type = MIME::type($this->_type, MIME_STRING))) { 404 $this->_type = $type; 405 406 /* Set the boundary string for 'multipart/*' parts. */ 407 if ($type == 'multipart') { 408 if (!$this->getContentTypeParameter('boundary')) { 409 $this->setContentTypeParameter('boundary', $this->_generateBoundary()); 410 } 411 } else { 412 $this->clearContentTypeParameter('boundary'); 413 } 414 } else { 415 $this->_type = 'x-unknown'; 416 $this->clearContentTypeParameter('boundary'); 417 } 418 } 419 420 /** 421 * Get the full MIME Content-Type of this part. 422 * 423 * @param boolean $charset Append character set information to the end of 424 * the content type if this is a text/* part. 425 * 426 * @return string The mimetype of this part 427 * (ex.: text/plain; charset=us-ascii). 428 */ 429 function getType($charset = false) 430 { 431 if (!isset($this->_type) || !isset($this->_subtype)) { 432 return false; 433 } 434 $ptype = $this->getPrimaryType(); 435 $type = $ptype . '/' . $this->getSubType(); 436 if ($charset && ($ptype == 'text')) { 437 $type .= '; charset=' . $this->getCharset(); 438 } 439 return $type; 440 } 441 442 /** 443 * If the subtype of a MIME part is unrecognized by an application, the 444 * default type should be used instead (See RFC 2046). This method 445 * returns the default subtype for a particular primary MIME Type. 446 * 447 * @return string The default mimetype of this part (ex.: text/plain). 448 */ 449 function getDefaultType() 450 { 451 switch ($this->getPrimaryType()) { 452 case 'text': 453 /* RFC 2046 (4.1.4): text parts default to text/plain. */ 454 return 'text/plain'; 455 456 case 'multipart': 457 /* RFC 2046 (5.1.3): multipart parts default to multipart/mixed. */ 458 return 'multipart/mixed'; 459 460 default: 461 /* RFC 2046 (4.2, 4.3, 4.4, 4.5.3, 5.2.4): all others default to 462 application/octet-stream. */ 463 return 'application/octet-stream'; 464 } 465 } 466 467 /** 468 * Get the primary type of this part. 469 * 470 * @return string The primary MIME type of this part. 471 */ 472 function getPrimaryType() 473 { 474 return $this->_type; 475 } 476 477 /** 478 * Get the subtype of this part. 479 * 480 * @return string The MIME subtype of this part. 481 */ 482 function getSubType() 483 { 484 return $this->_subtype; 485 } 486 487 /** 488 * Set the character set of this part. 489 * 490 * @param string $charset The character set of this part. 491 */ 492 function setCharset($charset) 493 { 494 $this->setContentTypeParameter('charset', $charset); 495 } 496 497 /** 498 * Get the character set to use for of this part. Returns a charset for 499 * all types (not just 'text/*') since we use this charset to determine 500 * how to encode text in MIME headers. 501 * 502 * @return string The character set of this part. Returns null if there 503 * is no character set. 504 */ 505 function getCharset() 506 { 507 $charset = $this->getContentTypeParameter('charset'); 508 return (empty($charset)) ? null : $charset; 509 } 510 511 /** 512 * Set the description of this part. 513 * 514 * @param string $description The description of this part. 515 */ 516 function setDescription($description) 517 { 518 $this->_description = MIME::encode($description, $this->getCharset()); 519 } 520 521 /** 522 * Get the description of this part. 523 * 524 * @param boolean $decode MIME decode description? 525 * @param boolean $default If the name parameter doesn't exist, should we 526 * use the default name from the description 527 * parameter? 528 * 529 * @return string The description of this part. 530 */ 531 function getDescription($decode = false, $default = false) 532 { 533 $desc = $this->_description; 534 535 if ($default && empty($desc)) { 536 $desc = $this->getName(); 537 if (empty($desc)) { 538 $desc = MIME_DEFAULT_DESCRIPTION; 539 } 540 } 541 542 if ($decode) { 543 return MIME::decode($desc); 544 } else { 545 return $desc; 546 } 547 } 548 549 /** 550 * Set the transfer encoding to use for this part. 551 * 552 * @param string $encoding The transfer encoding to use. 553 */ 554 function setTransferEncoding($encoding) 555 { 556 if (($mime_encoding = MIME::encoding($encoding, MIME_STRING))) { 557 $this->_transferEncoding = $mime_encoding; 558 } else { 559 /* RFC 2045: Any entity with unrecognized encoding must be treated 560 as if it has a Content-Type of "application/octet-stream" 561 regardless of what the Content-Type field actually says. */ 562 $this->setType('application/octet-stream'); 563 $this->_transferEncoding = 'x-unknown'; 564 } 565 } 566 567 /** 568 * Add a MIME subpart. 569 * 570 * @param MIME_Part $mime_part Add a MIME_Part subpart to the current 571 * MIME_Part. 572 * @param string $index The index of the added MIME_Part. 573 */ 574 function addPart($mime_part, $index = null) 575 { 576 /* Add the part to the parts list. */ 577 if (is_null($index)) { 578 end($this->_parts); 579 $id = key($this->_parts) + 1; 580 $ptr = &$this->_parts; 581 } else { 582 $ptr = &$this->_partFind($index, $this->_parts, true); 583 if (($pos = strrpos($index, '.'))) { 584 $id = substr($index, $pos + 1); 585 } else { 586 $id = $index; 587 } 588 } 589 590 /* Set the MIME ID if it has not already been set. */ 591 if ($mime_part->getMIMEId() === null) { 592 $mime_part->setMIMEId($id); 593 } 594 595 /* Store the part now. */ 596 $ptr[$id] = $mime_part; 597 598 /* Clear the ID -> Part mapping cache. */ 599 $this->_idmap = array(); 600 } 601 602 /** 603 * Get a list of all MIME subparts. 604 * 605 * @return array An array of the MIME_Part subparts. 606 */ 607 function getParts() 608 { 609 return $this->_parts; 610 } 611 612 /** 613 * Retrieve a specific MIME part. 614 * 615 * @param string $id The MIME_Part ID string. 616 * 617 * @return MIME_Part The MIME_Part requested, or false if the part 618 * doesn't exist. 619 */ 620 function getPart($id) 621 { 622 $mimeid = $this->getMIMEId(); 623 624 /* This will convert '#.0' to simply '#', which is how the part is 625 * internally stored. */ 626 $search_id = $id; 627 if (($str = strrchr($id, '.')) && 628 ($str == '.0')) { 629 $search_id = substr($search_id, 0, -2); 630 } 631 632 /* Return this part if: 633 1) There is only one part (e.g. the MIME ID is 0, or the 634 MIME ID is 1 and there are no subparts. 635 2) $id matches this parts MIME ID. */ 636 if (($search_id == 0) || 637 (($search_id == 1) && !count($this->_parts)) || 638 (!empty($mimeid) && ($search_id == $mimeid))) { 639 $part = $this; 640 } else { 641 $part = $this->_partFind($id, $this->_parts); 642 } 643 644 if ($part && 645 ($search_id != $id) && 646 ($part->getType() == 'message/rfc822')) { 647 $ret_part = Util::cloneObject($part); 648 $ret_part->_parts = array(); 649 return $ret_part; 650 } 651 652 return $part; 653 } 654 655 /** 656 * Remove a MIME_Part subpart. 657 * 658 * @param string $id The MIME Part to delete. 659 */ 660 function removePart($id) 661 { 662 if (($ptr = &$this->_partFind($id, $this->_parts))) { 663 unset($ptr); 664 $this->_idmap = array(); 665 } 666 } 667 668 /** 669 * Alter a current MIME subpart. 670 * 671 * @param string $id The MIME Part ID to alter. 672 * @param MIME_Part $mime_part The MIME Part to store. 673 */ 674 function alterPart($id, $mime_part) 675 { 676 if (($ptr = &$this->_partFind($id, $this->_parts))) { 677 $ptr = $mime_part; 678 $this->_idmap = array(); 679 } 680 } 681 682 /** 683 * Function used to find a specific MIME Part by ID. 684 * 685 * @access private 686 * 687 * @param string $id The MIME_Part ID string. 688 * @param array &$parts A list of MIME_Part objects. 689 * @param boolean $retarray Return a pointer to the array that stores 690 * (would store) the part rather than the part 691 * itself? 692 */ 693 function &_partFind($id, &$parts, $retarray = false) 694 { 695 /* Pointers don't persist through sessions; therefore, we must make 696 * sure that the IdMap is destroyed at the end of each request. 697 * How can we do this? We check to see if $_idmap contains an array 698 * of MIME_Parts or an array of arrays. */ 699 $check = reset($this->_idmap); 700 if (empty($check) || !is_a($check, 'MIME_Part')) { 701 $this->_idmap = array(); 702 $this->_generateIdMap($this->_parts); 703 } 704 705 if ($retarray) { 706 if ($pos = strrpos($id, '.')) { 707 $id = substr($id, 0, $pos); 708 } else { 709 return $parts; 710 } 711 } 712 713 if (isset($this->_idmap[$id])) { 714 return $this->_idmap[$id]; 715 } else { 716 $part = false; 717 return $part; 718 } 719 } 720 721 /** 722 * Generates a mapping of MIME_Parts with their MIME IDs. 723 * 724 * @access private 725 * 726 * @param array &$parts An array of MIME_Parts to map. 727 */ 728 function _generateIdMap(&$parts) 729 { 730 if (!empty($parts)) { 731 foreach (array_keys($parts) as $key) { 732 $ptr = &$parts[$key]; 733 $this->_idmap[$ptr->getMIMEId()] = &$ptr; 734 $this->_generateIdMap($ptr->_parts); 735 } 736 } 737 } 738 739 /** 740 * Add information about the MIME_Part. 741 * 742 * @param string $label The information label. 743 * @param mixed $data The information to store. 744 */ 745 function setInformation($label, $data) 746 { 747 $this->_information[$label] = $data; 748 } 749 750 /** 751 * Retrieve information about the MIME_Part. 752 * 753 * @param string $label The information label. 754 * 755 * @return mixed The information requested. 756 * Returns false if $label is not set. 757 */ 758 function getInformation($label) 759 { 760 return (isset($this->_information[$label])) ? $this->_information[$label] : false; 761 } 762 763 /** 764 * Add a disposition parameter to this part. 765 * 766 * @param string $label The disposition parameter label. 767 * @param string $data The disposition parameter data. 768 */ 769 function setDispositionParameter($label, $data) 770 { 771 $this->_dispositionParameters[$label] = $data; 772 } 773 774 /** 775 * Get a disposition parameter from this part. 776 * 777 * @param string $label The disposition parameter label. 778 * 779 * @return string The data requested. 780 * Returns false if $label is not set. 781 */ 782 function getDispositionParameter($label) 783 { 784 return (isset($this->_dispositionParameters[$label])) ? $this->_dispositionParameters[$label] : false; 785 } 786 787 /** 788 * Get all parameters from the Content-Disposition header. 789 * 790 * @return array An array of all the parameters 791 * Returns the empty array if no parameters set. 792 */ 793 function getAllDispositionParameters() 794 { 795 return $this->_dispositionParameters; 796 } 797 798 /** 799 * Add a content type parameter to this part. 800 * 801 * @param string $label The disposition parameter label. 802 * @param string $data The disposition parameter data. 803 */ 804 function setContentTypeParameter($label, $data) 805 { 806 $this->_contentTypeParameters[$label] = $data; 807 } 808 809 /** 810 * Clears a content type parameter from this part. 811 * 812 * @param string $label The disposition parameter label. 813 * @param string $data The disposition parameter data. 814 */ 815 function clearContentTypeParameter($label) 816 { 817 unset($this->_contentTypeParameters[$label]); 818 } 819 820 /** 821 * Get a content type parameter from this part. 822 * 823 * @param string $label The content type parameter label. 824 * 825 * @return string The data requested. 826 * Returns false if $label is not set. 827 */ 828 function getContentTypeParameter($label) 829 { 830 return (isset($this->_contentTypeParameters[$label])) ? $this->_contentTypeParameters[$label] : false; 831 } 832 833 /** 834 * Get all parameters from the Content-Type header. 835 * 836 * @return array An array of all the parameters 837 * Returns the empty array if no parameters set. 838 */ 839 function getAllContentTypeParameters() 840 { 841 return $this->_contentTypeParameters; 842 } 843 844 /** 845 * Sets a new string to use for EOLs. 846 * 847 * @param string $eol The string to use for EOLs. 848 */ 849 function setEOL($eol) 850 { 851 $this->_eol = $eol; 852 } 853 854 /** 855 * Get the string to use for EOLs. 856 * 857 * @return string The string to use for EOLs. 858 */ 859 function getEOL() 860 { 861 return $this->_eol; 862 } 863 864 /** 865 * Add the appropriate MIME headers for this part to an existing array. 866 * 867 * @param array $headers An array of any other headers for the part. 868 * 869 * @return array The headers, with the MIME headers added. 870 */ 871 function header($headers = array()) 872 { 873 $eol = $this->getEOL(); 874 $ptype = $this->getPrimaryType(); 875 $stype = $this->getSubType(); 876 877 /* Get the character set for this part. */ 878 $charset = $this->getCharset(); 879 880 /* Get the Content-Type - this is ALWAYS required. */ 881 $ctype = $this->getType(true); 882 foreach ($this->getAllContentTypeParameters() as $key => $value) { 883 /* Skip the charset key since that would have already been 884 * added to $ctype by getType(). */ 885 if ($key == 'charset') { 886 continue; 887 } 888 $ctype .= '; ' . $key . '="' . str_replace('"', '\"', MIME::encode($value, $charset)) . '"'; 889 } 890 $headers['Content-Type'] = MIME::wrapHeaders('Content-Type', $ctype, $eol); 891 892 /* Get the description, if any. */ 893 if (($descrip = $this->getDescription())) { 894 $headers['Content-Description'] = MIME::wrapHeaders('Content-Description', MIME::encode($descrip, $charset), $eol); 895 } 896 897 /* RFC 2045 [4] - message/rfc822 and message/partial require the 898 MIME-Version header only if they themselves claim to be MIME 899 compliant. */ 900 if (($ptype == 'message') && 901 (($stype == 'rfc822') || ($stype == 'partial')) && 902 (strpos($this->_contents, 'MIME-Version: 1.0') !== false)) { 903 $headers['MIME-Version'] = '1.0'; 904 } 905 906 /* message/* parts require no additional header information. */ 907 if ($ptype == 'message') { 908 return $headers; 909 } 910 911 /* Don't show Content-Disposition for multipart messages unless 912 there is a name parameter. */ 913 $name = $this->getName(); 914 if (($ptype != 'multipart') || !empty($name)) { 915 $disp = $this->getDisposition(); 916 917 /* Add any disposition parameter information, if available. */ 918 if (!empty($name)) { 919 $disp .= '; ' . 'filename="' . MIME::encode($name, $charset) . '"'; 920 } 921 922 $headers['Content-Disposition'] = MIME::wrapHeaders('Content-Disposition', $disp, $eol); 923 } 924 925 /* Add transfer encoding information. */ 926 $headers['Content-Transfer-Encoding'] = $this->getTransferEncoding(); 927 928 /* Add content ID information. */ 929 if (!is_null($this->_contentid)) { 930 $headers['Content-ID'] = $this->_contentid; 931 } 932 933 return $headers; 934 } 935 936 /** 937 * Return the entire part in MIME format. Includes headers on request. 938 * 939 * @param boolean $headers Include the MIME headers? 940 * 941 * @return string The MIME string. 942 */ 943 function toString($headers = true) 944 { 945 $eol = $this->getEOL(); 946 $ptype = $this->getPrimaryType(); 947 948 if ($headers) { 949 $text = ''; 950 foreach ($this->header() as $key => $val) { 951 $text .= $key . ': ' . $val . $eol; 952 } 953 $text .= $eol; 954 } 955 956 /* Any information about a message/* is embedded in the message 957 contents themself. Simply output the contents of the part 958 directly and return. */ 959 if ($ptype == 'message') { 960 if (isset($text)) { 961 return $text . $this->_contents; 962 } else { 963 return $this->_contents; 964 } 965 } 966 967 if (isset($text)) { 968 $text .= $this->transferEncode(); 969 } else { 970 $text = $this->transferEncode(); 971 } 972 973 /* Deal with multipart messages. */ 974 if ($ptype == 'multipart') { 975 $boundary = trim($this->getContentTypeParameter('boundary'), '"'); 976 if (!strlen($this->_contents)) { 977 $text .= 'This message is in MIME format.' . $eol; 978 } 979 foreach ($this->getParts() as $part) { 980 $text .= $eol . '--' . $boundary . $eol; 981 $oldEOL = $part->getEOL(); 982 $part->setEOL($eol); 983 $text .= $part->toString(true); 984 $part->setEOL($oldEOL); 985 } 986 $text .= $eol . '--' . $boundary . '--' . $eol; 987 } 988 989 return $text; 990 } 991 992 /** 993 * Returns the encoded part in strict RFC 822 & 2045 output - namely, all 994 * newlines end with the canonical <CR><LF> sequence. 995 * 996 * @param boolean $headers Include the MIME headers? 997 * 998 * @return string The entire MIME part. 999 */ 1000 function toCanonicalString($headers = true) 1001 { 1002 $string = $this->toString($headers); 1003 return $this->replaceEOL($string, MIME_PART_RFC_EOL); 1004 } 1005 1006 /** 1007 * Should we make sure the message is encoded via 7-bit (e.g. to adhere 1008 * to mail delivery standards such as RFC 2821)? 1009 * 1010 * @param boolean $use7bit Use 7-bit encoding? 1011 */ 1012 function strict7bit($use7bit) 1013 { 1014 $this->_encode7bit = $use7bit; 1015 } 1016 1017 /** 1018 * Get the transfer encoding for the part based on the user requested 1019 * transfer encoding and the current contents of the part. 1020 * 1021 * @return string The transfer-encoding of this part. 1022 */ 1023 function getTransferEncoding() 1024 { 1025 $encoding = $this->_transferEncoding; 1026 $ptype = $this->getPrimaryType(); 1027 $text = str_replace($this->getEOL(), ' ', $this->_contents); 1028 1029 /* If there are no contents, return whatever the current value of 1030 $_transferEncoding is. */ 1031 if (empty($text)) { 1032 return $encoding; 1033 } 1034 1035 switch ($ptype) { 1036 case 'message': 1037 /* RFC 2046 [5.2.1] - message/rfc822 messages only allow 7bit, 1038 8bit, and binary encodings. If the current encoding is either 1039 base64 or q-p, switch it to 8bit instead. 1040 RFC 2046 [5.2.2, 5.2.3, 5.2.4] - All other message/* messages 1041 only allow 7bit encodings. */ 1042 $encoding = ($this->getSubType() == 'rfc822') ? '8bit' : '7bit'; 1043 break; 1044 1045 case 'text': 1046 if (MIME::is8bit($text)) { 1047 $encoding = ($this->_encode7bit) ? 'quoted-printable' : '8bit'; 1048 } elseif (preg_match("/(?:\n|^)[^\n]{999,}(?:\n|$)/", $text)) { 1049 /* If the text is longer than 998 characters between 1050 * linebreaks, use quoted-printable encoding to ensure the 1051 * text will not be chopped (i.e. by sendmail if being sent 1052 * as mail text). */ 1053 $encoding = 'quoted-printable'; 1054 } 1055 break; 1056 1057 default: 1058 if (MIME::is8bit($text)) { 1059 $encoding = ($this->_encode7bit) ? 'base64' : '8bit'; 1060 } 1061 break; 1062 } 1063 1064 /* Need to do one last check for binary data if encoding is 7bit or 1065 * 8bit. If the message contains a NULL character at all, the message 1066 * MUST be in binary format. RFC 2046 [2.7, 2.8, 2.9]. Q-P and base64 1067 * can handle binary data fine so no need to switch those encodings. */ 1068 if ((($encoding == '8bit') || ($encoding == '7bit')) && 1069 preg_match('/\x00/', $text)) { 1070 $encoding = ($this->_encode7bit) ? 'base64' : 'binary'; 1071 } 1072 1073 return $encoding; 1074 } 1075 1076 /** 1077 * Retrieves the current encoding of the contents in the object. 1078 * 1079 * @return string The current encoding. 1080 */ 1081 function getCurrentEncoding() 1082 { 1083 return (is_null($this->_currentEncoding)) ? $this->_transferEncoding : $this->_currentEncoding; 1084 } 1085 1086 /** 1087 * Encodes the contents with the part's transfer encoding. 1088 * 1089 * @return string The encoded text. 1090 */ 1091 function transferEncode() 1092 { 1093 $encoding = $this->getTransferEncoding(); 1094 $eol = $this->getEOL(); 1095 1096 /* Set the 'lastTransferEncode' flag so that transferEncodeContents() 1097 can save a call to getTransferEncoding(). */ 1098 $this->_flags['lastTransferEncode'] = $encoding; 1099 1100 /* If contents are empty, or contents are already encoded to the 1101 correct encoding, return now. */ 1102 if (!strlen($this->_contents) || 1103 ($encoding == $this->_currentEncoding)) { 1104 return $this->_contents; 1105 } 1106 1107 switch ($encoding) { 1108 /* Base64 Encoding: See RFC 2045, section 6.8 */ 1109 case 'base64': 1110 /* Keeping these two lines separate seems to use much less 1111 memory than combining them (as of PHP 4.3). */ 1112 $encoded_contents = base64_encode($this->_contents); 1113 return chunk_split($encoded_contents, 76, $eol); 1114 1115 /* Quoted-Printable Encoding: See RFC 2045, section 6.7 */ 1116 case 'quoted-printable': 1117 $output = MIME::quotedPrintableEncode($this->_contents, $eol); 1118 if (($eollength = String::length($eol))) { 1119 return substr($output, 0, $eollength * -1); 1120 } else { 1121 return $output; 1122 } 1123 1124 default: 1125 return $this->replaceEOL($this->_contents); 1126 } 1127 } 1128 1129 /** 1130 * Decodes the contents of the part to either a 7bit or 8bit encoding. 1131 * 1132 * @return string The decoded text. 1133 * Returns the empty string if there is no text to decode. 1134 */ 1135 function transferDecode() 1136 { 1137 $encoding = $this->getCurrentEncoding(); 1138 1139 /* If the contents are empty, return now. */ 1140 if (!strlen($this->_contents)) { 1141 $this->_flags['lastTransferDecode'] = $encoding; 1142 return $this->_contents; 1143 } 1144 1145 switch ($encoding) { 1146 case 'base64': 1147 $message = base64_decode($this->_contents); 1148 $this->_flags['lastTransferDecode'] = '8bit'; 1149 break; 1150 1151 case 'quoted-printable': 1152 $message = preg_replace("/=\r?\n/", '', $this->_contents); 1153 $message = $this->replaceEOL($message); 1154 $message = quoted_printable_decode($message); 1155 $this->_flags['lastTransferDecode'] = (MIME::is8bit($message)) ? '8bit' : '7bit'; 1156 break; 1157 1158 /* Support for uuencoded encoding - although not required by RFCs, 1159 some mailers may still encode this way. */ 1160 case 'uuencode': 1161 case 'x-uuencode': 1162 case 'x-uue': 1163 if (function_exists('convert_uudecode')) { 1164 $message = convert_uuencode($this->_contents); 1165 } else { 1166 require_once 'Mail/mimeDecode.php'; 1167 $files = &Mail_mimeDecode::uudecode($this->_contents); 1168 $message = $files[0]['filedata']; 1169 } 1170 require_once 'Mail/mimeDecode.php'; 1171 $files = &Mail_mimeDecode::uudecode($this->_contents); 1172 $message = $files[0]['filedata']; 1173 $this->_flags['lastTransferDecode'] = '8bit'; 1174 break; 1175 1176 default: 1177 if (isset($this->_flags['lastTransferDecode']) && 1178 ($this->_flags['lastTransferDecode'] != $encoding)) { 1179 $message = $this->replaceEOL($this->_contents); 1180 } else { 1181 $message = $this->_contents; 1182 } 1183 $this->_flags['lastTransferDecode'] = $encoding; 1184 break; 1185 } 1186 1187 return $message; 1188 } 1189 1190 /** 1191 * Split the contents of the current Part into its respective subparts, 1192 * if it is multipart MIME encoding. Unlike the imap_*() functions, this 1193 * will preserve all MIME header information. 1194 * 1195 * The boundary content-type parameter must be set for this function to 1196 * work correctly. 1197 * 1198 * @return boolean True if the contents were successfully split. 1199 * False if any error occurred. 1200 */ 1201 function splitContents() 1202 { 1203 if (!($boundary = $this->getContentTypeParameter('boundary'))) { 1204 return false; 1205 } 1206 1207 if (!strlen($this->_contents)) { 1208 return false; 1209 } 1210 1211 $eol = $this->getEOL(); 1212 $retvalue = false; 1213 1214 foreach (explode($eol, $this->_contents) as $line) { 1215 $pos = strpos($line, '--' . $boundary); 1216 if (($pos === false) && isset($part_ptr)) { 1217 $data[] = $line; 1218 } elseif ($pos === 0) { 1219 $retvalue = true; 1220 if (isset($part_ptr)) { 1221 $this->_parts[$part_ptr]->setContents(implode($eol, $data)); 1222 $this->_parts[$part_ptr]->splitContents(); 1223 next($this->_parts); 1224 } else { 1225 reset($this->_parts); 1226 } 1227 if (isset($data)) { 1228 unset($data); 1229 } 1230 $data = array(); 1231 $part_ptr = key($this->_parts); 1232 } 1233 } 1234 1235 return $retvalue; 1236 } 1237 1238 /** 1239 * Replace newlines in this part's contents with those specified by either 1240 * the given newline sequence or the part's current EOL setting. 1241 * 1242 * @param string $text The text to replace. 1243 * @param string $eol The EOL sequence to use. If not present, uses the 1244 * part's current EOL setting. 1245 * 1246 * @return string The text with the newlines replaced by the desired 1247 * newline sequence. 1248 */ 1249 function replaceEOL($text, $eol = null) 1250 { 1251 if (is_null($eol)) { 1252 $eol = $this->getEOL(); 1253 } 1254 return preg_replace("/\r?\n/", $eol, $text); 1255 } 1256 1257 /** 1258 * Return the unique MIME_Part boundary string generated for this object. 1259 * This may not be the boundary string used when building the message 1260 * since a user defined 'boundary' Content-Type parameter will override 1261 * this value. 1262 * 1263 * @return string The unique boundary string. 1264 */ 1265 function getUniqueID() 1266 { 1267 return $this->_boundary; 1268 } 1269 1270 /** 1271 * Determine the size of a MIME_Part and its child members. 1272 * 1273 * @return integer Size of the MIME_Part, in bytes. 1274 */ 1275 function getBytes() 1276 { 1277 $bytes = 0; 1278 1279 if (empty($this->_flags['contentsSet']) && $this->_bytes) { 1280 $bytes = $this->_bytes; 1281 } elseif ($this->getPrimaryType() == 'multipart') { 1282 foreach ($this->getParts() as $part) { 1283 /* Skip multipart entries (since this may result in double 1284 counting). */ 1285 if ($part->getPrimaryType() != 'multipart') { 1286 $bytes += $part->getBytes(); 1287 } 1288 } 1289 } else { 1290 if ($this->getPrimaryType() == 'text') { 1291 $bytes = String::length($this->_contents, $this->getCharset()); 1292 } else { 1293 $bytes = strlen($this->_contents); 1294 } 1295 } 1296 1297 return $bytes; 1298 } 1299 1300 /** 1301 * Explicitly set the size (in bytes) of this part. This value will only 1302 * be returned (via getBytes()) if there are no contents currently set. 1303 * This function is useful for setting the size of the part when the 1304 * contents of the part are not fully loaded (i.e. creating a MIME_Part 1305 * object from IMAP header information without loading the data of the 1306 * part). 1307 * 1308 * @param integer $bytes The size of this part in bytes. 1309 */ 1310 function setBytes($bytes) 1311 { 1312 $this->_bytes = $bytes; 1313 } 1314 1315 /** 1316 * Output the size of this MIME_Part in KB. 1317 * 1318 * @return string Size of the MIME_Part, in string format. 1319 */ 1320 function getSize() 1321 { 1322 $bytes = $this->getBytes(); 1323 if (empty($bytes)) { 1324 return $bytes; 1325 } 1326 1327 $localeinfo = NLS::getLocaleInfo(); 1328 return number_format($bytes / 1024, 2, $localeinfo['decimal_point'], $localeinfo['thousands_sep']); 1329 } 1330 1331 /** 1332 * Add to the list of CIDs for this part. 1333 * 1334 * @param array $cids A list of MIME IDs of the part. 1335 * Key - MIME ID 1336 * Value - CID for the part 1337 */ 1338 function addCID($cids = array()) 1339 { 1340 $this->_cids += $cids; 1341 } 1342 1343 /** 1344 * Returns the list of CIDs for this part. 1345 * 1346 * @return array The list of CIDs for this part. 1347 */ 1348 function getCIDList() 1349 { 1350 asort($this->_cids, SORT_STRING); 1351 return $this->_cids; 1352 } 1353 1354 /** 1355 * Sets the Content-ID header for this part. 1356 * 1357 * @param string $cid Use this CID (if not already set). Else, generate a 1358 * random CID. 1359 */ 1360 function setContentID($cid = null) 1361 { 1362 if (is_null($this->_contentid)) { 1363 $this->_contentid = (is_null($cid)) ? (base_convert(microtime(), 10, 36) . '@' . $_SERVER['SERVER_NAME']) : $cid; 1364 } 1365 return $this->_contentid; 1366 } 1367 1368 /** 1369 * Returns the Content-ID for this part. 1370 * 1371 * @return string The Content-ID for this part. 1372 */ 1373 function getContentID() 1374 { 1375 return $this->_contentid; 1376 } 1377 1378 /** 1379 * Alter the MIME ID of this part. 1380 * 1381 * @param string $mimeid The MIME ID. 1382 */ 1383 function setMIMEId($mimeid) 1384 { 1385 $this->_mimeid = $mimeid; 1386 } 1387 1388 /** 1389 * Returns the MIME ID of this part. 1390 * 1391 * @return string The MIME ID. 1392 */ 1393 function getMIMEId() 1394 { 1395 return $this->_mimeid; 1396 } 1397 1398 /** 1399 * Returns the relative MIME ID of this part. 1400 * e.g., if the base part has MIME ID of 2, and you want the first 1401 * subpart of the base part, the relative MIME ID is 2.1. 1402 * 1403 * @param string $id The relative part ID. 1404 * 1405 * @return string The relative MIME ID. 1406 */ 1407 function getRelativeMIMEId($id) 1408 { 1409 $rel = $this->getMIMEId(); 1410 return (empty($rel)) ? $id : $rel . '.' . $id; 1411 } 1412 1413 /** 1414 * Returns a mapping of all MIME IDs to their content-types. 1415 * 1416 * @return array KEY: MIME ID, VALUE: Content type 1417 */ 1418 function contentTypeMap() 1419 { 1420 $map = array($this->getMIMEId() => $this->getType()); 1421 foreach ($this->_parts as $val) { 1422 $map += $val->contentTypeMap(); 1423 } 1424 return $map; 1425 } 1426 1427 /** 1428 * Generate the unique boundary string (if not already done). 1429 * 1430 * @access private 1431 * 1432 * @return string The boundary string. 1433 */ 1434 function _generateBoundary() 1435 { 1436 if (is_null($this->_boundary)) { 1437 $this->_boundary = '=_' . base_convert(microtime(), 10, 36); 1438 } 1439 return $this->_boundary; 1440 } 1441 1442 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |