Changeset 0917356 in github
- Timestamp:
- Jan 20, 2010 4:26:47 AM (3 years ago)
- Branches:
- master, HEAD, courier-fix, dev-browser-capabilities, pdo, release-0.6, release-0.7, release-0.8
- Children:
- ae9d583
- Parents:
- a7dba854
- Files:
-
- 4 edited
-
CHANGELOG (modified) (1 diff)
-
program/include/rcube_mail_mime.php (modified) (1 diff)
-
program/lib/Mail/mime.php (modified) (42 diffs)
-
program/lib/Mail/mimePart.php (modified) (18 diffs)
Legend:
- Unmodified
- Added
- Removed
-
CHANGELOG
rd6584f7 r0917356 2 2 =========================== 3 3 4 - Fix incompatybility with suhosin.executor.disable_emodifier (#1486321) 4 5 - Use PLAIN auth when CRAM fails and imap_auth_type='check' (#1486371) 5 6 - Fix removal of <title> tag from HTML messages (#1486432) -
program/include/rcube_mail_mime.php
r638fb8a r0917356 96 96 97 97 /** 98 * Creates a new mimePart object, using multipart/mixed as99 * the initial content-type and returns it during the100 * build process.101 *102 * @return object The multipart/mixed mimePart object103 * @access private104 */105 function &_addMixedPart()106 {107 $params['content_type'] = $this->_headers['Content-Type'] ? $this->_headers['Content-Type'] : 'multipart/mixed';108 $ret = new Mail_mimePart('', $params);109 return $ret;110 }111 112 113 /**114 98 * Encodes a header as per RFC2047 115 99 * -
program/lib/Mail/mime.php
rf7f9346 r0917356 46 46 * @author Cipriano Groenendal <cipri@php.net> 47 47 * @author Sean Coates <sean@php.net> 48 * @author Aleksander Machniak <alec@php.net> 48 49 * @copyright 2003-2006 PEAR <pear-group@php.net> 49 50 * @license http://www.opensource.org/licenses/bsd-license.php BSD License … … 143 144 144 145 /** 146 * Headers for the mail 147 * 148 * @var array 149 * @access private 150 */ 151 var $_headers = array(); 152 153 /** 145 154 * Build parameters 146 155 * … … 148 157 * @access private 149 158 */ 150 var $_build_params = array(); 151 152 /** 153 * Headers for the mail 154 * 155 * @var array 156 * @access private 157 */ 158 var $_headers = array(); 159 160 /** 161 * End Of Line sequence (for serialize) 162 * 163 * @var string 164 * @access private 165 */ 166 var $_eol; 167 168 169 /** 170 * Constructor function. 171 * 172 * @param string $crlf what type of linebreak to use. 173 * Defaults to "\r\n" 159 var $_build_params = array( 160 // What encoding to use for the headers 161 // Options: quoted-printable or base64 162 'head_encoding' => 'quoted-printable', 163 // What encoding to use for plain text 164 // Options: 7bit, 8bit, base64, or quoted-printable 165 'text_encoding' => 'quoted-printable', 166 // What encoding to use for html 167 // Options: 7bit, 8bit, base64, or quoted-printable 168 'html_encoding' => 'quoted-printable', 169 // The character set to use for html 170 'html_charset' => 'ISO-8859-1', 171 // The character set to use for text 172 'text_charset' => 'ISO-8859-1', 173 // The character set to use for headers 174 'head_charset' => 'ISO-8859-1', 175 // End-of-line sequence 176 'eol' => "\r\n", 177 // Delay attachment files IO until building the message 178 'delay_file_io' => false 179 ); 180 181 /** 182 * Constructor function 183 * 184 * @param mixed $params Build parameters that change the way the email 185 * is built. Should be an associative array. 186 * See $_build_params. 174 187 * 175 188 * @return void 176 * 177 * @access public 178 */ 179 function Mail_mime($crlf = "\r\n") 180 { 181 $this->_setEOL($crlf); 182 $this->_build_params = array( 183 'head_encoding' => 'quoted-printable', 184 'text_encoding' => '7bit', 185 'html_encoding' => 'quoted-printable', 186 '7bit_wrap' => 998, 187 'html_charset' => 'ISO-8859-1', 188 'text_charset' => 'ISO-8859-1', 189 'head_charset' => 'ISO-8859-1' 190 ); 191 } 192 193 /** 194 * wakeup function called by unserialize. It re-sets the EOL constant 195 * 196 * @access private 189 * @access public 190 */ 191 function Mail_mime($params = array()) 192 { 193 // Backward-compatible EOL setting 194 if (is_string($params)) { 195 $this->_build_params['eol'] = $params; 196 } else if (defined('MAIL_MIME_CRLF') && !isset($params['eol'])) { 197 $this->_build_params['eol'] = MAIL_MIME_CRLF; 198 } 199 200 // Update build parameters 201 if (!empty($params) && is_array($params)) { 202 while (list($key, $value) = each($params)) { 203 $this->_build_params[$key] = $value; 204 } 205 } 206 } 207 208 /** 209 * Set build parameter value 210 * 211 * @param string $name Parameter name 212 * @param string $value Parameter value 213 * 197 214 * @return void 198 */ 199 function __wakeup() 200 { 201 $this->_setEOL($this->_eol); 202 } 203 215 * @access public 216 * @since 1.6.0 217 */ 218 function setParam($name, $value) 219 { 220 $this->_build_params[$name] = $value; 221 } 222 223 /** 224 * Get build parameter value 225 * 226 * @param string $name Parameter name 227 * 228 * @return mixed Parameter value 229 * @access public 230 * @since 1.6.0 231 */ 232 function getParam($name) 233 { 234 return isset($this->_build_params[$name]) ? $this->_build_params[$name] : null; 235 } 204 236 205 237 /** … … 210 242 * 211 243 * @param string $data Either a string or 212 * the file name with the contents244 * the file name with the contents 213 245 * @param bool $isfile If true the first param should be treated 214 * as a file name, else as a string (default)246 * as a file name, else as a string (default) 215 247 * @param bool $append If true the text or file is appended to 216 * the existing body, else the old body is217 * overwritten218 * 219 * @return mixed true on success or PEAR_Error object248 * the existing body, else the old body is 249 * overwritten 250 * 251 * @return mixed True on success or PEAR_Error object 220 252 * @access public 221 253 */ … … 243 275 244 276 /** 277 * Get message text body 278 * 279 * @return string Text body 280 * @access public 281 * @since 1.6.0 282 */ 283 function getTXTBody() 284 { 285 return $this->_txtbody; 286 } 287 288 /** 245 289 * Adds a html part to the mail. 246 290 * 247 * @param string $data either a string or the file name with the248 * contents249 * @param bool $isfile aflag that determines whether $data is a250 * filename, or a string(false, default)251 * 252 * @return bool true on success291 * @param string $data Either a string or the file name with the 292 * contents 293 * @param bool $isfile A flag that determines whether $data is a 294 * filename, or a string(false, default) 295 * 296 * @return bool True on success 253 297 * @access public 254 298 */ … … 269 313 270 314 /** 315 * Get message HTML body 316 * 317 * @return string HTML body 318 * @access public 319 * @since 1.6.0 320 */ 321 function getHTMLBody() 322 { 323 return $this->_htmlbody; 324 } 325 326 /** 271 327 * Adds an image to the list of embedded images. 272 328 * 273 * @param string $file the image file name OR image data itself 274 * @param string $c_type the content type 275 * @param string $name the filename of the image. 276 * Only used if $file is the image data. 277 * @param bool $isfile whether $file is a filename or not. 278 * Defaults to true 279 * 280 * @return bool true on success 281 * @access public 282 */ 283 function addHTMLImage($file, $c_type='application/octet-stream', 284 $name = '', $isfile = true) 285 { 286 $filedata = ($isfile === true) ? $this->_file2str($file) 287 : $file; 288 if ($isfile === true) { 289 $filename = ($name == '' ? $file : $name); 290 } else { 329 * @param string $file The image file name OR image data itself 330 * @param string $c_type The content type 331 * @param string $name The filename of the image. 332 * Only used if $file is the image data. 333 * @param bool $isfile Whether $file is a filename or not. 334 * Defaults to true 335 * @param string $content_id Desired Content-ID of MIME part 336 * Defaults to generated unique ID 337 * 338 * @return bool True on success 339 * @access public 340 */ 341 function addHTMLImage($file, 342 $c_type='application/octet-stream', 343 $name = '', 344 $isfile = true, 345 $content_id = null 346 ) { 347 $bodyfile = null; 348 349 if ($isfile) { 350 // Don't load file into memory 351 if ($this->_build_params['delay_file_io']) { 352 $filedata = null; 353 $bodyfile = $file; 354 } else { 355 if (PEAR::isError($filedata = $this->_file2str($file))) { 356 return $filedata; 357 } 358 } 359 $filename = ($name ? $name : $file); 360 } else { 361 $filedata = $file; 291 362 $filename = $name; 292 363 } 293 if (PEAR::isError($filedata)) { 294 return $filedata; 295 } 364 365 if (!$content_id) { 366 $content_id = md5(uniqid(time())); 367 } 368 296 369 $this->_html_images[] = array( 297 'body' => $filedata, 298 'name' => $filename, 299 'c_type' => $c_type, 300 'cid' => md5(uniqid(time())) 301 ); 370 'body' => $filedata, 371 'body_file' => $bodyfile, 372 'name' => $filename, 373 'c_type' => $c_type, 374 'cid' => $content_id 375 ); 376 302 377 return true; 303 378 } … … 307 382 * 308 383 * @param string $file The file name of the file to attach 309 * OR the file contents itself384 * OR the file contents itself 310 385 * @param string $c_type The content type 311 386 * @param string $name The filename of the attachment 312 * Only use if $file is the contents387 * Only use if $file is the contents 313 388 * @param bool $isfile Whether $file is a filename or not 314 * Defaults to true389 * Defaults to true 315 390 * @param string $encoding The type of encoding to use. 316 * Defaults to base64.317 * Possible values: 7bit, 8bit, base64,318 * or quoted-printable.391 * Defaults to base64. 392 * Possible values: 7bit, 8bit, base64, 393 * or quoted-printable. 319 394 * @param string $disposition The content-disposition of this file 320 * Defaults to attachment.321 * Possible values: attachment, inline.395 * Defaults to attachment. 396 * Possible values: attachment, inline. 322 397 * @param string $charset The character set used in the filename 323 * of this attachment.398 * of this attachment. 324 399 * @param string $language The language of the attachment 325 400 * @param string $location The RFC 2557.4 location of the attachment 326 * @param string $n_encoding Use RFC 2047 for attachment name (Content-Type) encoding 327 * @param string $f_encoding Use RFC 2047 for attachment filename (Content-Disposition) encoding 328 * 329 * @return mixed true on success or PEAR_Error object 401 * @param string $n_encoding Encoding for attachment name (Content-Type) 402 * By default filenames are encoded using RFC2231 method 403 * Here you can set RFC2047 encoding (quoted-printable 404 * or base64) instead 405 * @param string $f_encoding Encoding for attachment filename (Content-Disposition) 406 * See $n_encoding description 407 * 408 * @return mixed True on success or PEAR_Error object 330 409 * @access public 331 410 */ 332 411 function addAttachment($file, 333 $c_type = 'application/octet-stream', 334 $name = '', 335 $isfile = true, 336 $encoding = 'base64', 337 $disposition = 'attachment', 338 $charset = '', 339 $language = '', 340 $location = '', 341 $n_encoding = NULL, 342 $f_encoding = NULL) 343 { 344 $filedata = ($isfile === true) ? $this->_file2str($file) : $file; 345 346 if (PEAR::isError($filedata)) { 347 return $filedata; 348 } 349 350 if ($isfile === true) { 412 $c_type = 'application/octet-stream', 413 $name = '', 414 $isfile = true, 415 $encoding = 'base64', 416 $disposition = 'attachment', 417 $charset = '', 418 $language = '', 419 $location = '', 420 $n_encoding = null, 421 $f_encoding = null 422 ) { 423 $bodyfile = null; 424 425 if ($isfile) { 426 // Don't load file into memory 427 if ($this->_build_params['delay_file_io']) { 428 $filedata = null; 429 $bodyfile = $file; 430 } else { 431 if (PEAR::isError($filedata = $this->_file2str($file))) { 432 return $filedata; 433 } 434 } 351 435 // Force the name the user supplied, otherwise use $file 352 $filename = (strlen($name)) ? $name : $file; 353 } else { 436 $filename = ($name ? $name : $file); 437 } else { 438 $filedata = $file; 354 439 $filename = $name; 355 440 } 441 356 442 if (!strlen($filename)) { 357 443 $msg = "The supplied filename for the attachment can't be empty"; … … 362 448 363 449 $this->_parts[] = array( 364 'body' => $filedata, 365 'name' => $filename, 366 'c_type' => $c_type, 367 'encoding' => $encoding, 368 'charset' => $charset, 369 'language' => $language, 370 'location' => $location, 371 'disposition' => $disposition, 372 'name-encoding' => $n_encoding, 373 'filename-encoding' => $f_encoding 374 ); 450 'body' => $filedata, 451 'body_file' => $bodyfile, 452 'name' => $filename, 453 'c_type' => $c_type, 454 'encoding' => $encoding, 455 'charset' => $charset, 456 'language' => $language, 457 'location' => $location, 458 'disposition' => $disposition, 459 'name_encoding' => $n_encoding, 460 'filename_encoding' => $f_encoding 461 ); 462 375 463 return true; 376 464 } … … 379 467 * Get the contents of the given file name as string 380 468 * 381 * @param string $file_name path of file to process382 * 383 * @return string contents of $file_name469 * @param string $file_name Path of file to process 470 * 471 * @return string Contents of $file_name 384 472 * @access private 385 473 */ 386 474 function &_file2str($file_name) 387 475 { 388 // Check state of file and raise an error properly476 // Check state of file and raise an error properly 389 477 if (!file_exists($file_name)) { 390 478 $err = PEAR::raiseError('File not found: ' . $file_name); … … 400 488 } 401 489 402 // Temporarily reset magic_quotes_runtime and read file contents490 // Temporarily reset magic_quotes_runtime and read file contents 403 491 if ($magic_quote_setting = get_magic_quotes_runtime()) { 404 set_magic_quotes_runtime(0);492 @ini_set('magic_quotes_runtime', 0); 405 493 } 406 494 $cont = file_get_contents($file_name); 407 495 if ($magic_quote_setting) { 408 set_magic_quotes_runtime($magic_quote_setting);496 @ini_set('magic_quotes_runtime', $magic_quote_setting); 409 497 } 410 498 … … 417 505 * 418 506 * @param mixed &$obj The object to add the part to, or 419 * null if a new object is to be created.507 * null if a new object is to be created. 420 508 * @param string $text The text to add. 421 509 * 422 * @return object The text mimePart object510 * @return object The text mimePart object 423 511 * @access private 424 512 */ … … 428 516 $params['encoding'] = $this->_build_params['text_encoding']; 429 517 $params['charset'] = $this->_build_params['text_charset']; 518 $params['eol'] = $this->_build_params['eol']; 519 430 520 if (is_object($obj)) { 431 521 $ret = $obj->addSubpart($text, $params); … … 442 532 * 443 533 * @param mixed &$obj The object to add the part to, or 444 * null if a new object is to be created.445 * 446 * @return object The html mimePart object534 * null if a new object is to be created. 535 * 536 * @return object The html mimePart object 447 537 * @access private 448 538 */ … … 452 542 $params['encoding'] = $this->_build_params['html_encoding']; 453 543 $params['charset'] = $this->_build_params['html_charset']; 544 $params['eol'] = $this->_build_params['eol']; 545 454 546 if (is_object($obj)) { 455 547 $ret = $obj->addSubpart($this->_htmlbody, $params); … … 473 565 $params = array(); 474 566 $params['content_type'] = 'multipart/mixed'; 475 476 //Create empty multipart/mixed Mail_mimePart object to return 567 $params['eol'] = $this->_build_params['eol']; 568 569 // Create empty multipart/mixed Mail_mimePart object to return 477 570 $ret = new Mail_mimePart('', $params); 478 571 return $ret; … … 485 578 * 486 579 * @param mixed &$obj The object to add the part to, or 487 * null if a new object is to be created.488 * 489 * @return object The multipart/mixed mimePart object580 * null if a new object is to be created. 581 * 582 * @return object The multipart/mixed mimePart object 490 583 * @access private 491 584 */ … … 493 586 { 494 587 $params['content_type'] = 'multipart/alternative'; 588 $params['eol'] = $this->_build_params['eol']; 589 495 590 if (is_object($obj)) { 496 591 return $obj->addSubpart('', $params); … … 507 602 * 508 603 * @param mixed &$obj The object to add the part to, or 509 * null if a new object is to be created510 * 511 * @return object The multipart/mixed mimePart object604 * null if a new object is to be created 605 * 606 * @return object The multipart/mixed mimePart object 512 607 * @access private 513 608 */ … … 515 610 { 516 611 $params['content_type'] = 'multipart/related'; 612 $params['eol'] = $this->_build_params['eol']; 613 517 614 if (is_object($obj)) { 518 615 return $obj->addSubpart('', $params); … … 530 627 * @param array $value The image information 531 628 * 532 * @return object The image mimePart object629 * @return object The image mimePart object 533 630 * @access private 534 631 */ … … 540 637 $params['dfilename'] = $value['name']; 541 638 $params['cid'] = $value['cid']; 542 543 if (!empty($value['name-encoding'])) { 544 $params['name-encoding'] = $value['name-encoding']; 545 } 546 if (!empty($value['filename-encoding'])) { 547 $params['filename-encoding'] = $value['filename-encoding']; 639 $params['body_file'] = $value['body_file']; 640 $params['eol'] = $this->_build_params['eol']; 641 642 if (!empty($value['name_encoding'])) { 643 $params['name_encoding'] = $value['name_encoding']; 644 } 645 if (!empty($value['filename_encoding'])) { 646 $params['filename_encoding'] = $value['filename_encoding']; 548 647 } 549 648 550 649 $ret = $obj->addSubpart($value['body'], $params); 551 650 return $ret; 552 553 651 } 554 652 … … 560 658 * @param array $value The attachment information 561 659 * 562 * @return object The image mimePart object660 * @return object The image mimePart object 563 661 * @access private 564 662 */ 565 663 function &_addAttachmentPart(&$obj, $value) 566 664 { 567 $params['dfilename'] = $value['name']; 568 $params['encoding'] = $value['encoding']; 665 $params['eol'] = $this->_build_params['eol']; 666 $params['dfilename'] = $value['name']; 667 $params['encoding'] = $value['encoding']; 668 $params['content_type'] = $value['c_type']; 669 $params['body_file'] = $value['body_file']; 670 $params['disposition'] = isset($value['disposition']) ? 671 $value['disposition'] : 'attachment'; 569 672 if ($value['charset']) { 570 673 $params['charset'] = $value['charset']; … … 576 679 $params['location'] = $value['location']; 577 680 } 578 if ($value['name-encoding']) { 579 $params['name-encoding'] = $value['name-encoding']; 580 } 581 if ($value['filename-encoding']) { 582 $params['filename-encoding'] = $value['filename-encoding']; 583 } 584 $params['content_type'] = $value['c_type']; 585 $params['disposition'] = isset($value['disposition']) ? 586 $value['disposition'] : 'attachment'; 681 if (!empty($value['name_encoding'])) { 682 $params['name_encoding'] = $value['name_encoding']; 683 } 684 if (!empty($value['filename_encoding'])) { 685 $params['filename_encoding'] = $value['filename_encoding']; 686 } 687 587 688 $ret = $obj->addSubpart($value['body'], $params); 588 689 return $ret; … … 594 695 * with Mail_Mime is created. This means that, 595 696 * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF 596 * using the $ xtra_headers parameter!697 * using the $headers parameter! 597 698 * 598 * @param string $separation The separation etween these two parts. 599 * @param array $build_params The Build parameters passed to the 600 * &get() function. See &get for more info. 601 * @param array $xtra_headers The extra headers that should be passed 602 * to the &headers() function. 603 * See that function for more info. 604 * @param bool $overwrite Overwrite the existing headers with new. 605 * 606 * @return string The complete e-mail. 607 * @access public 608 */ 609 function getMessage( 610 $separation = null, 611 $build_params = null, 612 $xtra_headers = null, 613 $overwrite = false 614 ) 615 { 699 * @param string $separation The separation between these two parts. 700 * @param array $params The Build parameters passed to the 701 * &get() function. See &get for more info. 702 * @param array $headers The extra headers that should be passed 703 * to the &headers() function. 704 * See that function for more info. 705 * @param bool $overwrite Overwrite the existing headers with new. 706 * 707 * @return mixed The complete e-mail or PEAR error object 708 * @access public 709 */ 710 function getMessage($separation = null, $params = null, $headers = null, 711 $overwrite = false 712 ) { 616 713 if ($separation === null) { 617 $separation = MAIL_MIME_CRLF; 618 } 619 $body = $this->get($build_params); 620 $head = $this->txtHeaders($xtra_headers, $overwrite); 714 $separation = $this->_build_params['eol']; 715 } 716 717 $body = $this->get($params); 718 719 if (PEAR::isError($body)) { 720 return $body; 721 } 722 723 $head = $this->txtHeaders($headers, $overwrite); 621 724 $mail = $head . $separation . $body; 622 725 return $mail; 623 726 } 624 727 728 /** 729 * Writes (appends) the complete e-mail into file. 730 * 731 * @param string $filename Output file location 732 * @param array $params The Build parameters passed to the 733 * &get() function. See &get for more info. 734 * @param array $headers The extra headers that should be passed 735 * to the &headers() function. 736 * See that function for more info. 737 * @param bool $overwrite Overwrite the existing headers with new. 738 * 739 * @return mixed True or PEAR error object 740 * @access public 741 */ 742 function saveMessage($filename, $params = null, $headers = null, $overwrite = false) 743 { 744 // Check state of file and raise an error properly 745 if (file_exists($filename) && !is_writable($filename)) { 746 $err = PEAR::raiseError('File is not writable: ' . $filename); 747 return $err; 748 } 749 750 // Temporarily reset magic_quotes_runtime and read file contents 751 if ($magic_quote_setting = get_magic_quotes_runtime()) { 752 @ini_set('magic_quotes_runtime', 0); 753 } 754 755 if (!($fh = fopen($filename, 'ab'))) { 756 $err = PEAR::raiseError('Unable to open file: ' . $filename); 757 return $err; 758 } 759 760 // Write message headers into file (skipping Content-* headers) 761 $head = $this->txtHeaders($headers, $overwrite, true); 762 if (fwrite($fh, $head) === false) { 763 $err = PEAR::raiseError('Error writing to file: ' . $filename); 764 return $err; 765 } 766 767 fclose($fh); 768 769 if ($magic_quote_setting) { 770 @ini_set('magic_quotes_runtime', $magic_quote_setting); 771 } 772 773 // Write the rest of the message into file 774 $res = $this->get($params, $filename); 775 776 return $res ? $res : true; 777 } 625 778 626 779 /** … … 628 781 * returns the mime content. 629 782 * 630 * @param array $build_params Build parameters that change the way the email 631 * is built. Should be associative. Can contain: 632 * head_encoding - What encoding to use for the headers. 633 * Options: quoted-printable or base64 634 * Default is quoted-printable 635 * text_encoding - What encoding to use for plain text 636 * Options: 7bit, 8bit, 637 * base64, or quoted-printable 638 * Default is 7bit 639 * html_encoding - What encoding to use for html 640 * Options: 7bit, 8bit, 641 * base64, or quoted-printable 642 * Default is quoted-printable 643 * 7bit_wrap - Number of characters before text is 644 * wrapped in 7bit encoding 645 * Default is 998 646 * html_charset - The character set to use for html. 647 * Default is iso-8859-1 648 * text_charset - The character set to use for text. 649 * Default is iso-8859-1 650 * head_charset - The character set to use for headers. 651 * Default is iso-8859-1 652 * 653 * @return string The mime content 654 * @access public 655 */ 656 function &get($build_params = null) 657 { 658 if (isset($build_params)) { 659 while (list($key, $value) = each($build_params)) { 783 * @param array $params Build parameters that change the way the email 784 * is built. Should be associative. See $_build_params. 785 * @param resource $filename Output file where to save the message instead of 786 * returning it 787 * 788 * @return mixed The MIME message content string, null or PEAR error object 789 * @access public 790 */ 791 function &get($params = null, $filename = null) 792 { 793 if (isset($params)) { 794 while (list($key, $value) = each($params)) { 660 795 $this->_build_params[$key] = $value; 661 796 } 662 797 } 663 798 664 if (isset($this->_headers['From'])) {665 // Bug #11381: Illegal characters in domain ID666 if (preg_match("|(@[0-9a-zA-Z\-\.]+)|", $this->_headers['From'], $matches)) {799 if (isset($this->_headers['From'])) { 800 // Bug #11381: Illegal characters in domain ID 801 if (preg_match("|(@[0-9a-zA-Z\-\.]+)|", $this->_headers['From'], $matches)) { 667 802 $domainID = $matches[1]; 668 } else{803 } else { 669 804 $domainID = "@localhost"; 670 805 } 671 foreach($this->_html_images as $i => $img){ 672 $this->_html_images[$i]['cid'] = $this->_html_images[$i]['cid'] . $domainID; 673 } 674 } 675 676 if (count($this->_html_images) AND isset($this->_htmlbody)) { 806 foreach ($this->_html_images as $i => $img) { 807 $this->_html_images[$i]['cid'] 808 = $this->_html_images[$i]['cid'] . $domainID; 809 } 810 } 811 812 if (count($this->_html_images) && isset($this->_htmlbody)) { 677 813 foreach ($this->_html_images as $key => $value) { 678 814 $regex = array(); … … 687 823 688 824 $this->_htmlbody = preg_replace($regex, $rep, $this->_htmlbody); 689 $this->_html_images[$key]['name'] =690 $this->_basename($this->_html_images[$key]['name']);825 $this->_html_images[$key]['name'] 826 = $this->_basename($this->_html_images[$key]['name']); 691 827 } 692 828 } … … 696 832 $html_images = count($this->_html_images) ? true : false; 697 833 $html = strlen($this->_htmlbody) ? true : false; 698 $text = (!$html ANDstrlen($this->_txtbody)) ? true : false;834 $text = (!$html && strlen($this->_txtbody)) ? true : false; 699 835 700 836 switch (true) { 701 case $text AND!$attachments:837 case $text && !$attachments: 702 838 $message =& $this->_addTextPart($null, $this->_txtbody); 703 839 break; 704 840 705 case !$text AND !$html AND$attachments:841 case !$text && !$html && $attachments: 706 842 $message =& $this->_addMixedPart(); 707 843 for ($i = 0; $i < count($this->_parts); $i++) { … … 710 846 break; 711 847 712 case $text AND$attachments:848 case $text && $attachments: 713 849 $message =& $this->_addMixedPart(); 714 850 $this->_addTextPart($message, $this->_txtbody); … … 718 854 break; 719 855 720 case $html AND !$attachments AND!$html_images:856 case $html && !$attachments && !$html_images: 721 857 if (isset($this->_txtbody)) { 722 858 $message =& $this->_addAlternativePart($null); … … 728 864 break; 729 865 730 case $html AND !$attachments AND $html_images: 866 case $html && !$attachments && $html_images: 867 // * Content-Type: multipart/alternative; 868 // * text 869 // * Content-Type: multipart/related; 870 // * html 871 // * image... 872 if (isset($this->_txtbody)) { 873 $message =& $this->_addAlternativePart($null); 874 $this->_addTextPart($message, $this->_txtbody); 875 876 $ht =& $this->_addRelatedPart($message); 877 $this->_addHtmlPart($ht); 878 for ($i = 0; $i < count($this->_html_images); $i++) { 879 $this->_addHtmlImagePart($ht, $this->_html_images[$i]); 880 } 881 } else { 882 // * Content-Type: multipart/related; 883 // * html 884 // * image... 885 $message =& $this->_addRelatedPart($null); 886 $this->_addHtmlPart($message); 887 for ($i = 0; $i < count($this->_html_images); $i++) { 888 $this->_addHtmlImagePart($message, $this->_html_images[$i]); 889 } 890 } 891 /* 892 // #13444, #9725: the code below was a non-RFC compliant hack 893 // * Content-Type: multipart/related; 894 // * Content-Type: multipart/alternative; 895 // * text 896 // * html 897 // * image... 731 898 $message =& $this->_addRelatedPart($null); 732 899 if (isset($this->_txtbody)) { … … 740 907 $this->_addHtmlImagePart($message, $this->_html_images[$i]); 741 908 } 909 */ 742 910 break; 743 911 744 case $html AND $attachments AND!$html_images:912 case $html && $attachments && !$html_images: 745 913 $message =& $this->_addMixedPart(); 746 914 if (isset($this->_txtbody)) { … … 756 924 break; 757 925 758 case $html AND $attachments AND$html_images:926 case $html && $attachments && $html_images: 759 927 $message =& $this->_addMixedPart(); 760 928 if (isset($this->_txtbody)) { … … 776 944 } 777 945 778 if (isset($message)) { 779 $output = $message->encode(); 780 781 $this->_headers = array_merge($this->_headers, 782 $output['headers']); 946 if (!isset($message)) { 947 $ret = null; 948 return $ret; 949 } 950 951 // Use saved boundary 952 if (!empty($this->_build_params['boundary'])) { 953 $boundary = $this->_build_params['boundary']; 954 } else { 955 $boundary = null; 956 } 957 958 // Write output to file 959 if ($filename) { 960 // Append mimePart message headers and body into file 961 if (PEAR::isError($headers = $message->encodeToFile($filename, $boundary))) { 962 return $headers; 963 } 964 $this->_headers = array_merge($this->_headers, $headers); 965 $ret = null; 966 return $ret; 967 } else { 968 if (PEAR::isError($output = $message->encode($boundary))) { 969 return $output; 970 } 971 $this->_headers = array_merge($this->_headers, $output['headers']); 783 972 $body = $output['body']; 784 973 return $body; 785 786 } else {787 $ret = false;788 return $ret;789 974 } 790 975 } … … 795 980 * $array['header-name'] = 'header-value'; 796 981 * 797 * @param array $xtra_headers Assoc array with any extra headers. 798 * Optional. 982 * @param array $xtra_headers Assoc array with any extra headers (optional) 799 983 * @param bool $overwrite Overwrite already existing headers. 984 * @param bool $skip_content Don't return content headers: Content-Type 985 * Content-Disposition and Content-Transfer-Encoding 800 986 * 801 * @return array Assoc array with the mime headers 802 * @access public 803 */ 804 function &headers($xtra_headers = null, $overwrite = false) 805 { 806 // Content-Type header should already be present, 807 // So just add mime version header 987 * @return array Assoc array with the mime headers 988 * @access public 989 */ 990 function &headers($xtra_headers = null, $overwrite = false, $skip_content = false) 991 { 992 // Add mime version header 808 993 $headers['MIME-Version'] = '1.0'; 809 if (isset($xtra_headers)) { 994 995 // Content-Type and Content-Transfer-Encoding headers should already 996 // be present if get() was called, but we'll re-set them to make sure 997 // we got them when called before get() or something in the message 998 // has been changed after get() [#14780] 999 if (!$skip_content) { 1000 $headers += $this->_contentHeaders(); 1001 } 1002 1003 if (!empty($xtra_headers)) { 810 1004 $headers = array_merge($headers, $xtra_headers); 811 1005 } 1006 812 1007 if ($overwrite) { 813 1008 $this->_headers = array_merge($this->_headers, $headers); … … 816 1011 } 817 1012 818 $encodedHeaders = $this->_encodeHeaders($this->_headers); 1013 $headers = $this->_headers; 1014 1015 if ($skip_content) { 1016 unset($headers['Content-Type']); 1017 unset($headers['Content-Transfer-Encoding']); 1018 unset($headers['Content-Disposition']); 1019 } 1020 1021 $encodedHeaders = $this->_encodeHeaders($headers); 819 1022 return $encodedHeaders; 820 1023 } … … 824 1027 * (usefull if you want to use the PHP mail() function) 825 1028 * 826 * @param array $xtra_headers Assoc array with any extra headers. 827 * Optional. 1029 * @param array $xtra_headers Assoc array with any extra headers (optional) 828 1030 * @param bool $overwrite Overwrite the existing heaers with new. 829 * 830 * @return string Plain text headers 831 * @access public 832 */ 833 function txtHeaders($xtra_headers = null, $overwrite = false) 834 { 835 $headers = $this->headers($xtra_headers, $overwrite); 1031 * @param bool $skip_content Don't return content headers: Content-Type 1032 * and Content-Transfer-Encoding 1033 * 1034 * @return string Plain text headers 1035 * @access public 1036 */ 1037 function txtHeaders($xtra_headers = null, $overwrite = false, $skip_content = false) 1038 { 1039 $headers = $this->headers($xtra_headers, $overwrite, $skip_content); 1040 1041 // Place Received: headers at the beginning of the message 1042 // Spam detectors often flag messages with it after the Subject: as spam 1043 if (isset($headers['Received'])) { 1044 $received = $headers['Received']; 1045 unset($headers['Received']); 1046 $headers = array('Received' => $received) + $headers; 1047 } 836 1048 837 1049 $ret = ''; 1050 $eol = $this->_build_params['eol']; 1051 838 1052 foreach ($headers as $key => $val) { 839 $ret .= "$key: $val" . MAIL_MIME_CRLF; 840 } 1053 if (is_array($val)) { 1054 foreach ($val as $value) { 1055 $ret .= "$key: $value" . $eol; 1056 } 1057 } else { 1058 $ret .= "$key: $val" . $eol; 1059 } 1060 } 1061 841 1062 return $ret; 842 1063 } … … 905 1126 906 1127 /** 907 * Since the PHP send function requires you to specif iy1128 * Since the PHP send function requires you to specify 908 1129 * recipients (To: header) separately from the other 909 1130 * headers, the To: header is not properly encoded. … … 914 1135 * @param string $recipients A comma-delimited list of recipients 915 1136 * 916 * @return string Encoded data1137 * @return string Encoded data 917 1138 * @access public 918 1139 */ … … 925 1146 926 1147 /** 927 * Encodes a headeras per RFC20471148 * Encodes headers as per RFC2047 928 1149 * 929 1150 * @param array $input The header data to encode 930 1151 * @param array $params Extra build parameters 931 1152 * 932 * @return array Encoded data1153 * @return array Encoded data 933 1154 * @access private 934 1155 */ … … 939 1160 $build_params[$key] = $value; 940 1161 } 941 //$hdr_name: Name of the heaer 942 //$hdr_value: Full line of header value. 943 //$atoms: The $hdr_value split into atoms* 944 //$atom: A single atom to encode.* 945 //$hdr_value_out: The recombined $hdr_val-atoms, or the encoded string. 946 //Note: Atom as specified here is not exactly the same as an RFC822 atom, 947 //as $atom's may contain just a single space. 948 949 $useIconv = true; 950 if (isset($build_params['ignore-iconv'])) { 951 $useIconv = !$build_params['ignore-iconv']; 952 } 1162 953 1163 foreach ($input as $hdr_name => $hdr_value) { 954 /* 955 $parts = preg_split('/([ ])/', $hdr_value, -1, PREG_SPLIT_DELIM_CAPTURE); 956 $atoms = array(); 957 foreach ($parts as $part){ 958 $atom .= $part; 959 $quoteMatch = preg_match_all('|"|', $atom, $matches) % 2; 960 if (!$quoteMatch){ 961 $atoms[] = $atom; 962 $atom = null; 1164 if (is_array($hdr_value)) { 1165 foreach ($hdr_value as $idx => $value) { 1166 $input[$hdr_name][$idx] = $this->encodeHeader( 1167 $hdr_name, $value, 1168 $build_params['head_charset'], $build_params['head_encoding'] 1169 ); 963 1170 } 964 } 965 if ($atom){ 966 $atoms[] = $atom; 967 } 968 foreach ($atoms as $atom){ 969 */ 970 if (preg_match('#([\x80-\xFF]){1}#', $hdr_value)) { 971 if (function_exists('iconv_mime_encode') && $useIconv) { 972 $imePrefs = array(); 973 if ($build_params['head_encoding'] == 'base64') { 974 $imePrefs['scheme'] = 'B'; 1171 } else { 1172 $input[$hdr_name] = $this->encodeHeader( 1173 $hdr_name, $hdr_value, 1174 $build_params['head_charset'], $build_params['head_encoding'] 1175 ); 1176 } 1177 } 1178 1179 return $input; 1180 } 1181 1182 /** 1183 * Encodes a header as per RFC2047 1184 * 1185 * @param string $name The header name 1186 * @param string $value The header data to encode 1187 * @param string $charset Character set name 1188 * @param string $encoding Encoding name (base64 or quoted-printable) 1189 * 1190 * @return string Encoded header data (without a name) 1191 * @access public 1192 * @since 1.5.3 1193 */ 1194 function encodeHeader($name, $value, $charset, $encoding) 1195 { 1196 // Structured headers 1197 $comma_headers = array( 1198 'from', 'to', 'cc', 'bcc', 'sender', 'reply-to', 1199 'resent-from', 'resent-to', 'resent-cc', 'resent-bcc', 1200 'resent-sender', 'resent-reply-to', 1201 ); 1202 $other_headers = array( 1203 'references', 'in-reply-to', 'message-id', 'resent-message-id', 1204 ); 1205 1206 $name = strtolower($name); 1207 $eol = $this->_build_params['eol']; 1208 1209 if (in_array($name, $comma_headers)) { 1210 $separator = ','; 1211 } else if (in_array($name, $other_headers)) { 1212 $separator = ' '; 1213 } 1214 1215 if (!$charset) { 1216 $charset = 'ISO-8859-1'; 1217 } 1218 1219 // Structured header (make sure addr-spec inside is not encoded) 1220 if (!empty($separator)) { 1221 $parts = $this->_explodeQuotedString($separator, $value); 1222 $value = ''; 1223 1224 foreach ($parts as $part) { 1225 $part = preg_replace('/\r?\n[\s\t]*/', $eol . ' ', $part); 1226 $part = trim($part); 1227 1228 if (!$part) { 1229 continue; 1230 } 1231 if ($value) { 1232 $value .= $separator==',' ? $separator.' ' : ' '; 1233 } else { 1234 $value = $name . ': '; 1235 } 1236 1237 // let's find phrase (name) and/or addr-spec 1238 if (preg_match('/^<\S+@\S+>$/', $part)) { 1239 $value .= $part; 1240 } else if (preg_match('/^\S+@\S+$/', $part)) { 1241 // address without brackets and without name 1242 $value .= $part; 1243 } else if (preg_match('/<*\S+@\S+>*$/', $part, $matches)) { 1244 // address with name (handle name) 1245 $address = $matches[0]; 1246 $word = str_replace($address, '', $part); 1247 $word = trim($word); 1248 // check if phrase requires quoting 1249 if ($word) { 1250 // non-ASCII: require encoding 1251 if (preg_match('#([\x80-\xFF]){1}#', $word)) { 1252 if ($word[0] == '"' && $word[strlen($word)-1] == '"') { 1253 // de-quote quoted-string, encoding changes 1254 // string to atom 1255 $search = array("\\\"", "\\\\"); 1256 $replace = array("\"", "\\"); 1257 $word = str_replace($search, $replace, $word); 1258 $word = substr($word, 1, -1); 1259 } 1260 // find length of last line 1261 if (($pos = strrpos($value, $eol)) !== false) { 1262 $last_len = strlen($value) - $pos; 1263 } else { 1264 $last_len = strlen($value); 1265 } 1266 $word = $this->_encodeString( 1267 $word, $charset, $encoding, $last_len 1268 ); 1269 } else if (($word[0] != '"' || $word[strlen($word)-1] != '"') 1270 && preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $word) 1271 ) { 1272 // ASCII: quote string if needed 1273 $word = '"'.addcslashes($word, '\\"').'"'; 1274 } 1275 } 1276 $value .= $word.' '.$address; 1277 } else { 1278 // addr-spec not found, don't encode (?) 1279 $value .= $part; 1280 } 1281 1282 // RFC2822 recommends 78 characters limit, use 76 from RFC2047 1283 $value = wordwrap($value, 76, $eol . ' '); 1284 } 1285 1286 $value = preg_replace('/^'.$name.': /', '', $value); 1287 1288 } else { 1289 // Unstructured header 1290 // non-ASCII: require encoding 1291 if (preg_match('#([\x80-\xFF]){1}#', $value)) { 1292 if ($value[0] == '"' && $value[strlen($value)-1] == '"') { 1293 // de-quote quoted-string, encoding changes 1294 // string to atom 1295 $search = array("\\\"", "\\\\"); 1296 $replace = array("\"", "\\"); 1297 $value = str_replace($search, $replace, $value); 1298 $value = substr($value, 1, -1); 1299 } 1300 $value = $this->_encodeString( 1301 $value, $charset, $encoding, strlen($name) + 2 1302 ); 1303 } else if (strlen($name.': '.$value) > 78) { 1304 // ASCII: check if header line isn't too long and use folding 1305 $value = preg_replace('/\r?\n[\s\t]*/', $eol . ' ', $value); 1306 $tmp = wordwrap($name.': '.$value, 78, $eol . ' '); 1307 $value = preg_replace('/^'.$name.':\s*/', '', $tmp); 1308 // hard limit 998 (RFC2822) 1309 $value = wordwrap($value, 998, $eol . ' ', true); 1310 } 1311 } 1312 1313 return $value; 1314 } 1315 1316 /** 1317 * Encodes a header value as per RFC2047 1318 * 1319 * @param string $value The header data to encode 1320 * @param string $charset Character set name 1321 * @param string $encoding Encoding name (base64 or quoted-printable) 1322 * @param int $prefix_len Prefix length 1323 * 1324 * @return string Encoded header data 1325 * @access private 1326 */ 1327 function _encodeString($value, $charset, $encoding, $prefix_len=0) 1328 { 1329 if ($encoding == 'base64') { 1330 // Base64 encode the entire string 1331 $value = base64_encode($value); 1332 1333 // Generate the header using the specified params and dynamicly 1334 // determine the maximum length of such strings. 1335 // 75 is the value specified in the RFC. 1336 $prefix = '=?' . $charset . '?B?'; 1337 $suffix = '?='; 1338 $maxLength = 75 - strlen($prefix . $suffix) - 2; 1339 $maxLength1stLine = $maxLength - $prefix_len; 1340 1341 // We can cut base4 every 4 characters, so the real max 1342 // we can get must be rounded down. 1343 $maxLength = $maxLength - ($maxLength % 4); 1344 $maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4); 1345 1346 $cutpoint = $maxLength1stLine; 1347 $value_out = $value; 1348 $output = ''; 1349 while ($value_out) { 1350 // Split translated string at every $maxLength 1351 $part = substr($value_out, 0, $cutpoint); 1352 $value_out = substr($value_out, $cutpoint); 1353 $cutpoint = $maxLength; 1354 // RFC 2047 specifies that any split header should 1355 // be seperated by a CRLF SPACE. 1356 if ($output) { 1357 $output .= $this->_build_params['eol'] . ' '; 1358 } 1359 $output .= $prefix . $part . $suffix; 1360 } 1361 $value = $output; 1362 } else { 1363 // quoted-printable encoding has been selected 1364 $value = Mail_mimePart::encodeQP($value); 1365 1366 // Generate the header using the specified params and dynamicly 1367 // determine the maximum length of such strings. 1368 // 75 is the value specified in the RFC. 1369 $prefix = '=?' . $charset . '?Q?'; 1370 $suffix = '?='; 1371 $maxLength = 75 - strlen($prefix . $suffix) - 3; 1372 $maxLength1stLine = $maxLength - $prefix_len; 1373 $maxLength = $maxLength - 1; 1374 1375 // This regexp will break QP-encoded text at every $maxLength 1376 // but will not break any encoded letters. 1377 $reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|"; 1378 $reg2nd = "|(.{0,$maxLength}[^\=][^\=])|"; 1379 1380 $value_out = $value; 1381 $realMax = $maxLength1stLine + strlen($prefix . $suffix); 1382 if (strlen($value_out) >= $realMax) { 1383 // Begin with the regexp for the first line. 1384 $reg = $reg1st; 1385 $output = ''; 1386 while ($value_out) { 1387 // Split translated string at every $maxLength 1388 // But make sure not to break any translated chars. 1389 $found = preg_match($reg, $value_out, $matches); 1390 1391 // After this first line, we need to use a different 1392 // regexp for the first line. 1393 $reg = $reg2nd; 1394 1395 // Save the found part and encapsulate it in the 1396 // prefix & suffix. Then remove the part from the 1397 // $value_out variable. 1398 if ($found) { 1399 $part = $matches[0]; 1400 $len = strlen($matches[0]); 1401 $value_out = substr($value_out, $len); 975 1402 } else { 976 $imePrefs['scheme'] = 'Q'; 1403 $part = $value_out; 1404 $value_out = ""; 977 1405 } 978 $imePrefs['input-charset'] = $build_params['head_charset']; 979 $imePrefs['output-charset'] = $build_params['head_charset']; 980 $imePrefs['line-length'] = 74; 981 $imePrefs['line-break-chars'] = "\r\n"; //Specified in RFC2047 982 983 $hdr_value = iconv_mime_encode($hdr_name, $hdr_value, $imePrefs); 984 $hdr_value = preg_replace("#^{$hdr_name}\:\ #", "", $hdr_value); 985 } elseif ($build_params['head_encoding'] == 'base64') { 986 //Base64 encoding has been selected. 987 //Base64 encode the entire string 988 $hdr_value = base64_encode($hdr_value); 989 990 //Generate the header using the specified params and dynamicly 991 //determine the maximum length of such strings. 992 //75 is the value specified in the RFC. The first -2 is there so 993 //the later regexp doesn't break any of the translated chars. 994 //The -2 on the first line-regexp is to compensate for the ": " 995 //between the header-name and the header value 996 $prefix = '=?' . $build_params['head_charset'] . '?B?'; 997 $suffix = '?='; 998 $maxLength = 75 - strlen($prefix . $suffix) - 2; 999 $maxLength1stLine = $maxLength - strlen($hdr_name) - 2; 1000 1001 //We can cut base4 every 4 characters, so the real max 1002 //we can get must be rounded down. 1003 $maxLength = $maxLength - ($maxLength % 4); 1004 $maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4); 1005 1006 $cutpoint = $maxLength1stLine; 1007 $hdr_value_out = $hdr_value; 1008 $output = ""; 1009 while ($hdr_value_out) { 1010 //Split translated string at every $maxLength 1011 $part = substr($hdr_value_out, 0, $cutpoint); 1012 $hdr_value_out = substr($hdr_value_out, $cutpoint); 1013 $cutpoint = $maxLength; 1014 //RFC 2047 specifies that any split header should 1015 //be seperated by a CRLF SPACE. 1016 if ($output) { 1017 $output .= "\r\n "; 1018 } 1019 $output .= $prefix . $part . $suffix; 1406 1407 // RFC 2047 specifies that any split header should 1408 // be seperated by a CRLF SPACE 1409 if ($output) { 1410 $output .= $this->_build_params['eol'] . ' '; 1020 1411 } 1021 $hdr_value = $output; 1022 } else { 1023 //quoted-printable encoding has been selected 1024 1025 //Fix for Bug #10298, Ota Mares <om@viazenetti.de> 1026 //Check if there is a double quote at beginning or end of 1027 //the string to prevent that an open or closing quote gets 1028 //ignored because it is encapsuled by an encoding pre/suffix. 1029 //Remove the double quote and set the specific prefix or 1030 //suffix variable so that we can concat the encoded string and 1031 //the double quotes back together to get the intended string. 1032 $quotePrefix = $quoteSuffix = ''; 1033 if ($hdr_value{0} == '"') { 1034 $hdr_value = substr($hdr_value, 1); 1035 $quotePrefix = '"'; 1036 } 1037 if ($hdr_value{strlen($hdr_value)-1} == '"') { 1038 $hdr_value = substr($hdr_value, 0, -1); 1039 $quoteSuffix = '"'; 1040 } 1041 1042 //Generate the header using the specified params and dynamicly 1043 //determine the maximum length of such strings. 1044 //75 is the value specified in the RFC. The -2 is there so 1045 //the later regexp doesn't break any of the translated chars. 1046 //The -2 on the first line-regexp is to compensate for the ": " 1047 //between the header-name and the header value 1048 $prefix = '=?' . $build_params['head_charset'] . '?Q?'; 1049 $suffix = '?='; 1050 $maxLength = 75 - strlen($prefix . $suffix) - 2 - 1; 1051 $maxLength1stLine = $maxLength - strlen($hdr_name) - 2; 1052 $maxLength = $maxLength - 1; 1053 1054 //Replace all special characters used by the encoder. 1055 $search = array('=', '_', '?', ' '); 1056 $replace = array('=3D', '=5F', '=3F', '_'); 1057 $hdr_value = str_replace($search, $replace, $hdr_value); 1058 1059 //Replace all extended characters (\x80-xFF) with their 1060 //ASCII values. 1061 $hdr_value = preg_replace('#([\x80-\xFF])#e', 1062 '"=" . strtoupper(dechex(ord("\1")))', 1063 $hdr_value); 1064 1065 //This regexp will break QP-encoded text at every $maxLength 1066 //but will not break any encoded letters. 1067 $reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|"; 1068 $reg2nd = "|(.{0,$maxLength}[^\=][^\=])|"; 1069 //Fix for Bug #10298, Ota Mares <om@viazenetti.de> 1070 //Concat the double quotes and encoded string together 1071 $hdr_value = $quotePrefix . $hdr_value . $quoteSuffix; 1072 1073 $hdr_value_out = $hdr_value; 1074 $realMax = $maxLength1stLine + strlen($prefix . $suffix); 1075 if (strlen($hdr_value_out) >= $realMax) { 1076 //Begin with the regexp for the first line. 1077 $reg = $reg1st; 1078 $output = ""; 1079 while ($hdr_value_out) { 1080 //Split translated string at every $maxLength 1081 //But make sure not to break any translated chars. 1082 $found = preg_match($reg, $hdr_value_out, $matches); 1083 1084 //After this first line, we need to use a different 1085 //regexp for the first line. 1086 $reg = $reg2nd; 1087 1088 //Save the found part and encapsulate it in the 1089 //prefix & suffix. Then remove the part from the 1090 //$hdr_value_out variable. 1091 if ($found) { 1092 $part = $matches[0]; 1093 $len = strlen($matches[0]); 1094 $hdr_value_out = substr($hdr_value_out, $len); 1095 } else { 1096 $part = $hdr_value_out; 1097 $hdr_value_out = ""; 1098 } 1099 1100 //RFC 2047 specifies that any split header should 1101 //be seperated by a CRLF SPACE 1102 if ($output) { 1103 $output .= "\r\n "; 1104 } 1105 $output .= $prefix . $part . $suffix; 1106 } 1107 $hdr_value_out = $output; 1108 } else { 1109 $hdr_value_out = $prefix . $hdr_value_out . $suffix; 1110 } 1111 $hdr_value = $hdr_value_out; 1412 $output .= $prefix . $part . $suffix; 1112 1413 } 1113 } 1114 $input[$hdr_name] = $hdr_value; 1115 } 1116 return $input; 1117 } 1118 1119 /** 1120 * Set the object's end-of-line and define the constant if applicable. 1121 * 1122 * @param string $eol End Of Line sequence 1123 * 1124 * @return void 1125 * @access private 1126 */ 1127 function _setEOL($eol) 1128 { 1129 $this->_eol = $eol; 1130 if (!defined('MAIL_MIME_CRLF')) { 1131 define('MAIL_MIME_CRLF', $this->_eol, true); 1132 } 1414 $value_out = $output; 1415 } else { 1416 $value_out = $prefix . $value_out . $suffix; 1417 } 1418 $value = $value_out; 1419 } 1420 1421 return $value; 1422 } 1423 1424 /** 1425 * Explode quoted string 1426 * 1427 * @param string $delimiter Delimiter expression string for preg_match() 1428 * @param string $string Input string 1429 * 1430 * @return array String tokens array 1431 * @access private 1432 */ 1433 function _explodeQuotedString($delimiter, $string) 1434 { 1435 $result = array(); 1436 $strlen = strlen($string); 1437 1438 for ($q=$p=$i=0; $i < $strlen; $i++) { 1439 if ($string[$i] == "\"" 1440 && (empty($string[$i-1]) || $string[$i-1] != "\\") 1441 ) { 1442 $q = $q ? false : true; 1443 } else if (!$q && preg_match("/$delimiter/", $string[$i])) { 1444 $result[] = substr($string, $p, $i - $p); 1445 $p = $i + 1; 1446 } 1447 } 1448 1449 $result[] = substr($string, $p); 1450 return $result; 1133 1451 } 1134 1452 … … 1136 1454 * Get file's basename (locale independent) 1137 1455 * 1138 * @param string Filename1139 * 1140 * @return string Basename1456 * @param string $filename Filename 1457 * 1458 * @return string Basename 1141 1459 * @access private 1142 1460 */ … … 1144 1462 { 1145 1463 // basename() is not unicode safe and locale dependent 1146 if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) 1464 if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) { 1147 1465 return preg_replace('/^.*[\\\\\\/]/', '', $filename); 1148 else1466 } else { 1149 1467 return preg_replace('/^.*[\/]/', '', $filename); 1468 } 1469 } 1470 1471 /** 1472 * Get Content-Type and Content-Transfer-Encoding headers of the message 1473 * 1474 * @return array Headers array 1475 * @access private 1476 */ 1477 function _contentHeaders() 1478 { 1479 $attachments = count($this->_parts) ? true : false; 1480 $html_images = count($this->_html_images) ? true : false; 1481 $html = strlen($this->_htmlbody) ? true : false; 1482 $text = (!$html && strlen($this->_txtbody)) ? true : false; 1483 $headers = array(); 1484 1485 // See get() 1486 switch (true) { 1487 case $text && !$attachments: 1488 $headers['Content-Type'] = 'text/plain'; 1489 break; 1490 1491 case !$text && !$html && $attachments: 1492 case $text && $attachments: 1493 case $html && $attachments && !$html_images: 1494 case $html && $attachments && $html_images: 1495 $headers['Content-Type'] = 'multipart/mixed'; 1496 break; 1497 1498 case $html && !$attachments && !$html_images && isset($this->_txtbody): 1499 case $html && !$attachments && $html_images && isset($this->_txtbody): 1500 $headers['Content-Type'] = 'multipart/alternative'; 1501 break; 1502 1503 case $html && !$attachments && !$html_images && !isset($this->_txtbody): 1504 $headers['Content-Type'] = 'text/html'; 1505 break; 1506 1507 case $html && !$attachments && $html_images && !isset($this->_txtbody): 1508 $headers['Content-Type'] = 'multipart/related'; 1509 break; 1510 1511 default: 1512 return $headers; 1513 } 1514 1515 $eol = !empty($this->_build_params['eol']) 1516 ? $this->_build_params['eol'] : "\r\n"; 1517 1518 if ($headers['Content-Type'] == 'text/plain') { 1519 // single-part message: add charset and encoding 1520 $headers['Content-Type'] 1521 .= ";$eol charset=" . $this->_build_params['text_charset']; 1522 $headers['Content-Transfer-Encoding'] 1523 = $this->_build_params['text_encoding']; 1524 } else if ($headers['Content-Type'] == 'text/html') { 1525 // single-part message: add charset and encoding 1526 $headers['Content-Type'] 1527 .= ";$eol charset=" . $this->_build_params['html_charset']; 1528 $headers['Content-Transfer-Encoding'] 1529 = $this->_build_params['html_encoding']; 1530 } else { 1531 // multipart message: add charset and boundary 1532 if (!empty($this->_build_params['boundary'])) { 1533 $boundary = $this->_build_params['boundary']; 1534 } else if (!empty($this->_headers['Content-Type']) 1535 && preg_match('/boundary="([^"]+)"/', $this->_headers['Content-Type'], $m) 1536 ) { 1537 $boundary = $m[1]; 1538 } else { 1539 $boundary = '=_' . md5(rand() . microtime()); 1540 } 1541 1542 $this->_build_params['boundary'] = $boundary; 1543 $headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; 1544 } 1545 1546 return $headers; 1150 1547 } 1151 1548 -
program/lib/Mail/mimePart.php
rf7f9346 r0917356 41 41 * THE POSSIBILITY OF SUCH DAMAGE. 42 42 * 43 * @category Mail 44 * @package Mail_Mime 45 * @author Richard Heyes <richard@phpguru.org> 46 * @author Cipriano Groenendal <cipri@php.net> 47 * @author Sean Coates <sean@php.net> 48 * @copyright 2003-2006 PEAR <pear-group@php.net> 49 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 50 * @version CVS: $Id$ 51 * @link http://pear.php.net/package/Mail_mime 43 * @category Mail 44 * @package Mail_Mime 45 * @author Richard Heyes <richard@phpguru.org> 46 * @author Cipriano Groenendal <cipri@php.net> 47 * @author Sean Coates <sean@php.net> 48 * @author Aleksander Machniak <alec@php.net> 49 * @copyright 2003-2006 PEAR <pear-group@php.net> 50 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 51 * @version CVS: $Id$ 52 * @link http://pear.php.net/package/Mail_mime 52 53 */ 53 54 … … 62 63 * This class however allows full control over the email. 63 64 * 64 * @category Mail 65 * @package Mail_Mime 66 * @author Richard Heyes <richard@phpguru.org> 67 * @author Cipriano Groenendal <cipri@php.net> 68 * @author Sean Coates <sean@php.net> 69 * @copyright 2003-2006 PEAR <pear-group@php.net> 70 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 71 * @version Release: @package_version@ 72 * @link http://pear.php.net/package/Mail_mime 65 * @category Mail 66 * @package Mail_Mime 67 * @author Richard Heyes <richard@phpguru.org> 68 * @author Cipriano Groenendal <cipri@php.net> 69 * @author Sean Coates <sean@php.net> 70 * @author Aleksander Machniak <alec@php.net> 71 * @copyright 2003-2006 PEAR <pear-group@php.net> 72 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 73 * @version Release: @package_version@ 74 * @link http://pear.php.net/package/Mail_mime 73 75 */ 74 class Mail_mimePart {75 76 /**76 class Mail_mimePart 77 { 78 /** 77 79 * The encoding type of this part 78 80 * … … 82 84 var $_encoding; 83 85 84 /**86 /** 85 87 * An array of subparts 86 88 * … … 90 92 var $_subparts; 91 93 92 /**94 /** 93 95 * The output of this part after being built 94 96 * … … 98 100 var $_encoded; 99 101 100 /**102 /** 101 103 * Headers for this part 102 104 * … … 106 108 var $_headers; 107 109 108 /**110 /** 109 111 * The body of this part (not encoded) 110 112 * … … 115 117 116 118 /** 117 * Constructor. 118 * 119 * Sets up the object. 120 * 121 * @param $body - The body of the mime part if any. 122 * @param $params - An associative array of parameters: 123 * content_type - The content type for this part eg multipart/mixed 124 * encoding - The encoding to use, 7bit, 8bit, base64, or quoted-printable 125 * cid - Content ID to apply 126 * disposition - Content disposition, inline or attachment 127 * dfilename - Optional filename parameter for content disposition 128 * description - Content description 129 * charset - Character set to use 130 * @access public 131 */ 119 * The location of file with body of this part (not encoded) 120 * 121 * @var string 122 * @access private 123 */ 124 var $_body_file; 125 126 /** 127 * The end-of-line sequence 128 * 129 * @var string 130 * @access private 131 */ 132 var $_eol = "\r\n"; 133 134 /** 135 * Constructor. 136 * 137 * Sets up the object. 138 * 139 * @param string $body The body of the mime part if any. 140 * @param array $params An associative array of optional parameters: 141 * content_type - The content type for this part eg multipart/mixed 142 * encoding - The encoding to use, 7bit, 8bit, 143 * base64, or quoted-printable 144 * cid - Content ID to apply 145 * disposition - Content disposition, inline or attachment 146 * dfilename - Filename parameter for content disposition 147 * description - Content description 148 * charset - Character set to use 149 * name_encoding - Encoding for attachment name (Content-Type) 150 * By default filenames are encoded using RFC2231 151 * Here you can set RFC2047 encoding (quoted-printable 152 * or base64) instead 153 * filename_encoding - Encoding for attachment filename (Content-Disposition) 154 * See 'name_encoding' 155 * eol - End of line sequence. Default: "\r\n" 156 * body_file - Location of file with part's body (instead of $body) 157 * 158 * @access public 159 */ 132 160 function Mail_mimePart($body = '', $params = array()) 133 161 { 134 if (!defined('MAIL_MIMEPART_CRLF')) { 135 define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE); 136 } 137 138 $contentType = array(); 139 $contentDisp = array(); 162 if (!empty($params['eol'])) { 163 $this->_eol = $params['eol']; 164 } else if (defined('MAIL_MIMEPART_CRLF')) { // backward-copat. 165 $this->_eol = MAIL_MIMEPART_CRLF; 166 } 167 168 $c_type = array(); 169 $c_disp = array(); 140 170 foreach ($params as $key => $value) { 141 171 switch ($key) { 142 case 'content_type': 143 $contentType['type'] = $value; 144 //$headers['Content-Type'] = $value . (isset($charset) ? '; charset="' . $charset . '"' : ''); 145 break; 146 147 case 'encoding': 148 $this->_encoding = $value; 149 $headers['Content-Transfer-Encoding'] = $value; 150 break; 151 152 case 'cid': 153 $headers['Content-ID'] = '<' . $value . '>'; 154 break; 155 156 case 'disposition': 157 $contentDisp['disp'] = $value; 158 break; 159 160 case 'dfilename': 161 $contentDisp['filename'] = $value; 162 $contentType['name'] = $value; 163 break; 164 165 case 'description': 166 $headers['Content-Description'] = $value; 167 break; 168 169 case 'charset': 170 $contentType['charset'] = $value; 171 $contentDisp['charset'] = $value; 172 break; 173 174 case 'language': 175 $contentType['language'] = $value; 176 $contentDisp['language'] = $value; 177 break; 178 179 case 'location': 180 $headers['Content-Location'] = $value; 181 break; 182 183 } 184 } 185 186 if (isset($contentType['type'])) { 187 $headers['Content-Type'] = $contentType['type']; 188 if (isset($contentType['charset'])) { 189 $headers['Content-Type'] .= "; charset={$contentType['charset']}"; 190 } 191 if (isset($contentType['name'])) { 192 $headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF; 193 $headers['Content-Type'] .= 194 $this->_buildHeaderParam('name', $contentType['name'], 195 isset($contentType['charset']) ? $contentType['charset'] : 'US-ASCII', 196 isset($contentType['language']) ? $contentType['language'] : NULL, 197 isset($params['name-encoding']) ? $params['name-encoding'] : NULL); 198 } 199 } 200 201 202 if (isset($contentDisp['disp'])) { 203 $headers['Content-Disposition'] = $contentDisp['disp']; 204 if (isset($contentDisp['filename'])) { 205 $headers['Content-Disposition'] .= ';' . MAIL_MIMEPART_CRLF; 206 $headers['Content-Disposition'] .= 207 $this->_buildHeaderParam('filename', $contentDisp['filename'], 208 isset($contentDisp['charset']) ? $contentDisp['charset'] : 'US-ASCII', 209 isset($contentDisp['language']) ? $contentDisp['language'] : NULL, 210 isset($params['filename-encoding']) ? $params['filename-encoding'] : NULL); 172 case 'content_type': 173 $c_type['type'] = $value; 174 break; 175 176 case 'encoding': 177 $this->_encoding = $value; 178 $headers['Content-Transfer-Encoding'] = $value; 179 break; 180 181 case 'cid': 182 $headers['Content-ID'] = '<' . $value . '>'; 183 break; 184 185 case 'disposition': 186 $c_disp['disp'] = $value; 187 break; 188 189 case 'dfilename': 190 $c_disp['filename'] = $value; 191 $c_type['name'] = $value; 192 break; 193 194 case 'description': 195 $headers['Content-Description'] = $value; 196 break; 197 198 case 'charset': 199 $c_type['charset'] = $value; 200 $c_disp['charset'] = $value; 201 break; 202 203 case 'language': 204 $c_type['language'] = $value; 205 $c_disp['language'] = $value; 206 break; 207 208 case 'location': 209 $headers['Content-Location'] = $value; 210 break; 211 212 case 'body_file': 213 $this->_body_file = $value; 214 break; 215 } 216 } 217 218 // Content-Type 219 if (isset($c_type['type'])) { 220 $headers['Content-Type'] = $c_type['type']; 221 if (isset($c_type['name'])) { 222 $headers['Content-Type'] .= ';' . $this->_eol; 223 $headers['Content-Type'] .= $this->_buildHeaderParam( 224 'name', $c_type['name'], 225 isset($c_type['charset']) ? $c_type['charset'] : 'US-ASCII', 226 isset($c_type['language']) ? $c_type['language'] : null, 227 isset($params['name_encoding']) ? $params['name_encoding'] : null 228 ); 229 } 230 if (isset($c_type['charset'])) { 231 $headers['Content-Type'] 232 .= ';' . $this->_eol . " charset={$c_type['charset']}"; 233 } 234 } 235 236 // Content-Disposition 237 if (isset($c_disp['disp'])) { 238 $headers['Content-Disposition'] = $c_disp['disp']; 239 if (isset($c_disp['filename'])) { 240 $headers['Content-Disposition'] .= ';' . $this->_eol; 241 $headers['Content-Disposition'] .= $this->_buildHeaderParam( 242 'filename', $c_disp['filename'], 243 isset($c_disp['charset']) ? $c_disp['charset'] : 'US-ASCII', 244 isset($c_disp['language']) ? $c_disp['language'] : null, 245 isset($params['filename_encoding']) ? $params['filename_encoding'] : null 246 ); 211 247 } 212 248 } … … 217 253 } 218 254 219 // Default encoding255 // Default encoding 220 256 if (!isset($this->_encoding)) { 221 257 $this->_encoding = '7bit'; … … 229 265 230 266 /** 231 * encode()232 *233 267 * Encodes and returns the email. Also stores 234 268 * it in the encoded member variable 235 269 * 270 * @param string $boundary Pre-defined boundary string 271 * 236 272 * @return An associative array containing two elements, 237 273 * body and headers. The headers element is itself 238 * an indexed array. 274 * an indexed array. On error returns PEAR error object. 239 275 * @access public 240 276 */ 241 function encode( )277 function encode($boundary=null) 242 278 { 243 279 $encoded =& $this->_encoded; 244 280 245 281 if (count($this->_subparts)) { 246 $boundary = '=_' . md5(rand() . microtime()); 247 $this->_headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF . "\t" . 'boundary="' . $boundary . '"'; 248 249 // Add body parts to $subparts 282 $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); 283 $eol = $this->_eol; 284 285 $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; 286 287 $encoded['body'] = ''; 288 250 289 for ($i = 0; $i < count($this->_subparts); $i++) { 251 $ headers = array();290 $encoded['body'] .= '--' . $boundary . $eol; 252 291 $tmp = $this->_subparts[$i]->encode(); 292 if (PEAR::isError($tmp)) { 293 return $tmp; 294 } 253 295 foreach ($tmp['headers'] as $key => $value) { 254 $headers[] = $key . ': ' . $value; 255 } 256 $subparts[] = implode(MAIL_MIMEPART_CRLF, $headers) . MAIL_MIMEPART_CRLF . MAIL_MIMEPART_CRLF . $tmp['body'] . MAIL_MIMEPART_CRLF; 257 } 258 259 $encoded['body'] = '--' . $boundary . MAIL_MIMEPART_CRLF . 260 implode('--' . $boundary . MAIL_MIMEPART_CRLF , $subparts) . 261 '--' . $boundary.'--' . MAIL_MIMEPART_CRLF; 262 296 $encoded['body'] .= $key . ': ' . $value . $eol; 297 } 298 $encoded['body'] .= $eol . $tmp['body'] . $eol; 299 } 300 301 $encoded['body'] .= '--' . $boundary . '--' . $eol; 302 303 } else if ($this->_body) { 304 $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding); 305 } else if ($this->_body_file) { 306 // Temporarily reset magic_quotes_runtime for file reads and writes 307 if ($magic_quote_setting = get_magic_quotes_runtime()) { 308 @ini_set('magic_quotes_runtime', 0); 309 } 310 $body = $this->_getEncodedDataFromFile($this->_body_file, $this->_encoding); 311 if ($magic_quote_setting) { 312 @ini_set('magic_quotes_runtime', $magic_quote_setting); 313 } 314 315 if (PEAR::isError($body)) { 316 return $body; 317 } 318 $encoded['body'] = $body; 263 319 } else { 264 $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding);320 $encoded['body'] = ''; 265 321 } 266 322 … … 272 328 273 329 /** 274 * &addSubPart() 275 * 330 * Encodes and saves the email into file. File must exist. 331 * Data will be appended to the file. 332 * 333 * @param string $filename Output file location 334 * @param string $boundary Pre-defined boundary string 335 * 336 * @return array An associative array containing message headers 337 * or PEAR error object 338 * @access public 339 * @since 1.6.0 340 */ 341 function encodeToFile($filename, $boundary=null) 342 { 343 if (file_exists($filename) && !is_writable($filename)) { 344 $err = PEAR::raiseError('File is not writeable: ' . $filename); 345 return $err; 346 } 347 348 if (!($fh = fopen($filename, 'ab'))) { 349 $err = PEAR::raiseError('Unable to open file: ' . $filename); 350 return $err; 351 } 352 353 // Temporarily reset magic_quotes_runtime for file reads and writes 354 if ($magic_quote_setting = get_magic_quotes_runtime()) { 355 @ini_set('magic_quotes_runtime', 0); 356 } 357 358 $res = $this->_encodePartToFile($fh, $boundary); 359 360 fclose($fh); 361 362 if ($magic_quote_setting) { 363 @ini_set('magic_quotes_runtime', $magic_quote_setting); 364 } 365 366 return PEAR::isError($res) ? $res : $this->_headers; 367 } 368 369 /** 370 * Encodes given email part into file 371 * 372 * @param string $fh Output file handle 373 * @param string $boundary Pre-defined boundary string 374 * 375 * @return array True on sucess or PEAR error object 376 * @access private 377 */ 378 function _encodePartToFile($fh, $boundary=null) 379 { 380 $eol = $this->_eol; 381 382 if (count($this->_subparts)) { 383 $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); 384 $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; 385 } 386 387 foreach ($this->_headers as $key => $value) { 388 fwrite($fh, $key . ': ' . $value . $eol); 389 } 390 391 if (count($this->_subparts)) { 392 for ($i = 0; $i < count($this->_subparts); $i++) { 393 fwrite($fh, $eol . '--' . $boundary . $eol); 394 $res = $this->_subparts[$i]->_encodePartToFile($fh); 395 if (PEAR::isError($res)) { 396 return $res; 397 } 398 } 399 400 fwrite($fh, $eol . '--' . $boundary . '--' . $eol); 401 402 } else if ($this->_body) { 403 fwrite($fh, $eol . $this->_getEncodedData($this->_body, $this->_encoding)); 404 } else if ($this->_body_file) { 405 fwrite($fh, $eol); 406 $res = $this->_getEncodedDataFromFile( 407 $this->_body_file, $this->_encoding, $fh 408 ); 409 if (PEAR::isError($res)) { 410 return $res; 411 } 412 } 413 414 return true; 415 } 416 417 /** 276 418 * Adds a subpart to current mime part and returns 277 419 * a reference to it 278 420 * 279 * @param $body The body of the subpart, if any. 280 * @param $params The parameters for the subpart, same 281 * as the $params argument for constructor. 282 * @return A reference to the part you just added. It is 283 * crucial if using multipart/* in your subparts that 284 * you use =& in your script when calling this function, 285 * otherwise you will not be able to add further subparts. 421 * @param string $body The body of the subpart, if any. 422 * @param array $params The parameters for the subpart, same 423 * as the $params argument for constructor. 424 * 425 * @return Mail_mimePart A reference to the part you just added. It is 426 * crucial if using multipart/* in your subparts that 427 * you use =& in your script when calling this function, 428 * otherwise you will not be able to add further subparts. 286 429 * @access public 287 430 */ … … 293 436 294 437 /** 295 * _getEncodedData()296 *297 438 * Returns encoded data based upon encoding passed to it 298 439 * 299 * @param $data The data to encode. 300 * @param $encoding The encoding type to use, 7bit, base64, 301 * or quoted-printable. 440 * @param string $data The data to encode. 441 * @param string $encoding The encoding type to use, 7bit, base64, 442 * or quoted-printable. 443 * 444 * @return string 302 445 * @access private 303 446 */ … … 305 448 { 306 449 switch ($encoding) { 307 case '8bit': 308 case '7bit': 309 return $data; 310 break; 311 312 case 'quoted-printable': 313 return $this->_quotedPrintableEncode($data); 314 break; 315 316 case 'base64': 317 return rtrim(chunk_split(base64_encode($data), 76, MAIL_MIMEPART_CRLF)); 318 break; 319 320 default: 321 return $data; 322 } 323 } 324 325 /** 326 * quotedPrintableEncode() 327 * 450 case 'quoted-printable': 451 return $this->_quotedPrintableEncode($data); 452 break; 453 454 case 'base64': 455 return rtrim(chunk_split(base64_encode($data), 76, $this->_eol)); 456 break; 457 458 case '8bit': 459 case '7bit': 460 default: 461 return $data; 462 } 463 } 464 465 /** 466 * Returns encoded data based upon encoding passed to it 467 * 468 * @param string $filename Data file location 469 * @param string $encoding The encoding type to use, 7bit, base64, 470 * or quoted-printable. 471 * @param resource $fh Output file handle. If set, data will be 472 * stored into it instead of returning it 473 * 474 * @return string Encoded data or PEAR error object 475 * @access private 476 */ 477 function _getEncodedDataFromFile($filename, $encoding, $fh=null) 478 { 479 if (!is_readable($filename)) { 480 $err = PEAR::raiseError('Unable to read file: ' . $filename); 481 return $err; 482 } 483 484 if (!($fd = fopen($filename, 'rb'))) { 485 $err = PEAR::raiseError('Could not open file: ' . $filename); 486 return $err; 487 } 488 489 $data = ''; 490 491 switch ($encoding) { 492 case 'quoted-printable': 493 while (!feof($fd)) { 494 $buffer = $this->_quotedPrintableEncode(fgets($fd)); 495 if ($fh) { 496 fwrite($fh, $buffer); 497 } else { 498 $data .= $buffer; 499 } 500 } 501 break; 502 503 case 'base64': 504 while (!feof($fd)) { 505 // Should read in a multiple of 57 bytes so that 506 // the output is 76 bytes per line. Don't use big chunks 507 // because base64 encoding is memory expensive 508 $buffer = fread($fd, 57 * 9198); // ca. 0.5 MB 509 $buffer = base64_encode($buffer); 510 $buffer = chunk_split($buffer, 76, $this->_eol); 511 if (feof($fd)) { 512 $buffer = rtrim($buffer); 513 } 514 515 if ($fh) { 516 fwrite($fh, $buffer); 517 } else { 518 $data .= $buffer; 519 } 520 } 521 break; 522 523 case '8bit': 524 case '7bit': 525 default: 526 while (!feof($fd)) { 527 $buffer = fread($fd, 1048576); // 1 MB 528 if ($fh) { 529 fwrite($fh, $buffer); 530 } else { 531 $data .= $buffer; 532 } 533 } 534 } 535 536 fclose($fd); 537 538 if (!$fh) { 539 return $data; 540 } 541 } 542 543 /** 328 544 * Encodes data to quoted-printable standard. 329 545 * 330 * @param $input The data to encode 331 * @param $line_max Optional max line length. Should 332 * not be more than 76 chars 546 * @param string $input The data to encode 547 * @param int $line_max Optional max line length. Should 548 * not be more than 76 chars 549 * 550 * @return string Encoded data 333 551 * 334 552 * @access private … … 336 554 function _quotedPrintableEncode($input , $line_max = 76) 337 555 { 556 $eol = $this->_eol; 557 /* 558 // imap_8bit() is extremely fast, but doesn't handle properly some characters 559 if (function_exists('imap_8bit') && $line_max == 76) { 560 $input = preg_replace('/\r?\n/', "\r\n", $input); 561 $input = imap_8bit($input); 562 if ($eol != "\r\n") { 563 $input = str_replace("\r\n", $eol, $input); 564 } 565 return $input; 566 } 567 */ 338 568 $lines = preg_split("/\r?\n/", $input); 339 $eol = MAIL_MIMEPART_CRLF;340 569 $escape = '='; 341 570 $output = ''; 342 571 343 while (list(, $line) = each($lines)) { 344 345 $line = preg_split('||', $line, -1, PREG_SPLIT_NO_EMPTY); 346 $linlen = count($line); 572 while (list($idx, $line) = each($lines)) { 347 573 $newline = ''; 348 349 for ($i = 0; $i < $linlen; $i++) { 574 $i = 0; 575 576 while (isset($line[$i])) { 350 577 $char = $line[$i]; 351 578 $dec = ord($char); 352 353 if (($dec == 32) AND ($i == ($linlen - 1))) { // convert space at eol only 579 $i++; 580 581 if (($dec == 32) && (!isset($line[$i]))) { 582 // convert space at eol only 354 583 $char = '=20'; 355 356 } elseif (($dec == 9) AND ($i == ($linlen - 1))) { // convert tab at eol only 357 $char = '=09'; 358 } elseif ($dec == 9) { 359 ; // Do nothing if a tab. 360 } elseif (($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) { 361 $char = $escape . strtoupper(sprintf('%02s', dechex($dec))); 362 } elseif (($dec == 46) AND (($newline == '') || ((strlen($newline) + strlen("=2E")) >= $line_max))) { 363 //Bug #9722: convert full-stop at bol, 364 //some Windows servers need this, won't break anything (cipri) 365 //Bug #11731: full-stop at bol also needs to be encoded 366 //if this line would push us over the line_max limit. 584 } elseif ($dec == 9 && isset($line[$i])) { 585 ; // Do nothing if a TAB is not on eol 586 } elseif (($dec == 61) || ($dec < 32) || ($dec > 126)) { 587 $char = $escape . sprintf('%02X', $dec); 588 } elseif (($dec == 46) && (($newline == '') 589 || ((strlen($newline) + strlen("=2E")) >= $line_max)) 590 ) { 591 // Bug #9722: convert full-stop at bol, 592 // some Windows servers need this, won't break anything (cipri) 593 // Bug #11731: full-stop at bol also needs to be encoded 594 // if this line would push us over the line_max limit. 367 595 $char = '=2E'; 368 596 } 369 597 370 //Note, when changing this line, also change the ($dec == 46) 371 //check line, as it mimics this line due to Bug #11731 372 if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted 373 $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay 598 // Note, when changing this line, also change the ($dec == 46) 599 // check line, as it mimics this line due to Bug #11731 600 // EOL is not counted 601 if ((strlen($newline) + strlen($char)) >= $line_max) { 602 // soft line break; " =\r\n" is okay 603 $output .= $newline . $escape . $eol; 374 604 $newline = ''; 375 605 } … … 377 607 } // end of for 378 608 $output .= $newline . $eol; 379 } 380 $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf 609 unset($lines[$idx]); 610 } 611 // Don't want last crlf 612 $output = substr($output, 0, -1 * strlen($eol)); 381 613 return $output; 382 614 } 383 615 384 616 /** 385 * _buildHeaderParam()386 *387 617 * Encodes the paramater of a header. 388 618 * 389 * @param $name The name of the header-parameter 390 * @param $value The value of the paramter 391 * @param $charset The characterset of $value 392 * @param $language The language used in $value 393 * @param $paramEnc Parameter encoding type 394 * @param $maxLength The maximum length of a line. Defauls to 78 619 * @param string $name The name of the header-parameter 620 * @param string $value The value of the paramter 621 * @param string $charset The characterset of $value 622 * @param string $language The language used in $value 623 * @param string $encoding Parameter encoding. If not set, parameter value 624 * is encoded according to RFC2231 625 * @param int $maxLength The maximum length of a line. Defauls to 75 626 * 627 * @return string 395 628 * 396 629 * @access private 397 630 */ 398 function _buildHeaderParam($name, $value, $charset=NULL, $language=NULL, $paramEnc=NULL, $maxLength=78) 399 { 400 // RFC 2045: 631 function _buildHeaderParam($name, $value, $charset=null, $language=null, 632 $encoding=null, $maxLength=75 633 ) { 634 // RFC 2045: 401 635 // value needs encoding if contains non-ASCII chars or is longer than 78 chars 402 if (!preg_match('#[^\x20-\x7E]#', $value)) { // ASCII 403 // token 404 if (!preg_match('#([^\x21,\x23-\x27,\x2A,\x2B,\x2D,\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])#', $value)) { 405 if (strlen($name) + strlen($value) + 3 <= $maxLength) 406 return " {$name}={$value};"; 407 } else { // quoted-string 636 if (!preg_match('#[^\x20-\x7E]#', $value)) { 637 $token_regexp = '#([^\x21,\x23-\x27,\x2A,\x2B,\x2D' 638 . ',\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])#'; 639 if (!preg_match($token_regexp, $value)) { 640 // token 641 if (strlen($name) + strlen($value) + 3 <= $maxLength) { 642 return " {$name}={$value}"; 643 } 644 } else { 645 // quoted-string 408 646 $quoted = addcslashes($value, '\\"'); 409 if (strlen($name) + strlen($quoted) + 5 <= $maxLength) 410 return " {$name}=\"{$quoted}\";"; 647 if (strlen($name) + strlen($quoted) + 5 <= $maxLength) { 648 return " {$name}=\"{$quoted}\""; 649 } 411 650 } 412 651 } 413 652 414 653 // RFC2047: use quoted-printable/base64 encoding 415 if ($paramEnc == 'quoted-printable' || $paramEnc == 'base64') 416 return $this->_buildRFC2047Param($name, $value, $charset, $paramEnc); 654 if ($encoding == 'quoted-printable' || $encoding == 'base64') { 655 return $this->_buildRFC2047Param($name, $value, $charset, $encoding); 656 } 417 657 418 658 // RFC2231: 419 $encValue = preg_replace (420 ' #([^\x21,\x23,\x24,\x26,\x2B,\x2D,\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])#e',421 '"%" . strtoupper(dechex(ord("\1")))',422 $value);659 $encValue = preg_replace_callback( 660 '/([^\x21,\x23,\x24,\x26,\x2B,\x2D,\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])/', 661 array($this, '_encodeReplaceCallback'), $value 662 ); 423 663 $value = "$charset'$language'$encValue"; 424 664 425 $header = " {$name}*={$value} ;";665 $header = " {$name}*={$value}"; 426 666 if (strlen($header) <= $maxLength) { 427 667 return $header; … … 446 686 $headCount++; 447 687 } 448 $headers = implode(';' . MAIL_MIMEPART_CRLF, $headers) . ';'; 688 689 $headers = implode(';' . $this->_eol, $headers); 449 690 return $headers; 450 691 } 451 692 452 693 /** 453 * Encodes header parameter as per RFC2047 if needed (values too long will be truncated)454 * 455 * @param string $name The parameter name456 * @param string $value The parameter value457 * @param string $charset The parameter charset694 * Encodes header parameter as per RFC2047 if needed 695 * 696 * @param string $name The parameter name 697 * @param string $value The parameter value 698 * @param string $charset The parameter charset 458 699 * @param string $encoding Encoding type (quoted-printable or base64) 459 * @param int $maxLength Encoded parameter max length (75 is the value specified in the RFC)700 * @param int $maxLength Encoded parameter max length. Default: 76 460 701 * 461 702 * @return string Parameter line 462 703 * @access private 463 704 */ 464 function _buildRFC2047Param($name, $value, $charset, $encoding='quoted-printable', $maxLength=75) 465 { 705 function _buildRFC2047Param($name, $value, $charset, 706 $encoding='quoted-printable', $maxLength=76 707 ) { 466 708 // WARNING: RFC 2047 says: "An 'encoded-word' MUST NOT be used in 467 // parameter of a MIME Content-Type or Content-Disposition field" 709 // parameter of a MIME Content-Type or Content-Disposition field", 468 710 // but... it's supported by many clients/servers 469 470 if ($encoding == 'base64') 471 {711 $quoted = ''; 712 713 if ($encoding == 'base64') { 472 714 $value = base64_encode($value); 473 715 $prefix = '=?' . $charset . '?B?'; 474 716 $suffix = '?='; 475 $quoted = ''; 476 477 $add_len = strlen($prefix . $suffix) + strlen($name) + 6; // 2 x SPACE, 2 x '"', '=', ';'717 718 // 2 x SPACE, 2 x '"', '=', ';' 719 $add_len = strlen($prefix . $suffix) + strlen($name) + 6; 478 720 $len = $add_len + strlen($value); 479 721 … … 484 726 $value = substr($value, $real_len); 485 727 486 $quoted .= $prefix . $_quote . $suffix . MAIL_MIMEPART_CRLF. ' ';728 $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; 487 729 $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' 488 730 $len = strlen($value) + $add_len; … … 490 732 $quoted .= $prefix . $value . $suffix; 491 733 492 } 493 else // quoted-printable 494 { 495 // Replace all special characters used by the encoder. 496 $search = array('=', '_', '?', ' '); 497 $replace = array('=3D', '=5F', '=3F', '_'); 498 $value = str_replace($search, $replace, $value); 499 500 // Replace all extended characters (\x80-xFF) with their 501 // ASCII values. 502 $value = preg_replace('/([\x80-\xFF])/e', 503 '"=" . strtoupper(dechex(ord("\1")))', $value); 504 734 } else { 735 // quoted-printable 736 $value = $this->encodeQP($value); 505 737 $prefix = '=?' . $charset . '?Q?'; 506 738 $suffix = '?='; 507 739 508 $add_len = strlen($prefix . $suffix) + strlen($name) + 6; // 2 x SPACE, 2 x '"', '=', ';' 740 // 2 x SPACE, 2 x '"', '=', ';' 741 $add_len = strlen($prefix . $suffix) + strlen($name) + 6; 509 742 $len = $add_len + strlen($value); 510 743 511 744 while ($len > $maxLength) { 512 745 $length = $maxLength - $add_len; 513 // not break any encoded letters514 if (preg_match("/^(.{0,$length}[^\=][^\=])/", $value, $matches))746 // don't break any encoded letters 747 if (preg_match("/^(.{0,$length}[^\=][^\=])/", $value, $matches)) { 515 748 $_quote = $matches[1]; 516 517 $quoted .= $prefix . $_quote . $suffix . MAIL_MIMEPART_CRLF . ' '; 749 } 750 751 $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; 518 752 $value = substr($value, strlen($_quote)); 519 753 $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' … … 524 758 } 525 759 526 return " {$name}=\"{$quoted}\"; "; 760 return " {$name}=\"{$quoted}\""; 761 } 762 763 /** 764 * Callback function to replace extended characters (\x80-xFF) with their 765 * ASCII values (RFC2231) 766 * 767 * @param array $matches Preg_replace's matches array 768 * 769 * @return string Encoded character string 770 * @access private 771 */ 772 function _encodeReplaceCallback($matches) 773 { 774 return sprintf('%%%02X', ord($matches[1])); 775 } 776 777 /** 778 * Encodes the given string using quoted-printable 779 * 780 * @param string $str String to encode 781 * 782 * @return string Encoded string 783 * @access public 784 * @since 1.6.0 785 */ 786 function encodeQP($str) 787 { 788 // Replace all special characters used by the encoder 789 $search = array('=', '_', '?', ' '); 790 $replace = array('=3D', '=5F', '=3F', '_'); 791 $str = str_replace($search, $replace, $str); 792 793 // Replace all extended characters (\x80-xFF) with their 794 // ASCII values. 795 return preg_replace_callback( 796 '/([\x80-\xFF])/', array('Mail_mimePart', '_qpReplaceCallback'), $str 797 ); 798 } 799 800 /** 801 * Callback function to replace extended characters (\x80-xFF) with their 802 * ASCII values (RFC2047: quoted-printable) 803 * 804 * @param array $matches Preg_replace's matches array 805 * 806 * @return string Encoded character string 807 * @access private 808 */ 809 function _qpReplaceCallback($matches) 810 { 811 return sprintf('=%02X', ord($matches[1])); 527 812 } 528 813
Note: See TracChangeset
for help on using the changeset viewer.
