Ignore:
Timestamp:
02/14/11 16:40:59 (13 years ago)
Author:
emersonfaria
Message:

Ticket #1551 - Correcoes de bugs e Melhorias de formatacao de email no Z-Push.

File:
1 edited

Legend:

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

    r3637 r3754  
    2929 *   notice, this list of conditions and the following disclaimer in the 
    3030 *   documentation and/or other materials provided with the distribution. 
    31  * - Neither the name of the authors, nor the names of its contributors  
    32  *   may be used to endorse or promote products derived from this  
     31 * - Neither the name of the authors, nor the names of its contributors 
     32 *   may be used to endorse or promote products derived from this 
    3333 *   software without specific prior written permission. 
    3434 * 
     
    6464 * removed deprecated referencing: $obj = &new Mail_mimeDecode($body); in _decode() 
    6565 * implemented automated decoding of strings from mail charset 
    66  *  
    67  * Reference implementation used:  
     66 * 
     67 * Reference implementation used: 
    6868 * http://download.pear.php.net/package/Mail_mimeDecode-1.5.1.tgz 
    69  *  
     69 * 
    7070 */ 
    7171 
     
    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                        $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 
     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( 
    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 == '') 
     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)) 
    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.