File indexing completed on 2024-12-22 05:37:17
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Mime 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Support class for MultiPart Mime Messages 0024 * 0025 * @category Zend 0026 * @package Zend_Mime 0027 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0028 * @license http://framework.zend.com/license/new-bsd New BSD License 0029 */ 0030 class Zend_Mime 0031 { 0032 const TYPE_OCTETSTREAM = 'application/octet-stream'; 0033 const TYPE_TEXT = 'text/plain'; 0034 const TYPE_HTML = 'text/html'; 0035 const ENCODING_7BIT = '7bit'; 0036 const ENCODING_8BIT = '8bit'; 0037 const ENCODING_QUOTEDPRINTABLE = 'quoted-printable'; 0038 const ENCODING_BASE64 = 'base64'; 0039 const DISPOSITION_ATTACHMENT = 'attachment'; 0040 const DISPOSITION_INLINE = 'inline'; 0041 const LINELENGTH = 72; 0042 const LINEEND = "\n"; 0043 const MULTIPART_ALTERNATIVE = 'multipart/alternative'; 0044 const MULTIPART_MIXED = 'multipart/mixed'; 0045 const MULTIPART_RELATED = 'multipart/related'; 0046 0047 /** 0048 * Boundary 0049 * 0050 * @var null|string 0051 */ 0052 protected $_boundary; 0053 0054 /** 0055 * @var int 0056 */ 0057 protected static $makeUnique = 0; 0058 0059 /** 0060 * Lookup-Tables for QuotedPrintable 0061 * 0062 * @var array 0063 */ 0064 public static $qpKeys = array( 0065 "\x00", 0066 "\x01", 0067 "\x02", 0068 "\x03", 0069 "\x04", 0070 "\x05", 0071 "\x06", 0072 "\x07", 0073 "\x08", 0074 "\x09", 0075 "\x0A", 0076 "\x0B", 0077 "\x0C", 0078 "\x0D", 0079 "\x0E", 0080 "\x0F", 0081 "\x10", 0082 "\x11", 0083 "\x12", 0084 "\x13", 0085 "\x14", 0086 "\x15", 0087 "\x16", 0088 "\x17", 0089 "\x18", 0090 "\x19", 0091 "\x1A", 0092 "\x1B", 0093 "\x1C", 0094 "\x1D", 0095 "\x1E", 0096 "\x1F", 0097 "\x7F", 0098 "\x80", 0099 "\x81", 0100 "\x82", 0101 "\x83", 0102 "\x84", 0103 "\x85", 0104 "\x86", 0105 "\x87", 0106 "\x88", 0107 "\x89", 0108 "\x8A", 0109 "\x8B", 0110 "\x8C", 0111 "\x8D", 0112 "\x8E", 0113 "\x8F", 0114 "\x90", 0115 "\x91", 0116 "\x92", 0117 "\x93", 0118 "\x94", 0119 "\x95", 0120 "\x96", 0121 "\x97", 0122 "\x98", 0123 "\x99", 0124 "\x9A", 0125 "\x9B", 0126 "\x9C", 0127 "\x9D", 0128 "\x9E", 0129 "\x9F", 0130 "\xA0", 0131 "\xA1", 0132 "\xA2", 0133 "\xA3", 0134 "\xA4", 0135 "\xA5", 0136 "\xA6", 0137 "\xA7", 0138 "\xA8", 0139 "\xA9", 0140 "\xAA", 0141 "\xAB", 0142 "\xAC", 0143 "\xAD", 0144 "\xAE", 0145 "\xAF", 0146 "\xB0", 0147 "\xB1", 0148 "\xB2", 0149 "\xB3", 0150 "\xB4", 0151 "\xB5", 0152 "\xB6", 0153 "\xB7", 0154 "\xB8", 0155 "\xB9", 0156 "\xBA", 0157 "\xBB", 0158 "\xBC", 0159 "\xBD", 0160 "\xBE", 0161 "\xBF", 0162 "\xC0", 0163 "\xC1", 0164 "\xC2", 0165 "\xC3", 0166 "\xC4", 0167 "\xC5", 0168 "\xC6", 0169 "\xC7", 0170 "\xC8", 0171 "\xC9", 0172 "\xCA", 0173 "\xCB", 0174 "\xCC", 0175 "\xCD", 0176 "\xCE", 0177 "\xCF", 0178 "\xD0", 0179 "\xD1", 0180 "\xD2", 0181 "\xD3", 0182 "\xD4", 0183 "\xD5", 0184 "\xD6", 0185 "\xD7", 0186 "\xD8", 0187 "\xD9", 0188 "\xDA", 0189 "\xDB", 0190 "\xDC", 0191 "\xDD", 0192 "\xDE", 0193 "\xDF", 0194 "\xE0", 0195 "\xE1", 0196 "\xE2", 0197 "\xE3", 0198 "\xE4", 0199 "\xE5", 0200 "\xE6", 0201 "\xE7", 0202 "\xE8", 0203 "\xE9", 0204 "\xEA", 0205 "\xEB", 0206 "\xEC", 0207 "\xED", 0208 "\xEE", 0209 "\xEF", 0210 "\xF0", 0211 "\xF1", 0212 "\xF2", 0213 "\xF3", 0214 "\xF4", 0215 "\xF5", 0216 "\xF6", 0217 "\xF7", 0218 "\xF8", 0219 "\xF9", 0220 "\xFA", 0221 "\xFB", 0222 "\xFC", 0223 "\xFD", 0224 "\xFE", 0225 "\xFF" 0226 ); 0227 0228 /** 0229 * @var array 0230 */ 0231 public static $qpReplaceValues = array( 0232 "=00", 0233 "=01", 0234 "=02", 0235 "=03", 0236 "=04", 0237 "=05", 0238 "=06", 0239 "=07", 0240 "=08", 0241 "=09", 0242 "=0A", 0243 "=0B", 0244 "=0C", 0245 "=0D", 0246 "=0E", 0247 "=0F", 0248 "=10", 0249 "=11", 0250 "=12", 0251 "=13", 0252 "=14", 0253 "=15", 0254 "=16", 0255 "=17", 0256 "=18", 0257 "=19", 0258 "=1A", 0259 "=1B", 0260 "=1C", 0261 "=1D", 0262 "=1E", 0263 "=1F", 0264 "=7F", 0265 "=80", 0266 "=81", 0267 "=82", 0268 "=83", 0269 "=84", 0270 "=85", 0271 "=86", 0272 "=87", 0273 "=88", 0274 "=89", 0275 "=8A", 0276 "=8B", 0277 "=8C", 0278 "=8D", 0279 "=8E", 0280 "=8F", 0281 "=90", 0282 "=91", 0283 "=92", 0284 "=93", 0285 "=94", 0286 "=95", 0287 "=96", 0288 "=97", 0289 "=98", 0290 "=99", 0291 "=9A", 0292 "=9B", 0293 "=9C", 0294 "=9D", 0295 "=9E", 0296 "=9F", 0297 "=A0", 0298 "=A1", 0299 "=A2", 0300 "=A3", 0301 "=A4", 0302 "=A5", 0303 "=A6", 0304 "=A7", 0305 "=A8", 0306 "=A9", 0307 "=AA", 0308 "=AB", 0309 "=AC", 0310 "=AD", 0311 "=AE", 0312 "=AF", 0313 "=B0", 0314 "=B1", 0315 "=B2", 0316 "=B3", 0317 "=B4", 0318 "=B5", 0319 "=B6", 0320 "=B7", 0321 "=B8", 0322 "=B9", 0323 "=BA", 0324 "=BB", 0325 "=BC", 0326 "=BD", 0327 "=BE", 0328 "=BF", 0329 "=C0", 0330 "=C1", 0331 "=C2", 0332 "=C3", 0333 "=C4", 0334 "=C5", 0335 "=C6", 0336 "=C7", 0337 "=C8", 0338 "=C9", 0339 "=CA", 0340 "=CB", 0341 "=CC", 0342 "=CD", 0343 "=CE", 0344 "=CF", 0345 "=D0", 0346 "=D1", 0347 "=D2", 0348 "=D3", 0349 "=D4", 0350 "=D5", 0351 "=D6", 0352 "=D7", 0353 "=D8", 0354 "=D9", 0355 "=DA", 0356 "=DB", 0357 "=DC", 0358 "=DD", 0359 "=DE", 0360 "=DF", 0361 "=E0", 0362 "=E1", 0363 "=E2", 0364 "=E3", 0365 "=E4", 0366 "=E5", 0367 "=E6", 0368 "=E7", 0369 "=E8", 0370 "=E9", 0371 "=EA", 0372 "=EB", 0373 "=EC", 0374 "=ED", 0375 "=EE", 0376 "=EF", 0377 "=F0", 0378 "=F1", 0379 "=F2", 0380 "=F3", 0381 "=F4", 0382 "=F5", 0383 "=F6", 0384 "=F7", 0385 "=F8", 0386 "=F9", 0387 "=FA", 0388 "=FB", 0389 "=FC", 0390 "=FD", 0391 "=FE", 0392 "=FF" 0393 ); 0394 0395 /** 0396 * @var string 0397 */ 0398 public static $qpKeysString = 0399 "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; 0400 0401 /** 0402 * Check if the given string is "printable" 0403 * 0404 * Checks that a string contains no unprintable characters. If this returns 0405 * false, encode the string for secure delivery. 0406 * 0407 * @param string $str 0408 * @return boolean 0409 */ 0410 public static function isPrintable($str) 0411 { 0412 return (strcspn($str, self::$qpKeysString) == strlen($str)); 0413 } 0414 0415 /** 0416 * Encode a given string with the QUOTED_PRINTABLE mechanism and wrap the lines. 0417 * 0418 * @param string $str 0419 * @param int $lineLength Line length; defaults to {@link LINELENGTH} 0420 * @param string $lineEnd Line end; defaults to {@link LINEEND} 0421 * @return string 0422 */ 0423 public static function encodeQuotedPrintable( 0424 $str, 0425 $lineLength = self::LINELENGTH, 0426 $lineEnd = self::LINEEND 0427 ) 0428 { 0429 $out = ''; 0430 $str = self::_encodeQuotedPrintable($str); 0431 0432 // Split encoded text into separate lines 0433 while (strlen($str) > 0) { 0434 $ptr = strlen($str); 0435 if ($ptr > $lineLength) { 0436 $ptr = $lineLength; 0437 } 0438 0439 // Ensure we are not splitting across an encoded character 0440 $pos = strrpos(substr($str, 0, $ptr), '='); 0441 if ($pos !== false && $pos >= $ptr - 2) { 0442 $ptr = $pos; 0443 } 0444 0445 // Check if there is a space at the end of the line and rewind 0446 if ($ptr > 0 && $str[$ptr - 1] == ' ') { 0447 --$ptr; 0448 } 0449 0450 // Add string and continue 0451 $out .= substr($str, 0, $ptr) . '=' . $lineEnd; 0452 $str = substr($str, $ptr); 0453 } 0454 0455 $out = rtrim($out, $lineEnd); 0456 $out = rtrim($out, '='); 0457 0458 return $out; 0459 } 0460 0461 /** 0462 * Converts a string into quoted printable format. 0463 * 0464 * @param string $str 0465 * @return string 0466 */ 0467 private static function _encodeQuotedPrintable($str) 0468 { 0469 $str = str_replace('=', '=3D', $str); 0470 $str = str_replace(self::$qpKeys, self::$qpReplaceValues, $str); 0471 $str = rtrim($str); 0472 0473 return $str; 0474 } 0475 0476 /** 0477 * Encode a given string with the QUOTED_PRINTABLE mechanism for Mail Headers. 0478 * 0479 * Mail headers depend on an extended quoted printable algorithm otherwise 0480 * a range of bugs can occur. 0481 * 0482 * @param string $str 0483 * @param string $charset 0484 * @param int $lineLength Line length; defaults to {@link LINELENGTH} 0485 * @param string $lineEnd Line end; defaults to {@link LINEEND} 0486 * @return string 0487 */ 0488 public static function encodeQuotedPrintableHeader( 0489 $str, $charset, $lineLength = self::LINELENGTH, $lineEnd = self::LINEEND 0490 ) 0491 { 0492 // Reduce line-length by the length of the required delimiter, charsets and encoding 0493 $prefix = sprintf('=?%s?Q?', $charset); 0494 $lineLength = $lineLength - strlen($prefix) - 3; 0495 0496 $str = self::_encodeQuotedPrintable($str); 0497 0498 // Mail-Header required chars have to be encoded also: 0499 $str = str_replace( 0500 array('?', ' ', '_', ','), array('=3F', '=20', '=5F', '=2C'), $str 0501 ); 0502 0503 // initialize first line, we need it anyways 0504 $lines = array(0 => ""); 0505 0506 // Split encoded text into separate lines 0507 $tmp = ""; 0508 while (strlen($str) > 0) { 0509 $currentLine = max(count($lines) - 1, 0); 0510 $token = self::getNextQuotedPrintableToken($str); 0511 $str = substr($str, strlen($token)); 0512 0513 $tmp .= $token; 0514 if ($token == '=20') { 0515 // only if we have a single char token or space, we can append the 0516 // tempstring it to the current line or start a new line if necessary. 0517 if (strlen($lines[$currentLine] . $tmp) > $lineLength) { 0518 $lines[$currentLine + 1] = $tmp; 0519 } else { 0520 $lines[$currentLine] .= $tmp; 0521 } 0522 $tmp = ""; 0523 } 0524 // don't forget to append the rest to the last line 0525 if (strlen($str) == 0) { 0526 $lines[$currentLine] .= $tmp; 0527 } 0528 } 0529 0530 // assemble the lines together by pre- and appending delimiters, charset, encoding. 0531 for ($i = 0; $i < count($lines); $i++) { 0532 $lines[$i] = " " . $prefix . $lines[$i] . "?="; 0533 } 0534 $str = trim(implode($lineEnd, $lines)); 0535 0536 return $str; 0537 } 0538 0539 /** 0540 * Retrieves the first token from a quoted printable string. 0541 * 0542 * @param string $str 0543 * @return string 0544 */ 0545 private static function getNextQuotedPrintableToken($str) 0546 { 0547 if (substr($str, 0, 1) == "=") { 0548 $token = substr($str, 0, 3); 0549 } else { 0550 $token = substr($str, 0, 1); 0551 } 0552 0553 return $token; 0554 } 0555 0556 /** 0557 * Encode a given string in mail header compatible base64 encoding. 0558 * 0559 * @param string $str 0560 * @param string $charset 0561 * @param int $lineLength Line length; defaults to {@link LINELENGTH} 0562 * @param string $lineEnd Line end; defaults to {@link LINEEND} 0563 * @return string 0564 */ 0565 public static function encodeBase64Header( 0566 $str, $charset, $lineLength = self::LINELENGTH, $lineEnd = self::LINEEND 0567 ) 0568 { 0569 $prefix = '=?' . $charset . '?B?'; 0570 $suffix = '?='; 0571 $remainingLength = $lineLength - strlen($prefix) - strlen($suffix); 0572 0573 $encodedValue = self::encodeBase64($str, $remainingLength, $lineEnd); 0574 $encodedValue = str_replace( 0575 $lineEnd, $suffix . $lineEnd . ' ' . $prefix, $encodedValue 0576 ); 0577 $encodedValue = $prefix . $encodedValue . $suffix; 0578 0579 return $encodedValue; 0580 } 0581 0582 /** 0583 * Encode a given string in base64 encoding and break lines 0584 * according to the maximum linelength. 0585 * 0586 * @param string $str 0587 * @param int $lineLength Line length; defaults to {@link LINELENGTH} 0588 * @param string $lineEnd Line end; defaults to {@link LINEEND} 0589 * @return string 0590 */ 0591 public static function encodeBase64( 0592 $str, $lineLength = self::LINELENGTH, $lineEnd = self::LINEEND 0593 ) 0594 { 0595 return rtrim(chunk_split(base64_encode($str), $lineLength, $lineEnd)); 0596 } 0597 0598 /** 0599 * Constructor 0600 * 0601 * @param null|string $boundary 0602 */ 0603 public function __construct($boundary = null) 0604 { 0605 // This string needs to be somewhat unique 0606 if ($boundary === null) { 0607 $this->_boundary = '=_' . md5(microtime(1) . self::$makeUnique++); 0608 } else { 0609 $this->_boundary = $boundary; 0610 } 0611 } 0612 0613 /** 0614 * Encode the given string with the given encoding. 0615 * 0616 * @param string $str 0617 * @param string $encoding 0618 * @param string $EOL Line end; defaults to {@link Zend_Mime::LINEEND} 0619 * @return string 0620 */ 0621 public static function encode($str, $encoding, $EOL = self::LINEEND) 0622 { 0623 switch ($encoding) { 0624 case self::ENCODING_BASE64: 0625 return self::encodeBase64($str, self::LINELENGTH, $EOL); 0626 0627 case self::ENCODING_QUOTEDPRINTABLE: 0628 return self::encodeQuotedPrintable($str, self::LINELENGTH, $EOL); 0629 0630 default: 0631 /** 0632 * @todo 7Bit and 8Bit is currently handled the same way. 0633 */ 0634 return $str; 0635 } 0636 } 0637 0638 /** 0639 * Return a MIME boundary 0640 * 0641 * @access public 0642 * @return string 0643 */ 0644 public function boundary() 0645 { 0646 return $this->_boundary; 0647 } 0648 0649 /** 0650 * Return a MIME boundary line 0651 * 0652 * @param string $EOL Line end; defaults to {@link LINEEND} 0653 * @return string 0654 */ 0655 public function boundaryLine($EOL = self::LINEEND) 0656 { 0657 return $EOL . '--' . $this->_boundary . $EOL; 0658 } 0659 0660 /** 0661 * Return MIME ending 0662 * 0663 * @param string $EOL Line end; defaults to {@link LINEEND} 0664 * @return string 0665 */ 0666 public function mimeEnd($EOL = self::LINEEND) 0667 { 0668 return $EOL . '--' . $this->_boundary . '--' . $EOL; 0669 } 0670 }