Ignore:
Timestamp:
06/15/11 15:52:23 (13 years ago)
Author:
emersonfaria
Message:

Ticket #1963 - Foram implementadas correcoes em varias funcoes do backend imap e no mimeDecode.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • contrib/z-push/include/mimeDecode.php

    r3754 r4613  
    102102class Mail_mimeDecode 
    103103{ 
    104         /** 
    105         * The raw email to decode 
    106         * 
    107         * @var    string 
    108         * @access private 
    109         */ 
    110         var $_input; 
    111  
    112         /** 
    113         * The header part of the input 
    114         * 
    115         * @var    string 
    116         * @access private 
    117         */ 
    118         var $_header; 
    119  
    120         /** 
    121         * The body part of the input 
    122         * 
    123         * @var    string 
    124         * @access private 
    125         */ 
    126         var $_body; 
    127  
    128         /** 
    129         * If an error occurs, this is used to store the message 
    130         * 
    131         * @var    string 
    132         * @access private 
    133         */ 
    134         var $_error; 
    135  
    136         /** 
    137         * Flag to determine whether to include bodies in the 
    138         * returned object. 
    139         * 
    140         * @var    boolean 
    141         * @access private 
    142         */ 
    143         var $_include_bodies; 
    144  
    145         /** 
    146         * Flag to determine whether to decode bodies 
    147         * 
    148         * @var    boolean 
    149         * @access private 
    150         */ 
    151         var $_decode_bodies; 
    152  
    153         /** 
    154         * Flag to determine whether to decode headers 
    155         * 
    156         * @var    boolean 
    157         * @access private 
    158         */ 
    159         var $_decode_headers; 
    160  
    161         /** 
    162         * Flag to determine whether to include attached messages 
    163         * as body in the returned object. Depends on $_include_bodies 
    164         * 
    165         * @var    boolean 
    166         * @access private 
    167         */ 
    168         var $_rfc822_bodies; 
    169  
    170         /** 
    171         * Constructor. 
    172         * 
    173         * Sets up the object, initialise the variables, and splits and 
    174         * stores the header and body of the input. 
    175         * 
    176         * @param string The input to decode 
    177         * @access public 
    178         */ 
    179         function Mail_mimeDecode($input, $deprecated_linefeed = '') 
    180         { 
    181                 list($header, $body)   = $this->_splitBodyHeader($input); 
    182  
    183                 $this->_input          = $input; 
    184                 $this->_header         = $header; 
    185                 $this->_body           = $body; 
    186                 $this->_decode_bodies  = false; 
    187                 $this->_include_bodies = true; 
    188                 $this->_rfc822_bodies  = false; 
    189         } 
    190  
    191         /** 
    192         * Begins the decoding process. If called statically 
    193         * it will create an object and call the decode() method 
    194         * of it. 
    195         * 
    196         * @param array An array of various parameters that determine 
    197         *              various things: 
    198         *              include_bodies - Whether to include the body in the returned 
    199         *                               object. 
    200         *              decode_bodies  - Whether to decode the bodies 
    201         *                               of the parts. (Transfer encoding) 
    202         *              decode_headers - Whether to decode headers 
    203         *              input          - If called statically, this will be treated 
    204         *                               as the input 
    205         *              charset        - convert all data to this charset 
    206         * @return object Decoded results 
    207         * @access public 
    208         */ 
    209         function decode($params = null) 
    210         { 
    211                 // determine if this method has been called statically 
    212                 $isStatic = !(isset($this) && get_class($this) == __CLASS__); 
    213  
    214                 // Have we been called statically? 
    215                 // If so, create an object and pass details to that. 
    216                 if ($isStatic AND isset($params['input'])) { 
    217  
    218                         $obj = new Mail_mimeDecode($params['input']); 
    219                         $structure = $obj->decode($params); 
    220  
    221                         // Called statically but no input 
    222                 } elseif ($isStatic) { 
    223                         return $this->raiseError('Called statically and no input given'); 
    224  
    225                         // Called via an object 
    226                 } else { 
    227                         $this->_include_bodies = isset($params['include_bodies']) ? 
    228                         $params['include_bodies'] : false; 
    229                         $this->_decode_bodies  = isset($params['decode_bodies']) ? 
    230                         $params['decode_bodies']  : false; 
    231                         $this->_decode_headers = isset($params['decode_headers']) ? 
    232                         $params['decode_headers'] : false; 
    233                         $this->_rfc822_bodies  = isset($params['rfc_822bodies']) ? 
    234                         $params['rfc_822bodies']  : false; 
    235                         $this->_charset = isset($params['charset']) ? 
    236                         $params['charset'] : 'utf-8'; 
    237                           
    238                         $structure = $this->_decode($this->_header, $this->_body); 
    239                         if ($structure === false) { 
    240                                 $structure = $this->raiseError($this->_error); 
    241                         } 
    242                 } 
    243  
    244                 return $structure; 
    245         } 
    246  
    247         /** 
    248         * Performs the decoding. Decodes the body string passed to it 
    249         * If it finds certain content-types it will call itself in a 
    250         * recursive fashion 
    251         * 
    252         * @param string Header section 
    253         * @param string Body section 
    254         * @return object Results of decoding process 
    255         * @access private 
    256         */ 
    257         function _decode($headers, $body, $default_ctype = 'text/plain') 
    258         { 
    259                 $return = new stdClass; 
    260                 $return->headers = array(); 
    261                 $headers = $this->_parseHeaders($headers); 
    262  
    263                 foreach ($headers as $value) { 
    264                         if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { 
    265                                 $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]); 
    266                                 $return->headers[strtolower($value['name'])][] = $value['value']; 
    267  
    268                         } elseif (isset($return->headers[strtolower($value['name'])])) { 
    269                                 $return->headers[strtolower($value['name'])][] = $value['value']; 
    270  
    271                         } else { 
    272                                 $return->headers[strtolower($value['name'])] = $value['value']; 
    273                         } 
    274                 } 
    275  
    276                 reset($headers); 
    277                 while (list($key, $value) = each($headers)) { 
    278                         $headers[$key]['name'] = strtolower($headers[$key]['name']); 
    279                         switch ($headers[$key]['name']) { 
    280  
    281                                 case 'content-type': 
    282                                         $content_type = $this->_parseHeaderValue($headers[$key]['value']); 
    283  
    284                                         if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) { 
    285                                                 $return->ctype_primary   = $regs[1]; 
    286                                                 $return->ctype_secondary = $regs[2]; 
    287                                         } 
    288  
    289                                         if (isset($content_type['other'])) { 
    290                                                 while (list($p_name, $p_value) = each($content_type['other'])) { 
    291                                                         $return->ctype_parameters[$p_name] = $p_value; 
    292                                                 } 
    293                                         } 
    294                                         break; 
    295  
    296                                 case 'content-disposition': 
    297                                         $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); 
    298                                         $return->disposition   = $content_disposition['value']; 
    299                                         if (isset($content_disposition['other'])) { 
    300                                                 while (list($p_name, $p_value) = each($content_disposition['other'])) { 
    301                                                         $return->d_parameters[$p_name] = $p_value; 
    302                                                 } 
    303                                         } 
    304                                         break; 
    305  
    306                                 case 'content-transfer-encoding': 
    307                                         $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']); 
    308                                         break; 
    309                         } 
    310                 } 
    311  
    312                 if (isset($content_type)) { 
    313                         switch (strtolower($content_type['value'])) { 
    314                                 case 'text/plain': 
    315                                         $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
    316                                         $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
    317                                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
    318                                         break; 
    319  
    320                                 case 'text/html': 
    321                                         $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
    322                                         $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
    323                                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
    324                                         break; 
    325  
    326                                 case 'multipart/parallel': 
    327                                 case 'multipart/appledouble': // Appledouble mail 
    328                                 case 'multipart/report': // RFC1892 
    329                                 case 'multipart/signed': // PGP 
    330                                 case 'multipart/digest': 
    331                                 case 'multipart/alternative': 
    332                                 case 'multipart/related': 
    333                                 case 'multipart/mixed': 
    334                                         if(!isset($content_type['other']['boundary'])){ 
    335                                                 $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; 
    336                                                 return false; 
    337                                         } 
    338  
    339                                         $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain'; 
    340  
    341                                         $parts = $this->_boundarySplit($body, $content_type['other']['boundary']); 
    342                                         for ($i = 0; $i < count($parts); $i++) { 
    343                                                 list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]); 
    344                                                 $part = $this->_decode($part_header, $part_body, $default_ctype); 
    345                                                 if($part === false) 
    346                                                 $part = $this->raiseError($this->_error); 
    347                                                 $return->parts[] = $part; 
    348                                         } 
    349                                         break; 
    350  
    351                                 case 'message/rfc822': 
    352                                         if ($this->_rfc822_bodies) { 
    353                                                 $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
    354                                                 $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
    355                                                 $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body); 
    356                                         } 
    357  
    358                                         $obj = new Mail_mimeDecode($body); 
    359                                         $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, 
     104    /** 
     105    * The raw email to decode 
     106    * 
     107    * @var    string 
     108    * @access private 
     109    */ 
     110    var $_input; 
     111 
     112    /** 
     113    * The header part of the input 
     114    * 
     115    * @var    string 
     116    * @access private 
     117    */ 
     118    var $_header; 
     119 
     120    /** 
     121    * The body part of the input 
     122    * 
     123    * @var    string 
     124    * @access private 
     125    */ 
     126    var $_body; 
     127 
     128    /** 
     129    * If an error occurs, this is used to store the message 
     130    * 
     131    * @var    string 
     132    * @access private 
     133    */ 
     134    var $_error; 
     135 
     136    /** 
     137    * Flag to determine whether to include bodies in the 
     138    * returned object. 
     139    * 
     140    * @var    boolean 
     141    * @access private 
     142    */ 
     143    var $_include_bodies; 
     144 
     145    /** 
     146    * Flag to determine whether to decode bodies 
     147    * 
     148    * @var    boolean 
     149    * @access private 
     150    */ 
     151    var $_decode_bodies; 
     152 
     153    /** 
     154    * Flag to determine whether to decode headers 
     155    * 
     156    * @var    boolean 
     157    * @access private 
     158    */ 
     159    var $_decode_headers; 
     160 
     161    /** 
     162    * Flag to determine whether to include attached messages 
     163    * as body in the returned object. Depends on $_include_bodies 
     164    * 
     165    * @var    boolean 
     166    * @access private 
     167    */ 
     168    var $_rfc822_bodies; 
     169 
     170    /** 
     171    * Constructor. 
     172    * 
     173    * Sets up the object, initialise the variables, and splits and 
     174    * stores the header and body of the input. 
     175    * 
     176    * @param string The input to decode 
     177    * @access public 
     178    */ 
     179    function Mail_mimeDecode($input, $deprecated_linefeed = '') 
     180    { 
     181        list($header, $body)   = $this->_splitBodyHeader($input); 
     182 
     183        $this->_input          = $input; 
     184        $this->_header         = $header; 
     185        $this->_body           = $body; 
     186        $this->_decode_bodies  = false; 
     187        $this->_include_bodies = true; 
     188        $this->_rfc822_bodies  = false; 
     189    } 
     190 
     191    /** 
     192    * Begins the decoding process. If called statically 
     193    * it will create an object and call the decode() method 
     194    * of it. 
     195    * 
     196    * @param array An array of various parameters that determine 
     197    *              various things: 
     198    *              include_bodies - Whether to include the body in the returned 
     199    *                               object. 
     200    *              decode_bodies  - Whether to decode the bodies 
     201    *                               of the parts. (Transfer encoding) 
     202    *              decode_headers - Whether to decode headers 
     203    *              input          - If called statically, this will be treated 
     204    *                               as the input 
     205    *              charset        - convert all data to this charset 
     206    * @return object Decoded results 
     207    * @access public 
     208    */ 
     209    function decode($params = null) 
     210    { 
     211        // determine if this method has been called statically 
     212        $isStatic = !(isset($this) && get_class($this) == __CLASS__); 
     213 
     214        // Have we been called statically? 
     215        // If so, create an object and pass details to that. 
     216        if ($isStatic AND isset($params['input'])) { 
     217 
     218            $obj = new Mail_mimeDecode($params['input']); 
     219            $structure = $obj->decode($params); 
     220 
     221        // Called statically but no input 
     222        } elseif ($isStatic) { 
     223            return $this->raiseError('Called statically and no input given'); 
     224 
     225        // Called via an object 
     226        } else { 
     227            $this->_include_bodies = isset($params['include_bodies']) ? 
     228                                 $params['include_bodies'] : false; 
     229            $this->_decode_bodies  = isset($params['decode_bodies']) ? 
     230                                 $params['decode_bodies']  : false; 
     231            $this->_decode_headers = isset($params['decode_headers']) ? 
     232                                 $params['decode_headers'] : false; 
     233            $this->_rfc822_bodies  = isset($params['rfc_822bodies']) ? 
     234                                 $params['rfc_822bodies']  : false; 
     235            $this->_charset = isset($params['charset']) ? 
     236                                 strtolower($params['charset']) : 'utf-8'; 
     237 
     238            $structure = $this->_decode($this->_header, $this->_body); 
     239            if ($structure === false) { 
     240                $structure = $this->raiseError($this->_error); 
     241            } 
     242        } 
     243 
     244        return $structure; 
     245    } 
     246 
     247    /** 
     248    * Performs the decoding. Decodes the body string passed to it 
     249    * If it finds certain content-types it will call itself in a 
     250    * recursive fashion 
     251    * 
     252    * @param string Header section 
     253    * @param string Body section 
     254    * @return object Results of decoding process 
     255    * @access private 
     256    */ 
     257    function _decode($headers, $body, $default_ctype = 'text/plain') 
     258    { 
     259        $return = new stdClass; 
     260        $return->headers = array(); 
     261        $headers = $this->_parseHeaders($headers); 
     262 
     263        foreach ($headers as $value) { 
     264            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { 
     265                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]); 
     266                $return->headers[strtolower($value['name'])][] = $value['value']; 
     267 
     268            } elseif (isset($return->headers[strtolower($value['name'])])) { 
     269                $return->headers[strtolower($value['name'])][] = $value['value']; 
     270 
     271            } else { 
     272                $return->headers[strtolower($value['name'])] = $value['value']; 
     273            } 
     274        } 
     275 
     276        reset($headers); 
     277        while (list($key, $value) = each($headers)) { 
     278            $headers[$key]['name'] = strtolower($headers[$key]['name']); 
     279            switch ($headers[$key]['name']) { 
     280 
     281                case 'content-type': 
     282                    $content_type = $this->_parseHeaderValue($headers[$key]['value']); 
     283 
     284                    if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) { 
     285                        $return->ctype_primary   = $regs[1]; 
     286                        $return->ctype_secondary = $regs[2]; 
     287                    } 
     288 
     289                    if (isset($content_type['other'])) { 
     290                        while (list($p_name, $p_value) = each($content_type['other'])) { 
     291                            $return->ctype_parameters[$p_name] = $p_value; 
     292                        } 
     293                    } 
     294                    break; 
     295 
     296                case 'content-disposition': 
     297                    $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); 
     298                    $return->disposition   = $content_disposition['value']; 
     299                    if (isset($content_disposition['other'])) { 
     300                        while (list($p_name, $p_value) = each($content_disposition['other'])) { 
     301                            $return->d_parameters[$p_name] = $p_value; 
     302                        } 
     303                    } 
     304                    break; 
     305 
     306                case 'content-transfer-encoding': 
     307                    $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']); 
     308                    break; 
     309            } 
     310        } 
     311 
     312        if (isset($content_type)) { 
     313            switch (strtolower($content_type['value'])) { 
     314                case 'text/plain': 
     315                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     316                    $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
     317                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
     318                    break; 
     319 
     320                case 'text/html': 
     321                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     322                    $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
     323                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
     324                    break; 
     325 
     326                case 'multipart/parallel': 
     327                case 'multipart/appledouble': // Appledouble mail 
     328                case 'multipart/report': // RFC1892 
     329                case 'multipart/signed': // PGP 
     330                case 'multipart/digest': 
     331                case 'multipart/alternative': 
     332                case 'multipart/related': 
     333                case 'multipart/mixed': 
     334                    if(!isset($content_type['other']['boundary'])){ 
     335                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; 
     336                        return false; 
     337                    } 
     338 
     339                    $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain'; 
     340 
     341                    $parts = $this->_boundarySplit($body, $content_type['other']['boundary']); 
     342                    for ($i = 0; $i < count($parts); $i++) { 
     343                        list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]); 
     344                        $part = $this->_decode($part_header, $part_body, $default_ctype); 
     345                        if($part === false) 
     346                            $part = $this->raiseError($this->_error); 
     347                        $return->parts[] = $part; 
     348                    } 
     349                    break; 
     350 
     351                case 'message/rfc822': 
     352                    if ($this->_rfc822_bodies) { 
     353                        $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     354                        $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
     355                        $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body); 
     356                    } 
     357 
     358                    $obj = new Mail_mimeDecode($body); 
     359                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, 
    360360                                                          'decode_bodies'  => $this->_decode_bodies, 
    361361                                                          'decode_headers' => $this->_decode_headers)); 
    362                                         unset($obj); 
    363                                         break; 
    364  
    365                                 default: 
    366                                         if(!isset($content_transfer_encoding['value'])) 
    367                                         $content_transfer_encoding['value'] = '7bit'; 
    368                                         // if there is no explicit charset, then don't try to convert to default charset 
    369                                         $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : ''; 
    370                                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null; 
    371                                         break; 
    372                         } 
    373  
    374                 } else { 
    375                         $ctype = explode('/', $default_ctype); 
    376                         $return->ctype_primary   = $ctype[0]; 
    377                         $return->ctype_secondary = $ctype[1]; 
    378                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; 
    379                 } 
    380  
    381                 return $return; 
    382         } 
    383  
    384         /** 
    385         * Given the output of the above function, this will return an 
    386         * array of references to the parts, indexed by mime number. 
    387         * 
    388         * @param  object $structure   The structure to go through 
    389         * @param  string $mime_number Internal use only. 
    390         * @return array               Mime numbers 
    391         */ 
    392         function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') 
    393         { 
    394                 $return = array(); 
    395                 if (!empty($structure->parts)) { 
    396                         if ($mime_number != '') { 
    397                                 $structure->mime_id = $prepend . $mime_number; 
    398                                 $return[$prepend . $mime_number] = &$structure; 
    399                         } 
    400                         for ($i = 0; $i < count($structure->parts); $i++) { 
    401  
    402  
    403                                 if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { 
    404                                         $prepend      = $prepend . $mime_number . '.'; 
    405                                         $_mime_number = ''; 
    406                                 } else { 
    407                                         $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); 
    408                                 } 
    409  
    410                                 $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); 
    411                                 foreach ($arr as $key => $val) { 
    412                                         $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; 
    413                                 } 
    414                         } 
    415                 } else { 
    416                         if ($mime_number == '') { 
    417                                 $mime_number = '1'; 
    418                         } 
    419                         $structure->mime_id = $prepend . $mime_number; 
    420                         $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; 
    421                 } 
    422  
    423                 return $return; 
    424         } 
    425  
    426         /** 
    427         * Given a string containing a header and body 
    428         * section, this function will split them (at the first 
    429         * blank line) and return them. 
    430         * 
    431         * @param string Input to split apart 
    432         * @return array Contains header and body section 
    433         * @access private 
    434         */ 
    435         function _splitBodyHeader($input) 
    436         { 
    437                 if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { 
    438                         return array($match[1], $match[2]); 
    439                 } 
    440                 $this->_error = 'Could not split header and body'; 
    441                 return false; 
    442         } 
    443  
    444         /** 
    445         * Parse headers given in $input and return 
    446         * as assoc array. 
    447         * 
    448         * @param string Headers to parse 
    449         * @return array Contains parsed headers 
    450         * @access private 
    451         */ 
    452         function _parseHeaders($input) 
    453         { 
    454  
    455                 if ($input !== '') { 
    456                         // Unfold the input 
    457                         $input   = preg_replace("/\r?\n/", "\r\n", $input); 
    458                         $input   = preg_replace("/\r\n(\t| )+/", ' ', $input); 
    459                         $headers = explode("\r\n", trim($input)); 
    460  
    461                         foreach ($headers as $value) { 
    462                                 $hdr_name = substr($value, 0, $pos = strpos($value, ':')); 
    463                                 $hdr_value = substr($value, $pos+1); 
    464                                 if($hdr_value[0] == ' ') 
    465                                 $hdr_value = substr($hdr_value, 1); 
    466  
    467                                 $return[] = array( 
     362                    unset($obj); 
     363                    break; 
     364 
     365                default: 
     366                    if(!isset($content_transfer_encoding['value'])) 
     367                        $content_transfer_encoding['value'] = '7bit'; 
     368                    // if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted 
     369                    $charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) )? $return->ctype_parameters['charset']: ''; 
     370                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null; 
     371                    break; 
     372            } 
     373 
     374        } else { 
     375            $ctype = explode('/', $default_ctype); 
     376            $return->ctype_primary   = $ctype[0]; 
     377            $return->ctype_secondary = $ctype[1]; 
     378            $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; 
     379        } 
     380 
     381        return $return; 
     382    } 
     383 
     384    /** 
     385    * Given the output of the above function, this will return an 
     386    * array of references to the parts, indexed by mime number. 
     387    * 
     388    * @param  object $structure   The structure to go through 
     389    * @param  string $mime_number Internal use only. 
     390    * @return array               Mime numbers 
     391    */ 
     392    function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') 
     393    { 
     394        $return = array(); 
     395        if (!empty($structure->parts)) { 
     396            if ($mime_number != '') { 
     397                $structure->mime_id = $prepend . $mime_number; 
     398                $return[$prepend . $mime_number] = &$structure; 
     399            } 
     400            for ($i = 0; $i < count($structure->parts); $i++) { 
     401 
     402 
     403                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { 
     404                    $prepend      = $prepend . $mime_number . '.'; 
     405                    $_mime_number = ''; 
     406                } else { 
     407                    $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); 
     408                } 
     409 
     410                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); 
     411                foreach ($arr as $key => $val) { 
     412                    $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; 
     413                } 
     414            } 
     415        } else { 
     416            if ($mime_number == '') { 
     417                $mime_number = '1'; 
     418            } 
     419            $structure->mime_id = $prepend . $mime_number; 
     420            $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; 
     421        } 
     422 
     423        return $return; 
     424    } 
     425 
     426    /** 
     427    * Given a string containing a header and body 
     428    * section, this function will split them (at the first 
     429    * blank line) and return them. 
     430    * 
     431    * @param string Input to split apart 
     432    * @return array Contains header and body section 
     433    * @access private 
     434    */ 
     435    function _splitBodyHeader($input) 
     436    { 
     437        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { 
     438            return array($match[1], $match[2]); 
     439        } 
     440        $this->_error = 'Could not split header and body'; 
     441        return false; 
     442    } 
     443 
     444    /** 
     445    * Parse headers given in $input and return 
     446    * as assoc array. 
     447    * 
     448    * @param string Headers to parse 
     449    * @return array Contains parsed headers 
     450    * @access private 
     451    */ 
     452    function _parseHeaders($input) 
     453    { 
     454 
     455        if ($input !== '') { 
     456            // Unfold the input 
     457            $input   = preg_replace("/\r?\n/", "\r\n", $input); 
     458            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input); 
     459            $headers = explode("\r\n", trim($input)); 
     460 
     461            foreach ($headers as $value) { 
     462                $hdr_name = substr($value, 0, $pos = strpos($value, ':')); 
     463                $hdr_value = substr($value, $pos+1); 
     464                if($hdr_value[0] == ' ') 
     465                    $hdr_value = substr($hdr_value, 1); 
     466 
     467                $return[] = array( 
    468468                                  'name'  => $hdr_name, 
    469469                                  'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value 
    470                                 ); 
    471                         } 
    472                 } else { 
    473                         $return = array(); 
    474                 } 
    475  
    476                 return $return; 
    477         } 
    478  
    479         /** 
    480         * Function to parse a header value, 
    481         * extract first part, and any secondary 
    482         * parts (after ;) This function is not as 
    483         * robust as it could be. Eg. header comments 
    484         * in the wrong place will probably break it. 
    485         * 
    486         * @param string Header value to parse 
    487         * @return array Contains parsed result 
    488         * @access private 
    489         */ 
    490         function _parseHeaderValue($input) 
    491         { 
    492  
    493                 if (($pos = strpos($input, ';')) !== false) { 
    494  
    495                         $return['value'] = trim(substr($input, 0, $pos)); 
    496                         $input = trim(substr($input, $pos+1)); 
    497  
    498                         if (strlen($input) > 0) { 
    499  
    500                                 // This splits on a semi-colon, if there's no preceeding backslash 
    501                                 // Now works with quoted values; had to glue the \; breaks in PHP 
    502                                 // the regex is already bordering on incomprehensible 
    503                                 //$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
    504                                 // simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex 
    505                                 $splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
    506                                 preg_match_all($splitRegex, $input, $matches); 
    507  
    508                                 $parameters = array(); 
    509                                 for ($i=0; $i<count($matches[0]); $i++) { 
    510                                         $param = $matches[0][$i]; 
    511                                         while (substr($param, -2) == '\;') { 
    512                                                 $param .= $matches[0][++$i]; 
    513                                         } 
    514                                         $parameters[] = $param; 
    515                                 } 
    516  
    517                                 for ($i = 0; $i < count($parameters); $i++) { 
    518                                         $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); 
    519                                         $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); 
    520                                         if (!empty($param_value[0]) && $param_value[0] == '"') { 
    521                                                 $param_value = substr($param_value, 1, -1); 
    522                                         } 
    523                                         $return['other'][$param_name] = $param_value; 
    524                                         $return['other'][strtolower($param_name)] = $param_value; 
    525                                 } 
    526                         } 
    527                 } else { 
    528                         $return['value'] = trim($input); 
    529                 } 
    530  
    531                 return $return; 
    532         } 
    533  
    534         /** 
    535         * This function splits the input based 
    536         * on the given boundary 
    537         * 
    538         * @param string Input to parse 
    539         * @return array Contains array of resulting mime parts 
    540         * @access private 
    541         */ 
    542         function _boundarySplit($input, $boundary) 
    543         { 
    544                 $parts = array(); 
    545  
    546                 $bs_possible = substr($boundary, 2, -2); 
    547                 $bs_check = '\"' . $bs_possible . '\"'; 
    548  
    549                 if ($boundary == $bs_check) { 
    550                         $boundary = $bs_possible; 
    551                 } 
    552  
    553                 $tmp = explode('--' . $boundary, $input); 
    554  
    555                 for ($i = 1; $i < count($tmp) - 1; $i++) { 
    556                         $parts[] = $tmp[$i]; 
    557                 } 
    558  
    559                 return $parts; 
    560         } 
    561  
    562         /** 
    563         * Given a header, this function will decode it 
    564         * according to RFC2047. Probably not *exactly* 
    565         * conformant, but it does pass all the given 
    566         * examples (in RFC2047). 
    567         * 
    568         * @param string Input header value to decode 
    569         * @return string Decoded header value 
    570         * @access private 
    571         */ 
    572         function _decodeHeader($input) 
    573         { 
    574                 // Remove white space between encoded-words 
    575                 $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); 
    576  
    577                 // For each encoded-word... 
    578                 while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { 
    579  
    580                         $encoded  = $matches[1]; 
    581                         $charset  = $matches[2]; 
    582                         $encoding = $matches[3]; 
    583                         $text     = $matches[4]; 
    584  
    585                         switch (strtolower($encoding)) { 
    586                                 case 'b': 
    587                                         $text = base64_decode($text); 
    588                                         break; 
    589  
    590                                 case 'q': 
    591                                         $text = str_replace('_', ' ', $text); 
    592                                         preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); 
    593                                         foreach($matches[1] as $value) 
    594                                         $text = str_replace('='.$value, chr(hexdec($value)), $text); 
    595                                         break; 
    596                         } 
    597  
    598                         $input = str_replace($encoded, $this->_fromCharset($charset, $text), $input); 
    599                 } 
    600  
    601                 return $input; 
    602         } 
    603  
    604         /** 
    605         * Given a body string and an encoding type, 
    606         * this function will decode and return it. 
    607         * 
    608         * @param  string Input body to decode 
    609         * @param  string Encoding type to use. 
    610         * @return string Decoded body 
    611         * @access private 
    612         */ 
    613         function _decodeBody($input, $encoding = '7bit', $charset = '') 
    614         { 
    615                 switch (strtolower($encoding)) { 
    616                         case '7bit': 
    617                                 return $this->_fromCharset($charset, $input);; 
    618                                 break; 
    619  
    620                         case '8bit': 
    621                                 return $this->_fromCharset($charset, $input); 
    622                                 break; 
    623  
    624                         case 'quoted-printable': 
    625                                 return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input)); 
    626                                 break; 
    627  
    628                         case 'base64': 
    629                                 return $this->_fromCharset($charset, base64_decode($input)); 
    630                                 break; 
    631  
    632                         default: 
    633                                 return $input; 
    634                 } 
    635         } 
    636  
    637         /** 
    638         * Given a quoted-printable string, this 
    639         * function will decode and return it. 
    640         * 
    641         * @param  string Input body to decode 
    642         * @return string Decoded body 
    643         * @access private 
    644         */ 
    645         function _quotedPrintableDecode($input) 
    646         { 
    647                 // Remove soft line breaks 
    648                 $input = preg_replace("/=\r?\n/", '', $input); 
    649  
    650                 // Replace encoded characters 
    651                 $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input); 
    652  
    653                 return $input; 
    654         } 
    655  
    656         /** 
    657         * Checks the input for uuencoded files and returns 
    658         * an array of them. Can be called statically, eg: 
    659         * 
    660         * $files =& Mail_mimeDecode::uudecode($some_text); 
    661         * 
    662         * It will check for the begin 666 ... end syntax 
    663         * however and won't just blindly decode whatever you 
    664         * pass it. 
    665         * 
    666         * @param  string Input body to look for attahcments in 
    667         * @return array  Decoded bodies, filenames and permissions 
    668         * @access public 
    669         * @author Unknown 
    670         */ 
    671         function &uudecode($input) 
    672         { 
    673                 // Find all uuencoded sections 
    674                 preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); 
    675  
    676                 for ($j = 0; $j < count($matches[3]); $j++) { 
    677  
    678                         $str      = $matches[3][$j]; 
    679                         $filename = $matches[2][$j]; 
    680                         $fileperm = $matches[1][$j]; 
    681  
    682                         $file = ''; 
    683                         $str = preg_split("/\r?\n/", trim($str)); 
    684                         $strlen = count($str); 
    685  
    686                         for ($i = 0; $i < $strlen; $i++) { 
    687                                 $pos = 1; 
    688                                 $d = 0; 
    689                                 $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); 
    690  
    691                                 while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { 
    692                                         $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
    693                                         $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
    694                                         $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
    695                                         $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); 
    696                                         $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
    697  
    698                                         $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
    699  
    700                                         $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077)); 
    701  
    702                                         $pos += 4; 
    703                                         $d += 3; 
    704                                 } 
    705  
    706                                 if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { 
    707                                         $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
    708                                         $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
    709                                         $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
    710                                         $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
    711  
    712                                         $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
    713  
    714                                         $pos += 3; 
    715                                         $d += 2; 
    716                                 } 
    717  
    718                                 if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { 
    719                                         $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
    720                                         $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
    721                                         $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
    722  
    723                                 } 
    724                         } 
    725                         $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); 
    726                 } 
    727  
    728                 return $files; 
    729         } 
    730  
    731         /** 
    732         * getSendArray() returns the arguments required for Mail::send() 
    733         * used to build the arguments for a mail::send() call 
    734         * 
    735         * Usage: 
    736         * $mailtext = Full email (for example generated by a template) 
    737         * $decoder = new Mail_mimeDecode($mailtext); 
    738         * $parts =  $decoder->getSendArray(); 
    739         * if (!PEAR::isError($parts) { 
    740         *     list($recipents,$headers,$body) = $parts; 
    741         *     $mail = Mail::factory('smtp'); 
    742         *     $mail->send($recipents,$headers,$body); 
    743         * } else { 
    744         *     echo $parts->message; 
    745         * } 
    746         * @return mixed   array of recipeint, headers,body or Pear_Error 
    747         * @access public 
    748         * @author Alan Knowles <alan@akbkhome.com> 
    749         */ 
    750         function getSendArray() 
    751         { 
    752                 // prevent warning if this is not set 
    753                 $this->_decode_headers = FALSE; 
    754                 $headerlist =$this->_parseHeaders($this->_header); 
    755                 $to = ""; 
    756                 if (!$headerlist) { 
    757                         return $this->raiseError("Message did not contain headers"); 
    758                 } 
    759                 foreach($headerlist as $item) { 
    760                         $header[$item['name']] = $item['value']; 
    761                         switch (strtolower($item['name'])) { 
    762                                 case "to": 
    763                                 case "cc": 
    764                                 case "bcc": 
    765                                         $to .= ",".$item['value']; 
    766                                 default: 
    767                                         break; 
    768                         } 
    769                 } 
    770                 if ($to == "") { 
    771                         return $this->raiseError("Message did not contain any recipents"); 
    772                 } 
    773                 $to = substr($to,1); 
    774                 return array($to,$header,$this->_body); 
    775         } 
    776  
    777         /** 
    778         * Returns a xml copy of the output of 
    779         * Mail_mimeDecode::decode. Pass the output in as the 
    780         * argument. This function can be called statically. Eg: 
    781         * 
    782         * $output = $obj->decode(); 
    783         * $xml    = Mail_mimeDecode::getXML($output); 
    784         * 
    785         * The DTD used for this should have been in the package. Or 
    786         * alternatively you can get it from cvs, or here: 
    787         * http://www.phpguru.org/xmail/xmail.dtd. 
    788         * 
    789         * @param  object Input to convert to xml. This should be the 
    790         *                output of the Mail_mimeDecode::decode function 
    791         * @return string XML version of input 
    792         * @access public 
    793         */ 
    794         function getXML($input) 
    795         { 
    796                 $crlf    =  "\r\n"; 
    797                 $output  = '<?xml version=\'1.0\'?>' . $crlf . 
     470                                 ); 
     471            } 
     472        } else { 
     473            $return = array(); 
     474        } 
     475 
     476        return $return; 
     477    } 
     478 
     479    /** 
     480    * Function to parse a header value, 
     481    * extract first part, and any secondary 
     482    * parts (after ;) This function is not as 
     483    * robust as it could be. Eg. header comments 
     484    * in the wrong place will probably break it. 
     485    * 
     486    * @param string Header value to parse 
     487    * @return array Contains parsed result 
     488    * @access private 
     489    */ 
     490    function _parseHeaderValue($input) 
     491    { 
     492 
     493        if (($pos = strpos($input, ';')) !== false) { 
     494 
     495            $return['value'] = trim(substr($input, 0, $pos)); 
     496            $input = trim(substr($input, $pos+1)); 
     497 
     498            if (strlen($input) > 0) { 
     499 
     500                // This splits on a semi-colon, if there's no preceeding backslash 
     501                // Now works with quoted values; had to glue the \; breaks in PHP 
     502                // the regex is already bordering on incomprehensible 
     503                //$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
     504                // simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex 
     505                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
     506                preg_match_all($splitRegex, $input, $matches); 
     507 
     508                $parameters = array(); 
     509                for ($i=0; $i<count($matches[0]); $i++) { 
     510                    $param = $matches[0][$i]; 
     511                    while (substr($param, -2) == '\;') { 
     512                        $param .= $matches[0][++$i]; 
     513                    } 
     514                    $parameters[] = $param; 
     515                } 
     516 
     517                for ($i = 0; $i < count($parameters); $i++) { 
     518                    $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); 
     519                    $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); 
     520                    if (!empty($param_value[0]) && $param_value[0] == '"') { 
     521                        $param_value = substr($param_value, 1, -1); 
     522                    } 
     523                    $return['other'][$param_name] = $param_value; 
     524                    $return['other'][strtolower($param_name)] = $param_value; 
     525                } 
     526            } 
     527        } else { 
     528            $return['value'] = trim($input); 
     529        } 
     530 
     531        return $return; 
     532    } 
     533 
     534    /** 
     535    * This function splits the input based 
     536    * on the given boundary 
     537    * 
     538    * @param string Input to parse 
     539    * @return array Contains array of resulting mime parts 
     540    * @access private 
     541    */ 
     542    function _boundarySplit($input, $boundary) 
     543    { 
     544        $parts = array(); 
     545 
     546        $bs_possible = substr($boundary, 2, -2); 
     547        $bs_check = '\"' . $bs_possible . '\"'; 
     548 
     549        if ($boundary == $bs_check) { 
     550            $boundary = $bs_possible; 
     551        } 
     552 
     553        $tmp = explode('--' . $boundary, $input); 
     554 
     555        for ($i = 1; $i < count($tmp) - 1; $i++) { 
     556            $parts[] = $tmp[$i]; 
     557        } 
     558 
     559        return $parts; 
     560    } 
     561 
     562    /** 
     563    * Given a header, this function will decode it 
     564    * according to RFC2047. Probably not *exactly* 
     565    * conformant, but it does pass all the given 
     566    * examples (in RFC2047). 
     567    * 
     568    * @param string Input header value to decode 
     569    * @return string Decoded header value 
     570    * @access private 
     571    */ 
     572    function _decodeHeader($input) 
     573    { 
     574        // Remove white space between encoded-words 
     575        $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); 
     576 
     577        // For each encoded-word... 
     578        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { 
     579 
     580            $encoded  = $matches[1]; 
     581            $charset  = $matches[2]; 
     582            $encoding = $matches[3]; 
     583            $text     = $matches[4]; 
     584 
     585            switch (strtolower($encoding)) { 
     586                case 'b': 
     587                    $text = base64_decode($text); 
     588                    break; 
     589 
     590                case 'q': 
     591                    $text = str_replace('_', ' ', $text); 
     592                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); 
     593                    foreach($matches[1] as $value) 
     594                        $text = str_replace('='.$value, chr(hexdec($value)), $text); 
     595                    break; 
     596            } 
     597 
     598            $input = str_replace($encoded, $this->_fromCharset($charset, $text), $input); 
     599        } 
     600 
     601        return $input; 
     602    } 
     603 
     604    /** 
     605    * Given a body string and an encoding type, 
     606    * this function will decode and return it. 
     607    * 
     608    * @param  string Input body to decode 
     609    * @param  string Encoding type to use. 
     610    * @return string Decoded body 
     611    * @access private 
     612    */ 
     613    function _decodeBody($input, $encoding = '7bit', $charset = '') 
     614    { 
     615        switch (strtolower($encoding)) { 
     616            case '7bit': 
     617                return $this->_fromCharset($charset, $input);; 
     618                break; 
     619 
     620            case '8bit': 
     621                return $this->_fromCharset($charset, $input); 
     622                break; 
     623 
     624            case 'quoted-printable': 
     625                return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input)); 
     626                break; 
     627 
     628            case 'base64': 
     629                return $this->_fromCharset($charset, base64_decode($input)); 
     630                break; 
     631 
     632            default: 
     633                return $input; 
     634        } 
     635    } 
     636 
     637    /** 
     638    * Given a quoted-printable string, this 
     639    * function will decode and return it. 
     640    * 
     641    * @param  string Input body to decode 
     642    * @return string Decoded body 
     643    * @access private 
     644    */ 
     645    function _quotedPrintableDecode($input) 
     646    { 
     647        // Remove soft line breaks 
     648        $input = preg_replace("/=\r?\n/", '', $input); 
     649 
     650        // Replace encoded characters 
     651        $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input); 
     652 
     653        return $input; 
     654    } 
     655 
     656    /** 
     657    * Checks the input for uuencoded files and returns 
     658    * an array of them. Can be called statically, eg: 
     659    * 
     660    * $files =& Mail_mimeDecode::uudecode($some_text); 
     661    * 
     662    * It will check for the begin 666 ... end syntax 
     663    * however and won't just blindly decode whatever you 
     664    * pass it. 
     665    * 
     666    * @param  string Input body to look for attahcments in 
     667    * @return array  Decoded bodies, filenames and permissions 
     668    * @access public 
     669    * @author Unknown 
     670    */ 
     671    function &uudecode($input) 
     672    { 
     673        // Find all uuencoded sections 
     674        preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); 
     675 
     676        for ($j = 0; $j < count($matches[3]); $j++) { 
     677 
     678            $str      = $matches[3][$j]; 
     679            $filename = $matches[2][$j]; 
     680            $fileperm = $matches[1][$j]; 
     681 
     682            $file = ''; 
     683            $str = preg_split("/\r?\n/", trim($str)); 
     684            $strlen = count($str); 
     685 
     686            for ($i = 0; $i < $strlen; $i++) { 
     687                $pos = 1; 
     688                $d = 0; 
     689                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); 
     690 
     691                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { 
     692                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     693                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     694                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
     695                    $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); 
     696                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     697 
     698                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
     699 
     700                    $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077)); 
     701 
     702                    $pos += 4; 
     703                    $d += 3; 
     704                } 
     705 
     706                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { 
     707                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     708                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     709                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
     710                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     711 
     712                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
     713 
     714                    $pos += 3; 
     715                    $d += 2; 
     716                } 
     717 
     718                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { 
     719                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     720                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     721                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     722 
     723                } 
     724            } 
     725            $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); 
     726        } 
     727 
     728        return $files; 
     729    } 
     730 
     731    /** 
     732    * getSendArray() returns the arguments required for Mail::send() 
     733    * used to build the arguments for a mail::send() call 
     734    * 
     735    * Usage: 
     736    * $mailtext = Full email (for example generated by a template) 
     737    * $decoder = new Mail_mimeDecode($mailtext); 
     738    * $parts =  $decoder->getSendArray(); 
     739    * if (!PEAR::isError($parts) { 
     740    *     list($recipents,$headers,$body) = $parts; 
     741    *     $mail = Mail::factory('smtp'); 
     742    *     $mail->send($recipents,$headers,$body); 
     743    * } else { 
     744    *     echo $parts->message; 
     745    * } 
     746    * @return mixed   array of recipeint, headers,body or Pear_Error 
     747    * @access public 
     748    * @author Alan Knowles <alan@akbkhome.com> 
     749    */ 
     750    function getSendArray() 
     751    { 
     752        // prevent warning if this is not set 
     753        $this->_decode_headers = FALSE; 
     754        $headerlist =$this->_parseHeaders($this->_header); 
     755        $to = ""; 
     756        if (!$headerlist) { 
     757            return $this->raiseError("Message did not contain headers"); 
     758        } 
     759        foreach($headerlist as $item) { 
     760            $header[$item['name']] = $item['value']; 
     761            switch (strtolower($item['name'])) { 
     762                case "to": 
     763                case "cc": 
     764                case "bcc": 
     765                    $to .= ",".$item['value']; 
     766                default: 
     767                   break; 
     768            } 
     769        } 
     770        if ($to == "") { 
     771            return $this->raiseError("Message did not contain any recipents"); 
     772        } 
     773        $to = substr($to,1); 
     774        return array($to,$header,$this->_body); 
     775    } 
     776 
     777    /** 
     778    * Returns a xml copy of the output of 
     779    * Mail_mimeDecode::decode. Pass the output in as the 
     780    * argument. This function can be called statically. Eg: 
     781    * 
     782    * $output = $obj->decode(); 
     783    * $xml    = Mail_mimeDecode::getXML($output); 
     784    * 
     785    * The DTD used for this should have been in the package. Or 
     786    * alternatively you can get it from cvs, or here: 
     787    * http://www.phpguru.org/xmail/xmail.dtd. 
     788    * 
     789    * @param  object Input to convert to xml. This should be the 
     790    *                output of the Mail_mimeDecode::decode function 
     791    * @return string XML version of input 
     792    * @access public 
     793    */ 
     794    function getXML($input) 
     795    { 
     796        $crlf    =  "\r\n"; 
     797        $output  = '<?xml version=\'1.0\'?>' . $crlf . 
    798798                   '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf . 
    799799                   '<email>' . $crlf . 
    800                 Mail_mimeDecode::_getXML($input) . 
     800                   Mail_mimeDecode::_getXML($input) . 
    801801                   '</email>'; 
    802802 
    803                 return $output; 
    804         } 
    805  
    806         /** 
    807         * Function that does the actual conversion to xml. Does a single 
    808         * mimepart at a time. 
    809         * 
    810         * @param  object  Input to convert to xml. This is a mimepart object. 
    811         *                 It may or may not contain subparts. 
    812         * @param  integer Number of tabs to indent 
    813         * @return string  XML version of input 
    814         * @access private 
    815         */ 
    816         function _getXML($input, $indent = 1) 
    817         { 
    818                 $htab    =  "\t"; 
    819                 $crlf    =  "\r\n"; 
    820                 $output  =  ''; 
    821                 $headers = @(array)$input->headers; 
    822  
    823                 foreach ($headers as $hdr_name => $hdr_value) { 
    824  
    825                         // Multiple headers with this name 
    826                         if (is_array($headers[$hdr_name])) { 
    827                                 for ($i = 0; $i < count($hdr_value); $i++) { 
    828                                         $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); 
    829                                 } 
    830  
    831                                 // Only one header of this sort 
    832                         } else { 
    833                                 $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); 
    834                         } 
    835                 } 
    836  
    837                 if (!empty($input->parts)) { 
    838                         for ($i = 0; $i < count($input->parts); $i++) { 
    839                                 $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf . 
    840                                 Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . 
    841                                 str_repeat($htab, $indent) . '</mimepart>' . $crlf; 
    842                         } 
    843                 } elseif (isset($input->body)) { 
    844                         $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' . 
    845                         $input->body . ']]></body>' . $crlf; 
    846                 } 
    847  
    848                 return $output; 
    849         } 
    850  
    851         /** 
    852         * Helper function to _getXML(). Returns xml of a header. 
    853         * 
    854         * @param  string  Name of header 
    855         * @param  string  Value of header 
    856         * @param  integer Number of tabs to indent 
    857         * @return string  XML version of input 
    858         * @access private 
    859         */ 
    860         function _getXML_helper($hdr_name, $hdr_value, $indent) 
    861         { 
    862                 $htab   = "\t"; 
    863                 $crlf   = "\r\n"; 
    864                 $return = ''; 
    865  
    866                 $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); 
    867                 $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); 
    868  
    869                 // Sort out any parameters 
    870                 if (!empty($new_hdr_value['other'])) { 
    871                         foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { 
    872                                 $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf . 
    873                                 str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf . 
    874                                 str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf . 
    875                                 str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf; 
    876                         } 
    877  
    878                         $params = implode('', $params); 
    879                 } else { 
    880                         $params = ''; 
    881                 } 
    882  
    883                 $return = str_repeat($htab, $indent) . '<header>' . $crlf . 
    884                 str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf . 
    885                 str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf . 
    886                 $params . 
    887                 str_repeat($htab, $indent) . '</header>' . $crlf; 
    888  
    889                 return $return; 
    890         } 
    891  
    892         /** 
    893         * Z-Push helper to decode text 
    894         * 
    895         * @param  string  current charset of input 
    896         * @param  string  input 
    897         * @return string  XML version of input 
    898         * @access private 
    899         */ 
    900         function _fromCharset($charset, $input) { 
    901                 if($charset == '' || ($charset == $this->_charset)) 
     803        return $output; 
     804    } 
     805 
     806    /** 
     807    * Function that does the actual conversion to xml. Does a single 
     808    * mimepart at a time. 
     809    * 
     810    * @param  object  Input to convert to xml. This is a mimepart object. 
     811    *                 It may or may not contain subparts. 
     812    * @param  integer Number of tabs to indent 
     813    * @return string  XML version of input 
     814    * @access private 
     815    */ 
     816    function _getXML($input, $indent = 1) 
     817    { 
     818        $htab    =  "\t"; 
     819        $crlf    =  "\r\n"; 
     820        $output  =  ''; 
     821        $headers = @(array)$input->headers; 
     822 
     823        foreach ($headers as $hdr_name => $hdr_value) { 
     824 
     825            // Multiple headers with this name 
     826            if (is_array($headers[$hdr_name])) { 
     827                for ($i = 0; $i < count($hdr_value); $i++) { 
     828                    $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); 
     829                } 
     830 
     831            // Only one header of this sort 
     832            } else { 
     833                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); 
     834            } 
     835        } 
     836 
     837        if (!empty($input->parts)) { 
     838            for ($i = 0; $i < count($input->parts); $i++) { 
     839                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf . 
     840                           Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . 
     841                           str_repeat($htab, $indent) . '</mimepart>' . $crlf; 
     842            } 
     843        } elseif (isset($input->body)) { 
     844            $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' . 
     845                       $input->body . ']]></body>' . $crlf; 
     846        } 
     847 
     848        return $output; 
     849    } 
     850 
     851    /** 
     852    * Helper function to _getXML(). Returns xml of a header. 
     853    * 
     854    * @param  string  Name of header 
     855    * @param  string  Value of header 
     856    * @param  integer Number of tabs to indent 
     857    * @return string  XML version of input 
     858    * @access private 
     859    */ 
     860    function _getXML_helper($hdr_name, $hdr_value, $indent) 
     861    { 
     862        $htab   = "\t"; 
     863        $crlf   = "\r\n"; 
     864        $return = ''; 
     865 
     866        $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); 
     867        $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); 
     868 
     869        // Sort out any parameters 
     870        if (!empty($new_hdr_value['other'])) { 
     871            foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { 
     872                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf . 
     873                            str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf . 
     874                            str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf . 
     875                            str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf; 
     876            } 
     877 
     878            $params = implode('', $params); 
     879        } else { 
     880            $params = ''; 
     881        } 
     882 
     883        $return = str_repeat($htab, $indent) . '<header>' . $crlf . 
     884                  str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf . 
     885                  str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf . 
     886                  $params . 
     887                  str_repeat($htab, $indent) . '</header>' . $crlf; 
     888 
     889        return $return; 
     890    } 
     891 
     892    /** 
     893    * Z-Push helper to decode text 
     894    * 
     895    * @param  string  current charset of input 
     896    * @param  string  input 
     897    * @return string  XML version of input 
     898    * @access private 
     899    */ 
     900    function _fromCharset($charset, $input) { 
     901        if($charset == '' || (strtolower($charset) == $this->_charset)) 
    902902            return $input; 
    903903 
    904904        return @iconv($charset, $this->_charset. "//TRANSLIT", $input); 
    905         } 
    906  
    907         /** 
    908         * Z-Push helper for error logging 
    909         * removing PEAR dependency 
    910         * 
    911         * @param  string  debug message 
    912         * @return boolean always false as there was an error 
    913         * @access private 
    914         */ 
    915         function raiseError($message) { 
    916                 debugLog("mimeDecode error: ". $message); 
    917                 return false; 
    918         } 
     905    } 
     906 
     907    /** 
     908    * Z-Push helper for error logging 
     909    * removing PEAR dependency 
     910    * 
     911    * @param  string  debug message 
     912    * @return boolean always false as there was an error 
     913    * @access private 
     914    */ 
     915    function raiseError($message) { 
     916        debugLog("mimeDecode error: ". $message); 
     917        return false; 
     918    } 
    919919} // End of class 
Note: See TracChangeset for help on using the changeset viewer.