web/lib/Zend/Mime/Decode.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Mime
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version    $Id: Decode.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    20  */
       
    21 
       
    22 /**
       
    23  * @see Zend_Mime
       
    24  */
       
    25 require_once 'Zend/Mime.php';
       
    26 
       
    27 /**
       
    28  * @category   Zend
       
    29  * @package    Zend_Mime
       
    30  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    31  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    32  */
       
    33 class Zend_Mime_Decode
       
    34 {
       
    35     /**
       
    36      * Explode MIME multipart string into seperate parts
       
    37      *
       
    38      * Parts consist of the header and the body of each MIME part.
       
    39      *
       
    40      * @param  string $body     raw body of message
       
    41      * @param  string $boundary boundary as found in content-type
       
    42      * @return array parts with content of each part, empty if no parts found
       
    43      * @throws Zend_Exception
       
    44      */
       
    45     public static function splitMime($body, $boundary)
       
    46     {
       
    47         // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r?
       
    48         $body = str_replace("\r", '', $body);
       
    49 
       
    50         $start = 0;
       
    51         $res = array();
       
    52         // find every mime part limiter and cut out the
       
    53         // string before it.
       
    54         // the part before the first boundary string is discarded:
       
    55         $p = strpos($body, '--' . $boundary . "\n", $start);
       
    56         if ($p === false) {
       
    57             // no parts found!
       
    58             return array();
       
    59         }
       
    60 
       
    61         // position after first boundary line
       
    62         $start = $p + 3 + strlen($boundary);
       
    63 
       
    64         while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
       
    65             $res[] = substr($body, $start, $p-$start);
       
    66             $start = $p + 3 + strlen($boundary);
       
    67         }
       
    68 
       
    69         // no more parts, find end boundary
       
    70         $p = strpos($body, '--' . $boundary . '--', $start);
       
    71         if ($p===false) {
       
    72             throw new Zend_Exception('Not a valid Mime Message: End Missing');
       
    73         }
       
    74 
       
    75         // the remaining part also needs to be parsed:
       
    76         $res[] = substr($body, $start, $p-$start);
       
    77         return $res;
       
    78     }
       
    79 
       
    80     /**
       
    81      * decodes a mime encoded String and returns a
       
    82      * struct of parts with header and body
       
    83      *
       
    84      * @param  string $message  raw message content
       
    85      * @param  string $boundary boundary as found in content-type
       
    86      * @param  string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
       
    87      * @return array|null parts as array('header' => array(name => value), 'body' => content), null if no parts found
       
    88      * @throws Zend_Exception
       
    89      */
       
    90     public static function splitMessageStruct($message, $boundary, $EOL = Zend_Mime::LINEEND)
       
    91     {
       
    92         $parts = self::splitMime($message, $boundary);
       
    93         if (count($parts) <= 0) {
       
    94             return null;
       
    95         }
       
    96         $result = array();
       
    97         foreach ($parts as $part) {
       
    98             self::splitMessage($part, $headers, $body, $EOL);
       
    99             $result[] = array('header' => $headers,
       
   100                               'body'   => $body    );
       
   101         }
       
   102         return $result;
       
   103     }
       
   104 
       
   105     /**
       
   106      * split a message in header and body part, if no header or an
       
   107      * invalid header is found $headers is empty
       
   108      *
       
   109      * The charset of the returned headers depend on your iconv settings.
       
   110      *
       
   111      * @param  string $message raw message with header and optional content
       
   112      * @param  array  $headers output param, array with headers as array(name => value)
       
   113      * @param  string $body    output param, content of message
       
   114      * @param  string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
       
   115      * @return null
       
   116      */
       
   117     public static function splitMessage($message, &$headers, &$body, $EOL = Zend_Mime::LINEEND)
       
   118     {
       
   119         // check for valid header at first line
       
   120         $firstline = strtok($message, "\n");
       
   121         if (!preg_match('%^[^\s]+[^:]*:%', $firstline)) {
       
   122             $headers = array();
       
   123             // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r?
       
   124             $body = str_replace(array("\r", "\n"), array('', $EOL), $message);
       
   125             return;
       
   126         }
       
   127 
       
   128         // find an empty line between headers and body
       
   129         // default is set new line
       
   130         if (strpos($message, $EOL . $EOL)) {
       
   131             list($headers, $body) = explode($EOL . $EOL, $message, 2);
       
   132         // next is the standard new line
       
   133         } else if ($EOL != "\r\n" && strpos($message, "\r\n\r\n")) {
       
   134             list($headers, $body) = explode("\r\n\r\n", $message, 2);
       
   135         // next is the other "standard" new line
       
   136         } else if ($EOL != "\n" && strpos($message, "\n\n")) {
       
   137             list($headers, $body) = explode("\n\n", $message, 2);
       
   138         // at last resort find anything that looks like a new line
       
   139         } else {
       
   140             @list($headers, $body) = @preg_split("%([\r\n]+)\\1%U", $message, 2);
       
   141         }
       
   142 
       
   143         $headers = iconv_mime_decode_headers($headers, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
       
   144 
       
   145         if ($headers === false ) {
       
   146             // an error occurs during the decoding
       
   147             return;
       
   148         }
       
   149 
       
   150         // normalize header names
       
   151         foreach ($headers as $name => $header) {
       
   152             $lower = strtolower($name);
       
   153             if ($lower == $name) {
       
   154                 continue;
       
   155             }
       
   156             unset($headers[$name]);
       
   157             if (!isset($headers[$lower])) {
       
   158                 $headers[$lower] = $header;
       
   159                 continue;
       
   160             }
       
   161             if (is_array($headers[$lower])) {
       
   162                 $headers[$lower][] = $header;
       
   163                 continue;
       
   164             }
       
   165             $headers[$lower] = array($headers[$lower], $header);
       
   166         }
       
   167     }
       
   168 
       
   169     /**
       
   170      * split a content type in its different parts
       
   171      *
       
   172      * @param  string $type       content-type
       
   173      * @param  string $wantedPart the wanted part, else an array with all parts is returned
       
   174      * @return string|array wanted part or all parts as array('type' => content-type, partname => value)
       
   175      */
       
   176     public static function splitContentType($type, $wantedPart = null)
       
   177     {
       
   178         return self::splitHeaderField($type, $wantedPart, 'type');
       
   179     }
       
   180 
       
   181     /**
       
   182      * split a header field like content type in its different parts
       
   183      *
       
   184      * @param  string $type       header field
       
   185      * @param  string $wantedPart the wanted part, else an array with all parts is returned
       
   186      * @param  string $firstName  key name for the first part
       
   187      * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
       
   188      * @throws Zend_Exception
       
   189      */
       
   190     public static function splitHeaderField($field, $wantedPart = null, $firstName = 0)
       
   191     {
       
   192         $wantedPart = strtolower($wantedPart);
       
   193         $firstName = strtolower($firstName);
       
   194 
       
   195         // special case - a bit optimized
       
   196         if ($firstName === $wantedPart) {
       
   197             $field = strtok($field, ';');
       
   198             return $field[0] == '"' ? substr($field, 1, -1) : $field;
       
   199         }
       
   200 
       
   201         $field = $firstName . '=' . $field;
       
   202         if (!preg_match_all('%([^=\s]+)\s*=\s*("[^"]+"|[^;]+)(;\s*|$)%', $field, $matches)) {
       
   203             throw new Zend_Exception('not a valid header field');
       
   204         }
       
   205 
       
   206         if ($wantedPart) {
       
   207             foreach ($matches[1] as $key => $name) {
       
   208                 if (strcasecmp($name, $wantedPart)) {
       
   209                     continue;
       
   210                 }
       
   211                 if ($matches[2][$key][0] != '"') {
       
   212                     return $matches[2][$key];
       
   213                 }
       
   214                 return substr($matches[2][$key], 1, -1);
       
   215             }
       
   216             return null;
       
   217         }
       
   218 
       
   219         $split = array();
       
   220         foreach ($matches[1] as $key => $name) {
       
   221             $name = strtolower($name);
       
   222             if ($matches[2][$key][0] == '"') {
       
   223                 $split[$name] = substr($matches[2][$key], 1, -1);
       
   224             } else {
       
   225                 $split[$name] = $matches[2][$key];
       
   226             }
       
   227         }
       
   228 
       
   229         return $split;
       
   230     }
       
   231 
       
   232     /**
       
   233      * decode a quoted printable encoded string
       
   234      *
       
   235      * The charset of the returned string depends on your iconv settings.
       
   236      *
       
   237      * @param  string encoded string
       
   238      * @return string decoded string
       
   239      */
       
   240     public static function decodeQuotedPrintable($string)
       
   241     {
       
   242         return iconv_mime_decode($string, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
       
   243     }
       
   244 }