File indexing completed on 2025-02-09 07:20:15
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 * @see Zend_Mime 0024 */ 0025 // require_once 'Zend/Mime.php'; 0026 0027 /** 0028 * @category Zend 0029 * @package Zend_Mime 0030 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0031 * @license http://framework.zend.com/license/new-bsd New BSD License 0032 */ 0033 class Zend_Mime_Decode 0034 { 0035 /** 0036 * Explode MIME multipart string into seperate parts 0037 * 0038 * Parts consist of the header and the body of each MIME part. 0039 * 0040 * @param string $body raw body of message 0041 * @param string $boundary boundary as found in content-type 0042 * @return array parts with content of each part, empty if no parts found 0043 * @throws Zend_Exception 0044 */ 0045 public static function splitMime($body, $boundary) 0046 { 0047 // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r? 0048 $body = str_replace("\r", '', $body); 0049 0050 $start = 0; 0051 $res = array(); 0052 // find every mime part limiter and cut out the 0053 // string before it. 0054 // the part before the first boundary string is discarded: 0055 $p = strpos($body, '--' . $boundary . "\n", $start); 0056 if ($p === false) { 0057 // no parts found! 0058 return array(); 0059 } 0060 0061 // position after first boundary line 0062 $start = $p + 3 + strlen($boundary); 0063 0064 while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) { 0065 $res[] = substr($body, $start, $p-$start); 0066 $start = $p + 3 + strlen($boundary); 0067 } 0068 0069 // no more parts, find end boundary 0070 $p = strpos($body, '--' . $boundary . '--', $start); 0071 if ($p === false) { 0072 throw new Zend_Exception('Not a valid Mime Message: End Missing'); 0073 } 0074 0075 // the remaining part also needs to be parsed: 0076 $res[] = substr($body, $start, $p - $start); 0077 0078 return $res; 0079 } 0080 0081 /** 0082 * decodes a mime encoded String and returns a 0083 * struct of parts with header and body 0084 * 0085 * @param string $message raw message content 0086 * @param string $boundary boundary as found in content-type 0087 * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND} 0088 * @return array|null parts as array('header' => array(name => value), 'body' => content), null if no parts found 0089 * @throws Zend_Exception 0090 */ 0091 public static function splitMessageStruct( 0092 $message, $boundary, $EOL = Zend_Mime::LINEEND 0093 ) 0094 { 0095 $parts = self::splitMime($message, $boundary); 0096 if (count($parts) <= 0) { 0097 return null; 0098 } 0099 $result = array(); 0100 foreach ($parts as $part) { 0101 self::splitMessage($part, $headers, $body, $EOL); 0102 $result[] = array( 0103 'header' => $headers, 0104 'body' => $body 0105 ); 0106 } 0107 0108 return $result; 0109 } 0110 0111 /** 0112 * split a message in header and body part, if no header or an 0113 * invalid header is found $headers is empty 0114 * 0115 * The charset of the returned headers depend on your iconv settings. 0116 * 0117 * @param string $message raw message with header and optional content 0118 * @param array $headers output param, array with headers as array(name => value) 0119 * @param string $body output param, content of message 0120 * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND} 0121 * @return null 0122 */ 0123 public static function splitMessage( 0124 $message, &$headers, &$body, $EOL = Zend_Mime::LINEEND 0125 ) 0126 { 0127 // check for valid header at first line 0128 $firstline = strtok($message, "\n"); 0129 if (!preg_match('%^[^\s]+[^:]*:%', $firstline)) { 0130 $headers = array(); 0131 // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r? 0132 $body = str_replace( 0133 array( 0134 "\r", 0135 "\n" 0136 ), array( 0137 '', 0138 $EOL 0139 ), $message 0140 ); 0141 0142 return; 0143 } 0144 0145 // find an empty line between headers and body 0146 // default is set new line 0147 if (strpos($message, $EOL . $EOL)) { 0148 list($headers, $body) = explode($EOL . $EOL, $message, 2); 0149 // next is the standard new line 0150 } else { 0151 if ($EOL != "\r\n" && strpos($message, "\r\n\r\n")) { 0152 list($headers, $body) = explode("\r\n\r\n", $message, 2); 0153 // next is the other "standard" new line 0154 } else { 0155 if ($EOL != "\n" && strpos($message, "\n\n")) { 0156 list($headers, $body) = explode("\n\n", $message, 2); 0157 // at last resort find anything that looks like a new line 0158 } else { 0159 @list($headers, $body) = 0160 @preg_split("%([\r\n]+)\\1%U", $message, 2); 0161 } 0162 } 0163 } 0164 0165 $headers = iconv_mime_decode_headers( 0166 $headers, ICONV_MIME_DECODE_CONTINUE_ON_ERROR 0167 ); 0168 0169 if ($headers === false) { 0170 // an error occurs during the decoding 0171 return; 0172 } 0173 0174 // normalize header names 0175 foreach ($headers as $name => $header) { 0176 $lower = strtolower($name); 0177 if ($lower == $name) { 0178 continue; 0179 } 0180 unset($headers[$name]); 0181 if (!isset($headers[$lower])) { 0182 $headers[$lower] = $header; 0183 continue; 0184 } 0185 if (is_array($headers[$lower])) { 0186 $headers[$lower][] = $header; 0187 continue; 0188 } 0189 $headers[$lower] = array( 0190 $headers[$lower], 0191 $header 0192 ); 0193 } 0194 } 0195 0196 /** 0197 * split a content type in its different parts 0198 * 0199 * @param string $type content-type 0200 * @param string $wantedPart the wanted part, else an array with all parts is returned 0201 * @return string|array wanted part or all parts as array('type' => content-type, partname => value) 0202 */ 0203 public static function splitContentType($type, $wantedPart = null) 0204 { 0205 return self::splitHeaderField($type, $wantedPart, 'type'); 0206 } 0207 0208 /** 0209 * split a header field like content type in its different parts 0210 * 0211 * @param string $field 0212 * @param string $wantedPart the wanted part, else an array with all parts is returned 0213 * @param int|string $firstName key name for the first part 0214 * @throws Zend_Exception 0215 * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value) 0216 */ 0217 public static function splitHeaderField( 0218 $field, $wantedPart = null, $firstName = 0 0219 ) 0220 { 0221 $wantedPart = strtolower($wantedPart); 0222 $firstName = strtolower($firstName); 0223 0224 // special case - a bit optimized 0225 if ($firstName === $wantedPart) { 0226 $field = strtok($field, ';'); 0227 0228 return $field[0] == '"' ? substr($field, 1, -1) : $field; 0229 } 0230 0231 $field = $firstName . '=' . $field; 0232 if (!preg_match_all('%([^=\s]+)\s*=\s*("[^"]+"|[^;]+)(;\s*|$)%', $field, $matches)) { 0233 throw new Zend_Exception('not a valid header field'); 0234 } 0235 0236 if ($wantedPart) { 0237 foreach ($matches[1] as $key => $name) { 0238 if (strcasecmp($name, $wantedPart)) { 0239 continue; 0240 } 0241 if ($matches[2][$key][0] != '"') { 0242 return $matches[2][$key]; 0243 } 0244 0245 return substr($matches[2][$key], 1, -1); 0246 } 0247 0248 return null; 0249 } 0250 0251 $split = array(); 0252 foreach ($matches[1] as $key => $name) { 0253 $name = strtolower($name); 0254 if ($matches[2][$key][0] == '"') { 0255 $split[$name] = substr($matches[2][$key], 1, -1); 0256 } else { 0257 $split[$name] = $matches[2][$key]; 0258 } 0259 } 0260 0261 return $split; 0262 } 0263 0264 /** 0265 * decode a quoted printable encoded string 0266 * 0267 * The charset of the returned string depends on your iconv settings. 0268 * 0269 * @param string $string Encoded string 0270 * @return string Decoded string 0271 */ 0272 public static function decodeQuotedPrintable($string) 0273 { 0274 return quoted_printable_decode($string); 0275 } 0276 }