Mail
[ class tree: Mail ] [ index: Mail ] [ all elements ]

Source for file mimeDecode.php

Documentation is available at mimeDecode.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. // +-----------------------------------------------------------------------+
  4. // | Copyright (c) 2002-2003  Richard Heyes                                |
  5. // | Copyright (c) 2003-2005  The PHP Group                                |
  6. // | All rights reserved.                                                  |
  7. // |                                                                       |
  8. // | Redistribution and use in source and binary forms, with or without    |
  9. // | modification, are permitted provided that the following conditions    |
  10. // | are met:                                                              |
  11. // |                                                                       |
  12. // | o Redistributions of source code must retain the above copyright      |
  13. // |   notice, this list of conditions and the following disclaimer.       |
  14. // | o Redistributions in binary form must reproduce the above copyright   |
  15. // |   notice, this list of conditions and the following disclaimer in the |
  16. // |   documentation and/or other materials provided with the distribution.|
  17. // | o The names of the authors may not be used to endorse or promote      |
  18. // |   products derived from this software without specific prior written  |
  19. // |   permission.                                                         |
  20. // |                                                                       |
  21. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
  22. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
  23. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  24. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
  25. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  26. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
  27. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  28. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  29. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
  30. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  31. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
  32. // |                                                                       |
  33. // +-----------------------------------------------------------------------+
  34. // | Author: Richard Heyes <richard@phpguru.org>                           |
  35. // +-----------------------------------------------------------------------+
  36.  
  37. require_once 'PEAR.php';
  38.  
  39. /**
  40. *  +----------------------------- IMPORTANT ------------------------------+
  41. *  | Usage of this class compared to native php extensions such as        |
  42. *  | mailparse or imap, is slow and may be feature deficient. If available|
  43. *  | you are STRONGLY recommended to use the php extensions.              |
  44. *  +----------------------------------------------------------------------+
  45. *
  46. * Mime Decoding class
  47. *
  48. * This class will parse a raw mime email and return
  49. * the structure. Returned structure is similar to
  50. * that returned by imap_fetchstructure().
  51. *
  52. * USAGE: (assume $input is your raw email)
  53. *
  54. * $decode = new Mail_mimeDecode($input, "\r\n");
  55. * $structure = $decode->decode();
  56. * print_r($structure);
  57. *
  58. * Or statically:
  59. *
  60. * $params['input'] = $input;
  61. * $structure = Mail_mimeDecode::decode($params);
  62. * print_r($structure);
  63. *
  64. * TODO:
  65. *  o Implement multipart/appledouble
  66. *  o UTF8: ???
  67.  
  68.         > 4. We have also found a solution for decoding the UTF-8 
  69.         > headers. Therefore I made the following function:
  70.         > 
  71.         > function decode_utf8($txt) {
  72.         > $txt=strtr($txt,$trans);
  73.         > return(utf8_decode($txt));
  74.         > }
  75.         > 
  76.         > And I have inserted the following line to the class:
  77.         > 
  78.         > if (strtolower($charset)=="utf-8") $text=decode_utf8($text);
  79.         > 
  80.         > ... before the following one in the "_decodeHeader" function:
  81.         > 
  82.         > $input = str_replace($encoded, $text, $input);
  83.         > 
  84.         > This way from now on it can easily decode the UTF-8 headers too.
  85.  
  86. *
  87. @author  Richard Heyes <richard@phpguru.org>
  88. @version $Revision: 1.46 $
  89. @package Mail
  90. */
  91. class Mail_mimeDecode extends PEAR
  92. {
  93.     /**
  94.      * The raw email to decode
  95.      * @var    string 
  96.      */
  97.     var $_input;
  98.  
  99.     /**
  100.      * The header part of the input
  101.      * @var    string 
  102.      */
  103.     var $_header;
  104.  
  105.     /**
  106.      * The body part of the input
  107.      * @var    string 
  108.      */
  109.     var $_body;
  110.  
  111.     /**
  112.      * If an error occurs, this is used to store the message
  113.      * @var    string 
  114.      */
  115.     var $_error;
  116.  
  117.     /**
  118.      * Flag to determine whether to include bodies in the
  119.      * returned object.
  120.      * @var    boolean 
  121.      */
  122.     var $_include_bodies;
  123.  
  124.     /**
  125.      * Flag to determine whether to decode bodies
  126.      * @var    boolean 
  127.      */
  128.     var $_decode_bodies;
  129.  
  130.     /**
  131.      * Flag to determine whether to decode headers
  132.      * @var    boolean 
  133.      */
  134.     var $_decode_headers;
  135.  
  136.     /**
  137.      * Constructor.
  138.      *
  139.      * Sets up the object, initialise the variables, and splits and
  140.      * stores the header and body of the input.
  141.      *
  142.      * @param string The input to decode
  143.      * @access public
  144.      */
  145.     function Mail_mimeDecode($input)
  146.     {
  147.         list($header$body)   $this->_splitBodyHeader($input);
  148.  
  149.         $this->_input          $input;
  150.         $this->_header         $header;
  151.         $this->_body           $body;
  152.         $this->_decode_bodies  = false;
  153.         $this->_include_bodies = true;
  154.     }
  155.  
  156.     /**
  157.      * Begins the decoding process. If called statically
  158.      * it will create an object and call the decode() method
  159.      * of it.
  160.      *
  161.      * @param array An array of various parameters that determine
  162.      *               various things:
  163.      *               include_bodies - Whether to include the body in the returned
  164.      *                                object.
  165.      *               decode_bodies  - Whether to decode the bodies
  166.      *                                of the parts. (Transfer encoding)
  167.      *               decode_headers - Whether to decode headers
  168.      *               input          - If called statically, this will be treated
  169.      *                                as the input
  170.      * @return object Decoded results
  171.      * @access public
  172.      */
  173.     function decode($params = null)
  174.     {
  175.         // determine if this method has been called statically
  176.         $isStatic !(isset($this&& get_class($this== __CLASS__);
  177.  
  178.         // Have we been called statically?
  179.     // If so, create an object and pass details to that.
  180.         if ($isStatic AND isset($params['input'])) {
  181.  
  182.             $obj = new Mail_mimeDecode($params['input']);
  183.             $structure $obj->decode($params);
  184.  
  185.         // Called statically but no input
  186.         elseif ($isStatic{
  187.             return PEAR::raiseError('Called statically and no input given');
  188.  
  189.         // Called via an object
  190.         else {
  191.             $this->_include_bodies = isset($params['include_bodies']?
  192.                                  $params['include_bodies': false;
  193.             $this->_decode_bodies  = isset($params['decode_bodies']?
  194.                                  $params['decode_bodies']  : false;
  195.             $this->_decode_headers = isset($params['decode_headers']?
  196.                                  $params['decode_headers': false;
  197.  
  198.             $structure $this->_decode($this->_header$this->_body);
  199.             if ($structure === false{
  200.                 $structure $this->raiseError($this->_error);
  201.             }
  202.         }
  203.  
  204.         return $structure;
  205.     }
  206.  
  207.     /**
  208.      * Performs the decoding. Decodes the body string passed to it
  209.      * If it finds certain content-types it will call itself in a
  210.      * recursive fashion
  211.      *
  212.      * @param string Header section
  213.      * @param string Body section
  214.      * @return object Results of decoding process
  215.      * @access private
  216.      */
  217.     function _decode($headers$body$default_ctype 'text/plain')
  218.     {
  219.         $return = new stdClass;
  220.         $return->headers = array();
  221.         $headers $this->_parseHeaders($headers);
  222.  
  223.         foreach ($headers as $value{
  224.             if (isset($return->headers[strtolower($value['name'])]AND !is_array($return->headers[strtolower($value['name'])])) {
  225.                 $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]);
  226.                 $return->headers[strtolower($value['name'])][$value['value'];
  227.  
  228.             elseif (isset($return->headers[strtolower($value['name'])])) {
  229.                 $return->headers[strtolower($value['name'])][$value['value'];
  230.  
  231.             else {
  232.                 $return->headers[strtolower($value['name'])$value['value'];
  233.             }
  234.         }
  235.  
  236.         reset($headers);
  237.         while (list($key$valueeach($headers)) {
  238.             $headers[$key]['name'strtolower($headers[$key]['name']);
  239.             switch ($headers[$key]['name']{
  240.  
  241.                 case 'content-type':
  242.                     $content_type $this->_parseHeaderValue($headers[$key]['value']);
  243.  
  244.                     if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i'$content_type['value']$regs)) {
  245.                         $return->ctype_primary   = $regs[1];
  246.                         $return->ctype_secondary = $regs[2];
  247.                     }
  248.  
  249.                     if (isset($content_type['other'])) {
  250.                         while (list($p_name$p_valueeach($content_type['other'])) {
  251.                             $return->ctype_parameters[$p_name$p_value;
  252.                         }
  253.                     }
  254.                     break;
  255.  
  256.                 case 'content-disposition':
  257.                     $content_disposition $this->_parseHeaderValue($headers[$key]['value']);
  258.                     $return->disposition   = $content_disposition['value'];
  259.                     if (isset($content_disposition['other'])) {
  260.                         while (list($p_name$p_valueeach($content_disposition['other'])) {
  261.                             $return->d_parameters[$p_name$p_value;
  262.                         }
  263.                     }
  264.                     break;
  265.  
  266.                 case 'content-transfer-encoding':
  267.                     $content_transfer_encoding $this->_parseHeaderValue($headers[$key]['value']);
  268.                     break;
  269.             }
  270.         }
  271.  
  272.         if (isset($content_type)) {
  273.             switch (strtolower($content_type['value'])) {
  274.                 case 'text/plain':
  275.                     $encoding = isset($content_transfer_encoding$content_transfer_encoding['value''7bit';
  276.                     $this->_include_bodies $return->body = ($this->_decode_bodies $this->_decodeBody($body$encoding$body: null;
  277.                     break;
  278.  
  279.                 case 'text/html':
  280.                     $encoding = isset($content_transfer_encoding$content_transfer_encoding['value''7bit';
  281.                     $this->_include_bodies $return->body = ($this->_decode_bodies $this->_decodeBody($body$encoding$body: null;
  282.                     break;
  283.  
  284.                 case 'multipart/parallel':
  285.                 case 'multipart/report'// RFC1892
  286.                 case 'multipart/signed'// PGP
  287.                 case 'multipart/digest':
  288.                 case 'multipart/alternative':
  289.                 case 'multipart/related':
  290.                 case 'multipart/mixed':
  291.                     if(!isset($content_type['other']['boundary'])){
  292.                         $this->_error 'No boundary found for ' $content_type['value'' part';
  293.                         return false;
  294.                     }
  295.  
  296.                     $default_ctype (strtolower($content_type['value']=== 'multipart/digest''message/rfc822' 'text/plain';
  297.  
  298.                     $parts $this->_boundarySplit($body$content_type['other']['boundary']);
  299.                     for ($i = 0; $i count($parts)$i++{
  300.                         list($part_header$part_body$this->_splitBodyHeader($parts[$i]);
  301.                         $part $this->_decode($part_header$part_body$default_ctype);
  302.                         if($part === false)
  303.                             $part $this->raiseError($this->_error);
  304.                         $return->parts[$part;
  305.                     }
  306.                     break;
  307.  
  308.                 case 'message/rfc822':
  309.                     $obj &new Mail_mimeDecode($body);
  310.                     $return->parts[$obj->decode(array('include_bodies' => $this->_include_bodies,
  311.                                                           'decode_bodies'  => $this->_decode_bodies,
  312.                                                           'decode_headers' => $this->_decode_headers));
  313.                     unset($obj);
  314.                     break;
  315.  
  316.                 default:
  317.                     if(!isset($content_transfer_encoding['value']))
  318.                         $content_transfer_encoding['value''7bit';
  319.                     $this->_include_bodies $return->body = ($this->_decode_bodies $this->_decodeBody($body$content_transfer_encoding['value']$body: null;
  320.                     break;
  321.             }
  322.  
  323.         else {
  324.             $ctype explode('/'$default_ctype);
  325.             $return->ctype_primary   = $ctype[0];
  326.             $return->ctype_secondary = $ctype[1];
  327.             $this->_include_bodies $return->body = ($this->_decode_bodies $this->_decodeBody($body$body: null;
  328.         }
  329.  
  330.         return $return;
  331.     }
  332.  
  333.     /**
  334.      * Given the output of the above function, this will return an
  335.      * array of references to the parts, indexed by mime number.
  336.      *
  337.      * @param  object $structure   The structure to go through
  338.      * @param  string $mime_number Internal use only.
  339.      * @return array               Mime numbers
  340.      */
  341.     function &getMimeNumbers(&$structure$no_refs = false$mime_number ''$prepend '')
  342.     {
  343.         $return = array();
  344.         if (!empty($structure->parts)) {
  345.             if ($mime_number != ''{
  346.                 $structure->mime_id = $prepend $mime_number;
  347.                 $return[$prepend $mime_number&$structure;
  348.             }
  349.             for ($i = 0; $i count($structure->parts)$i++{
  350.  
  351.             
  352.                 if (!empty($structure->headers['content-type']AND substr(strtolower($structure->headers['content-type'])08== 'message/'{
  353.                     $prepend      $prepend $mime_number '.';
  354.                     $_mime_number '';
  355.                 else {
  356.                     $_mime_number ($mime_number == '' $i + 1 : sprintf('%s.%s'$mime_number$i + 1));
  357.                 }
  358.  
  359.                 $arr &Mail_mimeDecode::getMimeNumbers($structure->parts[$i]$no_refs$_mime_number$prepend);
  360.                 foreach ($arr as $key => $val{
  361.                     $no_refs $return[$key'' $return[$key&$arr[$key];
  362.                 }
  363.             }
  364.         else {
  365.             if ($mime_number == ''{
  366.                 $mime_number '1';
  367.             }
  368.             $structure->mime_id = $prepend $mime_number;
  369.             $no_refs $return[$prepend $mime_number'' $return[$prepend $mime_number&$structure;
  370.         }
  371.         
  372.         return $return;
  373.     }
  374.  
  375.     /**
  376.      * Given a string containing a header and body
  377.      * section, this function will split them (at the first
  378.      * blank line) and return them.
  379.      *
  380.      * @param string Input to split apart
  381.      * @return array Contains header and body section
  382.      * @access private
  383.      */
  384.     function _splitBodyHeader($input)
  385.     {
  386.         if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s"$input$match)) {
  387.             return array($match[1]$match[2]);
  388.         }
  389.         $this->_error 'Could not split header and body';
  390.         return false;
  391.     }
  392.  
  393.     /**
  394.      * Parse headers given in $input and return
  395.      * as assoc array.
  396.      *
  397.      * @param string Headers to parse
  398.      * @return array Contains parsed headers
  399.      * @access private
  400.      */
  401.     function _parseHeaders($input)
  402.     {
  403.  
  404.         if ($input !== ''{
  405.             // Unfold the input
  406.             $input   preg_replace("/\r?\n/""\r\n"$input);
  407.             $input   preg_replace("/\r\n(\t| )+/"' '$input);
  408.             $headers explode("\r\n"trim($input));
  409.  
  410.             foreach ($headers as $value{
  411.                 $hdr_name substr($value0$pos strpos($value':'));
  412.                 $hdr_value substr($value$pos+1);
  413.                 if($hdr_value[0== ' ')
  414.                     $hdr_value substr($hdr_value1);
  415.  
  416.                 $return[= array(
  417.                                   'name'  => $hdr_name,
  418.                                   'value' => $this->_decode_headers $this->_decodeHeader($hdr_value$hdr_value
  419.                                  );
  420.             }
  421.         else {
  422.             $return = array();
  423.         }
  424.  
  425.         return $return;
  426.     }
  427.  
  428.     /**
  429.      * Function to parse a header value,
  430.      * extract first part, and any secondary
  431.      * parts (after ;) This function is not as
  432.      * robust as it could be. Eg. header comments
  433.      * in the wrong place will probably break it.
  434.      *
  435.      * @param string Header value to parse
  436.      * @return array Contains parsed result
  437.      * @access private
  438.      */
  439.     function _parseHeaderValue($input)
  440.     {
  441.  
  442.         if (($pos strpos($input';')) !== false{
  443.  
  444.             $return['value'trim(substr($input0$pos));
  445.             $input trim(substr($input$pos+1));
  446.  
  447.             if (strlen($input> 0{
  448.  
  449.                 // This splits on a semi-colon, if there's no preceeding backslash
  450.                 // Now works with quoted values; had to glue the \; breaks in PHP
  451.                 // the regex is already bordering on incomprehensible
  452.                 $splitRegex '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
  453.                 preg_match_all($splitRegex$input$matches);
  454.                 $parameters = array();
  455.                 for ($i=0; $i<count($matches[0])$i++{
  456.                     $param $matches[0][$i];
  457.                     while (substr($param-2== '\;'{
  458.                         $param .= $matches[0][++$i];
  459.                     }
  460.                     $parameters[$param;
  461.                 }
  462.  
  463.                 for ($i = 0; $i count($parameters)$i++{
  464.                     $param_name  trim(substr($parameters[$i]0$pos strpos($parameters[$i]'='))"'\";\t\\ ");
  465.                     $param_value trim(str_replace('\;'';'substr($parameters[$i]$pos + 1))"'\";\t\\ ");
  466.                     if ($param_value[0== '"'{
  467.                         $param_value substr($param_value1-1);
  468.                     }
  469.                     $return['other'][$param_name$param_value;
  470.                     $return['other'][strtolower($param_name)$param_value;
  471.                 }
  472.             }
  473.         else {
  474.             $return['value'trim($input);
  475.         }
  476.  
  477.         return $return;
  478.     }
  479.  
  480.     /**
  481.      * This function splits the input based
  482.      * on the given boundary
  483.      *
  484.      * @param string Input to parse
  485.      * @return array Contains array of resulting mime parts
  486.      * @access private
  487.      */
  488.     function _boundarySplit($input$boundary)
  489.     {
  490.         $parts = array();
  491.  
  492.         $bs_possible substr($boundary2-2);
  493.         $bs_check '\"' $bs_possible '\"';
  494.  
  495.         if ($boundary == $bs_check{
  496.             $boundary $bs_possible;
  497.         }
  498.  
  499.         $tmp explode('--' $boundary$input);
  500.  
  501.         for ($i = 1; $i count($tmp- 1; $i++{
  502.             $parts[$tmp[$i];
  503.         }
  504.  
  505.         return $parts;
  506.     }
  507.  
  508.     /**
  509.      * Given a header, this function will decode it
  510.      * according to RFC2047. Probably not *exactly*
  511.      * conformant, but it does pass all the given
  512.      * examples (in RFC2047).
  513.      *
  514.      * @param string Input header value to decode
  515.      * @return string Decoded header value
  516.      * @access private
  517.      */
  518.     function _decodeHeader($input)
  519.     {
  520.         // Remove white space between encoded-words
  521.         $input preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i''\1=?'$input);
  522.  
  523.         // For each encoded-word...
  524.         while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i'$input$matches)) {
  525.  
  526.             $encoded  $matches[1];
  527.             $charset  $matches[2];
  528.             $encoding $matches[3];
  529.             $text     $matches[4];
  530.  
  531.             switch (strtolower($encoding)) {
  532.                 case 'b':
  533.                     $text base64_decode($text);
  534.                     break;
  535.  
  536.                 case 'q':
  537.                     $text str_replace('_'' '$text);
  538.                     preg_match_all('/=([a-f0-9]{2})/i'$text$matches);
  539.                     foreach($matches[1as $value)
  540.                         $text str_replace('='.$valuechr(hexdec($value))$text);
  541.                     break;
  542.             }
  543.  
  544.             $input str_replace($encoded$text$input);
  545.         }
  546.  
  547.         return $input;
  548.     }
  549.  
  550.     /**
  551.      * Given a body string and an encoding type,
  552.      * this function will decode and return it.
  553.      *
  554.      * @param  string Input body to decode
  555.      * @param  string Encoding type to use.
  556.      * @return string Decoded body
  557.      * @access private
  558.      */
  559.     function _decodeBody($input$encoding '7bit')
  560.     {
  561.         switch (strtolower($encoding)) {
  562.             case '7bit':
  563.                 return $input;
  564.                 break;
  565.  
  566.             case 'quoted-printable':
  567.                 return $this->_quotedPrintableDecode($input);
  568.                 break;
  569.  
  570.             case 'base64':
  571.                 return base64_decode($input);
  572.                 break;
  573.  
  574.             default:
  575.                 return $input;
  576.         }
  577.     }
  578.  
  579.     /**
  580.      * Given a quoted-printable string, this
  581.      * function will decode and return it.
  582.      *
  583.      * @param  string Input body to decode
  584.      * @return string Decoded body
  585.      * @access private
  586.      */
  587.     function _quotedPrintableDecode($input)
  588.     {
  589.         // Remove soft line breaks
  590.         $input preg_replace("/=\r?\n/"''$input);
  591.  
  592.         // Replace encoded characters
  593.         $input preg_replace('/=([a-f0-9]{2})/ie'"chr(hexdec('\\1'))"$input);
  594.  
  595.         return $input;
  596.     }
  597.  
  598.     /**
  599.      * Checks the input for uuencoded files and returns
  600.      * an array of them. Can be called statically, eg:
  601.      *
  602.      * $files =& Mail_mimeDecode::uudecode($some_text);
  603.      *
  604.      * It will check for the begin 666 ... end syntax
  605.      * however and won't just blindly decode whatever you
  606.      * pass it.
  607.      *
  608.      * @param  string Input body to look for attahcments in
  609.      * @return array  Decoded bodies, filenames and permissions
  610.      * @access public
  611.      * @author Unknown
  612.      */
  613.     function &uudecode($input)
  614.     {
  615.         // Find all uuencoded sections
  616.         preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us"$input$matches);
  617.  
  618.         for ($j = 0; $j count($matches[3])$j++{
  619.  
  620.             $str      $matches[3][$j];
  621.             $filename $matches[2][$j];
  622.             $fileperm $matches[1][$j];
  623.  
  624.             $file '';
  625.             $str preg_split("/\r?\n/"trim($str));
  626.             $strlen count($str);
  627.  
  628.             for ($i = 0; $i $strlen$i++{
  629.                 $pos = 1;
  630.                 $d = 0;
  631.                 $len=(int)(((ord(substr($str[$i],0,1)) -32' '077);
  632.  
  633.                 while (($d + 3 <= $lenAND ($pos + 4 <= strlen($str[$i]))) {
  634.                     $c0 (ord(substr($str[$i],$pos,1)) ^ 0x20);
  635.                     $c1 (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
  636.                     $c2 (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
  637.                     $c3 (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
  638.                     $file .= chr(((($c0 ' '077<< 2((($c1 ' '077>> 4));
  639.  
  640.                     $file .= chr(((($c1 ' '077<< 4((($c2 ' '077>> 2));
  641.  
  642.                     $file .= chr(((($c2 ' '077<< 6|  (($c3 ' '077));
  643.  
  644.                     $pos += 4;
  645.                     $d += 3;
  646.                 }
  647.  
  648.                 if (($d + 2 <= $len&& ($pos + 3 <= strlen($str[$i]))) {
  649.                     $c0 (ord(substr($str[$i],$pos,1)) ^ 0x20);
  650.                     $c1 (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
  651.                     $c2 (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
  652.                     $file .= chr(((($c0 ' '077<< 2((($c1 ' '077>> 4));
  653.  
  654.                     $file .= chr(((($c1 ' '077<< 4((($c2 ' '077>> 2));
  655.  
  656.                     $pos += 3;
  657.                     $d += 2;
  658.                 }
  659.  
  660.                 if (($d + 1 <= $len&& ($pos + 2 <= strlen($str[$i]))) {
  661.                     $c0 (ord(substr($str[$i],$pos,1)) ^ 0x20);
  662.                     $c1 (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
  663.                     $file .= chr(((($c0 ' '077<< 2((($c1 ' '077>> 4));
  664.  
  665.                 }
  666.             }
  667.             $files[= array('filename' => $filename'fileperm' => $fileperm'filedata' => $file);
  668.         }
  669.  
  670.         return $files;
  671.     }
  672.  
  673.     /**
  674.      * getSendArray() returns the arguments required for Mail::send()
  675.      * used to build the arguments for a mail::send() call
  676.      *
  677.      * Usage:
  678.      * $mailtext = Full email (for example generated by a template)
  679.      * $decoder = new Mail_mimeDecode($mailtext);
  680.      * $parts =  $decoder->getSendArray();
  681.      * if (!PEAR::isError($parts) {
  682.      *     list($recipents,$headers,$body) = $parts;
  683.      *     $mail = Mail::factory('smtp');
  684.      *     $mail->send($recipents,$headers,$body);
  685.      * } else {
  686.      *     echo $parts->message;
  687.      * }
  688.      * @return mixed   array of recipeint, headers,body or Pear_Error
  689.      * @access public
  690.      * @author Alan Knowles <alan@akbkhome.com>
  691.      */
  692.     function getSendArray()
  693.     {
  694.         // prevent warning if this is not set
  695.         $this->_decode_headers = FALSE;
  696.         $headerlist =$this->_parseHeaders($this->_header);
  697.         $to "";
  698.         if (!$headerlist{
  699.             return $this->raiseError("Message did not contain headers");
  700.         }
  701.         foreach($headerlist as $item{
  702.             $header[$item['name']] $item['value'];
  703.             switch (strtolower($item['name'])) {
  704.                 case "to":
  705.                 case "cc":
  706.                 case "bcc":
  707.                     $to ",".$item['value'];
  708.                 default:
  709.                    break;
  710.             }
  711.         }
  712.         if ($to == ""{
  713.             return $this->raiseError("Message did not contain any recipents");
  714.         }
  715.         $to substr($to,1);
  716.         return array($to,$header,$this->_body);
  717.     
  718.  
  719.     /**
  720.      * Returns a xml copy of the output of
  721.      * Mail_mimeDecode::decode. Pass the output in as the
  722.      * argument. This function can be called statically. Eg:
  723.      *
  724.      * $output = $obj->decode();
  725.      * $xml    = Mail_mimeDecode::getXML($output);
  726.      *
  727.      * The DTD used for this should have been in the package. Or
  728.      * alternatively you can get it from cvs, or here:
  729.      * http://www.phpguru.org/xmail/xmail.dtd.
  730.      *
  731.      * @param  object Input to convert to xml. This should be the
  732.      *                 output of the Mail_mimeDecode::decode function
  733.      * @return string XML version of input
  734.      * @access public
  735.      */
  736.     function getXML($input)
  737.     {
  738.         $crlf    =  "\r\n";
  739.         $output  '<?xml version=\'1.0\'?>' $crlf .
  740.                    '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' $crlf .
  741.                    '<email>' $crlf .
  742.                    Mail_mimeDecode::_getXML($input.
  743.                    '</email>';
  744.  
  745.         return $output;
  746.     }
  747.  
  748.     /**
  749.      * Function that does the actual conversion to xml. Does a single
  750.      * mimepart at a time.
  751.      *
  752.      * @param  object  Input to convert to xml. This is a mimepart object.
  753.      *                  It may or may not contain subparts.
  754.      * @param  integer Number of tabs to indent
  755.      * @return string  XML version of input
  756.      * @access private
  757.      */
  758.     function _getXML($input$indent = 1)
  759.     {
  760.         $htab    =  "\t";
  761.         $crlf    =  "\r\n";
  762.         $output  =  '';
  763.         $headers @(array)$input->headers;
  764.  
  765.         foreach ($headers as $hdr_name => $hdr_value{
  766.  
  767.             // Multiple headers with this name
  768.             if (is_array($headers[$hdr_name])) {
  769.                 for ($i = 0; $i count($hdr_value)$i++{
  770.                     $output .= Mail_mimeDecode::_getXML_helper($hdr_name$hdr_value[$i]$indent);
  771.                 }
  772.  
  773.             // Only one header of this sort
  774.             else {
  775.                 $output .= Mail_mimeDecode::_getXML_helper($hdr_name$hdr_value$indent);
  776.             }
  777.         }
  778.  
  779.         if (!empty($input->parts)) {
  780.             for ($i = 0; $i count($input->parts)$i++{
  781.                 $output .= $crlf str_repeat($htab$indent'<mimepart>' $crlf .
  782.                            Mail_mimeDecode::_getXML($input->parts[$i]$indent+1.
  783.                            str_repeat($htab$indent'</mimepart>' $crlf;
  784.             }
  785.         elseif (isset($input->body)) {
  786.             $output .= $crlf str_repeat($htab$indent'<body><![CDATA[' .
  787.                        $input->body . ']]></body>' $crlf;
  788.         }
  789.  
  790.         return $output;
  791.     }
  792.  
  793.     /**
  794.      * Helper function to _getXML(). Returns xml of a header.
  795.      *
  796.      * @param  string  Name of header
  797.      * @param  string  Value of header
  798.      * @param  integer Number of tabs to indent
  799.      * @return string  XML version of input
  800.      * @access private
  801.      */
  802.     function _getXML_helper($hdr_name$hdr_value$indent)
  803.     {
  804.         $htab   "\t";
  805.         $crlf   "\r\n";
  806.         $return '';
  807.  
  808.         $new_hdr_value ($hdr_name != 'received'Mail_mimeDecode::_parseHeaderValue($hdr_value: array('value' => $hdr_value);
  809.         $new_hdr_name  str_replace(' ''-'ucwords(str_replace('-'' '$hdr_name)));
  810.  
  811.         // Sort out any parameters
  812.         if (!empty($new_hdr_value['other'])) {
  813.             foreach ($new_hdr_value['other'as $paramname => $paramvalue{
  814.                 $params[str_repeat($htab$indent$htab '<parameter>' $crlf .
  815.                             str_repeat($htab$indent$htab $htab '<paramname>' htmlspecialchars($paramname'</paramname>' $crlf .
  816.                             str_repeat($htab$indent$htab $htab '<paramvalue>' htmlspecialchars($paramvalue'</paramvalue>' $crlf .
  817.                             str_repeat($htab$indent$htab '</parameter>' $crlf;
  818.             }
  819.  
  820.             $params implode(''$params);
  821.         else {
  822.             $params '';
  823.         }
  824.  
  825.         $return str_repeat($htab$indent'<header>' $crlf .
  826.                   str_repeat($htab$indent$htab '<headername>' htmlspecialchars($new_hdr_name'</headername>' $crlf .
  827.                   str_repeat($htab$indent$htab '<headervalue>' htmlspecialchars($new_hdr_value['value']'</headervalue>' $crlf .
  828.                   $params .
  829.                   str_repeat($htab$indent'</header>' $crlf;
  830.  
  831.         return $return;
  832.     }
  833.  
  834. // End of class
  835. ?>

Documentation generated on Mon, 11 Mar 2019 14:31:07 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.