Ignore:
Timestamp:
08/03/11 12:00:08 (13 years ago)
Author:
thiagoaos
Message:

Ticket #2180 - Adicionado código fonte completo do zpush

File:
1 edited

Legend:

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

    r4613 r4898  
    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                                  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, 
    360                                                           'decode_bodies'  => $this->_decode_bodies, 
    361                                                           '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, 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( 
    468                                   'name'  => $hdr_name, 
    469                                   '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 . 
     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, 
     360                                                'decode_bodies'  => $this->_decode_bodies, 
     361                                                '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                                         
     369                                        // if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted 
     370                                        $charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) ) ? $return->ctype_parameters['charset']: ''; 
     371                                        $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null; 
     372                                        break; 
     373                        } 
     374 
     375                } else { 
     376                        $ctype = explode('/', $default_ctype); 
     377                        $return->ctype_primary   = $ctype[0]; 
     378                        $return->ctype_secondary = $ctype[1]; 
     379                        $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; 
     380                } 
     381 
     382                return $return; 
     383        } 
     384 
     385        /** 
     386         * Given the output of the above function, this will return an 
     387         * array of references to the parts, indexed by mime number. 
     388         * 
     389         * @param  object $structure   The structure to go through 
     390         * @param  string $mime_number Internal use only. 
     391         * @return array               Mime numbers 
     392         */ 
     393        function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') 
     394        { 
     395                $return = array(); 
     396                if (!empty($structure->parts)) { 
     397                        if ($mime_number != '') { 
     398                                $structure->mime_id = $prepend . $mime_number; 
     399                                $return[$prepend . $mime_number] = &$structure; 
     400                        } 
     401                        for ($i = 0; $i < count($structure->parts); $i++) { 
     402                                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { 
     403                                        $prepend      = $prepend . $mime_number . '.'; 
     404                                        $_mime_number = ''; 
     405                                } else { 
     406                                        $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); 
     407                                } 
     408 
     409                                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); 
     410                                foreach ($arr as $key => $val) { 
     411                                        $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; 
     412                                } 
     413                        } 
     414                } else { 
     415                        if ($mime_number == '') { 
     416                                $mime_number = '1'; 
     417                        } 
     418                        $structure->mime_id = $prepend . $mime_number; 
     419                        $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; 
     420                } 
     421 
     422                return $return; 
     423        } 
     424 
     425        /** 
     426         * Given a string containing a header and body 
     427         * section, this function will split them (at the first 
     428         * blank line) and return them. 
     429         * 
     430         * @param string Input to split apart 
     431         * @return array Contains header and body section 
     432         * @access private 
     433         */ 
     434        function _splitBodyHeader($input) 
     435        { 
     436                if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { 
     437                        return array($match[1], $match[2]); 
     438                } 
     439                $this->_error = 'Could not split header and body'; 
     440                return false; 
     441        } 
     442 
     443        /** 
     444         * Parse headers given in $input and return 
     445         * as assoc array. 
     446         * 
     447         * @param string Headers to parse 
     448         * @return array Contains parsed headers 
     449         * @access private 
     450         */ 
     451        function _parseHeaders($input) 
     452        { 
     453 
     454                if ($input !== '') { 
     455                        // Unfold the input 
     456                        $input   = preg_replace("/\r?\n/", "\r\n", $input); 
     457                        $input   = preg_replace("/\r\n(\t| )+/", ' ', $input); 
     458                        $headers = explode("\r\n", trim($input)); 
     459 
     460                        foreach ($headers as $value) { 
     461                                $hdr_name = substr($value, 0, $pos = strpos($value, ':')); 
     462                                $hdr_value = substr($value, $pos+1); 
     463                                if($hdr_value[0] == ' ') 
     464                                        $hdr_value = substr($hdr_value, 1); 
     465 
     466                                $return[] = array( 
     467                                        'name'  => $hdr_name, 
     468                                        'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value 
     469                                ); 
     470                        } 
     471                } else { 
     472                        $return = array(); 
     473                } 
     474 
     475                return $return; 
     476        } 
     477 
     478        /** 
     479         * Function to parse a header value, 
     480         * extract first part, and any secondary 
     481         * parts (after ;) This function is not as 
     482         * robust as it could be. Eg. header comments 
     483         * in the wrong place will probably break it. 
     484         * 
     485         * @param string Header value to parse 
     486         * @return array Contains parsed result 
     487         * @access private 
     488         */ 
     489        function _parseHeaderValue($input) 
     490        { 
     491                if (($pos = strpos($input, ';')) !== false) { 
     492                        $return['value'] = trim(substr($input, 0, $pos)); 
     493                        $input = trim(substr($input, $pos+1)); 
     494 
     495                        if (strlen($input) > 0) { 
     496                                // This splits on a semi-colon, if there's no preceeding backslash 
     497                                // Now works with quoted values; had to glue the \; breaks in PHP 
     498                                // the regex is already bordering on incomprehensible 
     499                                //$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
     500                                // simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex 
     501                                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
     502                                preg_match_all($splitRegex, $input, $matches); 
     503 
     504                                $parameters = array(); 
     505                                for ($i=0; $i<count($matches[0]); $i++) { 
     506                                        $param = $matches[0][$i]; 
     507                                        while (substr($param, -2) == '\;') { 
     508                                                $param .= $matches[0][++$i]; 
     509                                        } 
     510                                        $parameters[] = $param; 
     511                                } 
     512 
     513                                for ($i = 0; $i < count($parameters); $i++) { 
     514                                        $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); 
     515                                        $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); 
     516                                        if (!empty($param_value[0]) && $param_value[0] == '"') { 
     517                                                $param_value = substr($param_value, 1, -1); 
     518                                        } 
     519                                        $return['other'][$param_name] = $param_value; 
     520                                        $return['other'][strtolower($param_name)] = $param_value; 
     521                                } 
     522                        } 
     523                } else { 
     524                        $return['value'] = trim($input); 
     525                } 
     526 
     527                return $return; 
     528        } 
     529 
     530        /** 
     531         * This function splits the input based 
     532         * on the given boundary 
     533         * 
     534         * @param string Input to parse 
     535         * @return array Contains array of resulting mime parts 
     536         * @access private 
     537         */ 
     538        function _boundarySplit($input, $boundary) 
     539        { 
     540                $parts = array(); 
     541 
     542                $bs_possible = substr($boundary, 2, -2); 
     543                $bs_check = '\"' . $bs_possible . '\"'; 
     544 
     545                if ($boundary == $bs_check) { 
     546                        $boundary = $bs_possible; 
     547                } 
     548 
     549                $tmp = explode('--' . $boundary, $input); 
     550 
     551                for ($i = 1; $i < count($tmp) - 1; $i++) { 
     552                        $parts[] = $tmp[$i]; 
     553                } 
     554 
     555                return $parts; 
     556        } 
     557 
     558        /** 
     559         * Given a header, this function will decode it 
     560         * according to RFC2047. Probably not *exactly* 
     561         * conformant, but it does pass all the given 
     562         * examples (in RFC2047). 
     563         * 
     564         * @param string Input header value to decode 
     565         * @return string Decoded header value 
     566         * @access private 
     567         */ 
     568        function _decodeHeader($input) 
     569        { 
     570                // Remove white space between encoded-words 
     571                $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); 
     572 
     573                // For each encoded-word... 
     574                while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { 
     575                        $encoded  = $matches[1]; 
     576                        $charset  = $matches[2]; 
     577                        $encoding = $matches[3]; 
     578                        $text     = $matches[4]; 
     579 
     580                        switch (strtolower($encoding)) { 
     581                                case 'b': 
     582                                        $text = base64_decode($text); 
     583                                        break; 
     584 
     585                                case 'q': 
     586                                        $text = str_replace('_', ' ', $text); 
     587                                        preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); 
     588                                        foreach($matches[1] as $value) 
     589                                        $text = str_replace('='.$value, chr(hexdec($value)), $text); 
     590                                        break; 
     591                        } 
     592 
     593                        $input = str_replace($encoded, $this->_fromCharset($charset, $text), $input); 
     594                } 
     595 
     596                return $input; 
     597        } 
     598 
     599        /** 
     600         * Given a body string and an encoding type, 
     601         * this function will decode and return it. 
     602         * 
     603         * @param  string Input body to decode 
     604         * @param  string Encoding type to use. 
     605         * @return string Decoded body 
     606         * @access private 
     607         */ 
     608        function _decodeBody($input, $encoding = '7bit', $charset = '') 
     609        { 
     610                switch (strtolower($encoding)) { 
     611                        case '7bit': 
     612                                return $this->_fromCharset($charset, $input);; 
     613                                break; 
     614 
     615                        case '8bit': 
     616                                return $this->_fromCharset($charset, $input); 
     617                                break; 
     618 
     619                        case 'quoted-printable': 
     620                                return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input)); 
     621                                break; 
     622 
     623                        case 'base64': 
     624                                return $this->_fromCharset($charset, base64_decode($input)); 
     625                                break; 
     626 
     627                        default: 
     628                                return $input; 
     629                } 
     630        } 
     631 
     632        /** 
     633         * Given a quoted-printable string, this 
     634         * function will decode and return it. 
     635         * 
     636         * @param  string Input body to decode 
     637         * @return string Decoded body 
     638         * @access private 
     639         */ 
     640        function _quotedPrintableDecode($input) 
     641        { 
     642                // Remove soft line breaks 
     643                $input = preg_replace("/=\r?\n/", '', $input); 
     644 
     645                // Replace encoded characters 
     646                $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input); 
     647 
     648                return $input; 
     649        } 
     650 
     651        /** 
     652         * Checks the input for uuencoded files and returns 
     653         * an array of them. Can be called statically, eg: 
     654         * 
     655         * $files =& Mail_mimeDecode::uudecode($some_text); 
     656         * 
     657         * It will check for the begin 666 ... end syntax 
     658         * however and won't just blindly decode whatever you 
     659         * pass it. 
     660         * 
     661         * @param  string Input body to look for attahcments in 
     662         * @return array  Decoded bodies, filenames and permissions 
     663         * @access public 
     664         * @author Unknown 
     665         */ 
     666        function &uudecode($input) 
     667        { 
     668                // Find all uuencoded sections 
     669                preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); 
     670 
     671                for ($j = 0; $j < count($matches[3]); $j++) { 
     672                        $str      = $matches[3][$j]; 
     673                        $filename = $matches[2][$j]; 
     674                        $fileperm = $matches[1][$j]; 
     675 
     676                        $file = ''; 
     677                        $str = preg_split("/\r?\n/", trim($str)); 
     678                        $strlen = count($str); 
     679 
     680                        for ($i = 0; $i < $strlen; $i++) { 
     681                                $pos = 1; 
     682                                $d = 0; 
     683                                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); 
     684 
     685                                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { 
     686                                        $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     687                                        $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     688                                        $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
     689                                        $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); 
     690                                        $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     691 
     692                                        $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
     693 
     694                                        $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077)); 
     695 
     696                                        $pos += 4; 
     697                                        $d += 3; 
     698                                } 
     699 
     700                                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { 
     701                                        $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     702                                        $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     703                                        $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
     704                                        $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     705 
     706                                        $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
     707 
     708                                        $pos += 3; 
     709                                        $d += 2; 
     710                                } 
     711 
     712                                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { 
     713                                        $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     714                                        $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     715                                        $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     716                                } 
     717                        } 
     718                        $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); 
     719                } 
     720 
     721                return $files; 
     722        } 
     723 
     724        /** 
     725         * getSendArray() returns the arguments required for Mail::send() 
     726         * used to build the arguments for a mail::send() call 
     727         * 
     728         * Usage: 
     729         * $mailtext = Full email (for example generated by a template) 
     730         * $decoder = new Mail_mimeDecode($mailtext); 
     731         * $parts =  $decoder->getSendArray(); 
     732         * if (!PEAR::isError($parts) { 
     733         *     list($recipents,$headers,$body) = $parts; 
     734         *     $mail = Mail::factory('smtp'); 
     735         *     $mail->send($recipents,$headers,$body); 
     736         * } else { 
     737         *     echo $parts->message; 
     738         * } 
     739         * @return mixed   array of recipeint, headers,body or Pear_Error 
     740         * @access public 
     741         * @author Alan Knowles <alan@akbkhome.com> 
     742         */ 
     743        function getSendArray() 
     744        { 
     745                // prevent warning if this is not set 
     746                $this->_decode_headers = FALSE; 
     747                $headerlist =$this->_parseHeaders($this->_header); 
     748                $to = ""; 
     749                if (!$headerlist) { 
     750                        return $this->raiseError("Message did not contain headers"); 
     751                } 
     752                foreach($headerlist as $item) { 
     753                        $header[$item['name']] = $item['value']; 
     754                        switch (strtolower($item['name'])) { 
     755                                case "to": 
     756                                case "cc": 
     757                                case "bcc": 
     758                                        $to .= ",".$item['value']; 
     759                                default: 
     760                                        break; 
     761                        } 
     762                } 
     763                if ($to == "") { 
     764                        return $this->raiseError("Message did not contain any recipents"); 
     765                } 
     766                $to = substr($to,1); 
     767                return array($to,$header,$this->_body); 
     768        } 
     769 
     770        /** 
     771         * Returns a xml copy of the output of 
     772         * Mail_mimeDecode::decode. Pass the output in as the 
     773         * argument. This function can be called statically. Eg: 
     774         * 
     775         * $output = $obj->decode(); 
     776         * $xml    = Mail_mimeDecode::getXML($output); 
     777         * 
     778         * The DTD used for this should have been in the package. Or 
     779         * alternatively you can get it from cvs, or here: 
     780         * http://www.phpguru.org/xmail/xmail.dtd. 
     781         * 
     782         * @param  object Input to convert to xml. This should be the 
     783         *                output of the Mail_mimeDecode::decode function 
     784         * @return string XML version of input 
     785         * @access public 
     786         */ 
     787        function getXML($input) 
     788        { 
     789                $crlf    =  "\r\n"; 
     790                $output  = '<?xml version=\'1.0\'?>' . $crlf . 
    798791                   '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf . 
    799792                   '<email>' . $crlf . 
    800                    Mail_mimeDecode::_getXML($input) . 
     793                Mail_mimeDecode::_getXML($input) . 
    801794                   '</email>'; 
    802795 
    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)) 
    902             return $input; 
    903  
    904         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     } 
     796                return $output; 
     797        } 
     798 
     799        /** 
     800         * Function that does the actual conversion to xml. Does a single 
     801         * mimepart at a time. 
     802         * 
     803         * @param  object  Input to convert to xml. This is a mimepart object. 
     804         *                 It may or may not contain subparts. 
     805         * @param  integer Number of tabs to indent 
     806         * @return string  XML version of input 
     807         * @access private 
     808         */ 
     809        function _getXML($input, $indent = 1) 
     810        { 
     811                $htab    =  "\t"; 
     812                $crlf    =  "\r\n"; 
     813                $output  =  ''; 
     814                $headers = @(array)$input->headers; 
     815 
     816                foreach ($headers as $hdr_name => $hdr_value) { 
     817                        // Multiple headers with this name 
     818                        if (is_array($headers[$hdr_name])) { 
     819                                for ($i = 0; $i < count($hdr_value); $i++) { 
     820                                        $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); 
     821                                } 
     822 
     823                                // Only one header of this sort 
     824                        } else { 
     825                                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); 
     826                        } 
     827                } 
     828 
     829                if (!empty($input->parts)) { 
     830                        for ($i = 0; $i < count($input->parts); $i++) { 
     831                                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf . 
     832                                Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . 
     833                                str_repeat($htab, $indent) . '</mimepart>' . $crlf; 
     834                        } 
     835                } elseif (isset($input->body)) { 
     836                        $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' . 
     837                        $input->body . ']]></body>' . $crlf; 
     838                } 
     839 
     840                return $output; 
     841        } 
     842 
     843        /** 
     844         * Helper function to _getXML(). Returns xml of a header. 
     845         * 
     846         * @param  string  Name of header 
     847         * @param  string  Value of header 
     848         * @param  integer Number of tabs to indent 
     849         * @return string  XML version of input 
     850         * @access private 
     851         */ 
     852        function _getXML_helper($hdr_name, $hdr_value, $indent) 
     853        { 
     854                $htab   = "\t"; 
     855                $crlf   = "\r\n"; 
     856                $return = ''; 
     857 
     858                $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); 
     859                $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); 
     860 
     861                // Sort out any parameters 
     862                if (!empty($new_hdr_value['other'])) { 
     863                        foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { 
     864                                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf . 
     865                                str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf . 
     866                                str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf . 
     867                                str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf; 
     868                        } 
     869 
     870                        $params = implode('', $params); 
     871                } else { 
     872                        $params = ''; 
     873                } 
     874 
     875                $return = str_repeat($htab, $indent) . '<header>' . $crlf . 
     876                str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf . 
     877                str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf . 
     878                $params . 
     879                str_repeat($htab, $indent) . '</header>' . $crlf; 
     880 
     881                return $return; 
     882        } 
     883 
     884        /** 
     885         * Z-Push helper to decode text 
     886         * 
     887         * @param  string  current charset of input 
     888         * @param  string  input 
     889         * @return string  XML version of input 
     890         * @access private 
     891         */ 
     892        function _fromCharset($charset, $input) { 
     893                if($charset == '' || (strtolower($charset) == $this->_charset)) 
     894                        return $input; 
     895 
     896                return @iconv($charset, $this->_charset. "//TRANSLIT", $input); 
     897        } 
     898 
     899        /** 
     900         * Z-Push helper for error logging 
     901         * removing PEAR dependency 
     902         * 
     903         * @param  string  debug message 
     904         * @return boolean always false as there was an error 
     905         * @access private 
     906         */ 
     907        function raiseError($message) { 
     908                debugLog("mimeDecode error: ". $message); 
     909                return false; 
     910        } 
    919911} // End of class 
Note: See TracChangeset for help on using the changeset viewer.