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