phpDocumentor pond
[ class tree: pond ] [ index: pond ] [ all elements ]

Source for file mime_parser.php

Documentation is available at mime_parser.php

  1. <?php
  2. /**
  3.  * mime_parser.php
  4.  *
  5.  * @package pond
  6.  */
  7.  
  8. /**
  9.  * dummy docblock makes error-free autodocs
  10.  */
  11. define('MIME_PARSER_START',        1);
  12. /**
  13.  * dummy docblock makes error-free autodocs
  14.  */
  15. define('MIME_PARSER_HEADER',       2);
  16. /**
  17.  * dummy docblock makes error-free autodocs
  18.  */
  19. define('MIME_PARSER_HEADER_VALUE'3);
  20. /**
  21.  * dummy docblock makes error-free autodocs
  22.  */
  23. define('MIME_PARSER_BODY',         4);
  24. /**
  25.  * dummy docblock makes error-free autodocs
  26.  */
  27. define('MIME_PARSER_BODY_START',   5);
  28. /**
  29.  * dummy docblock makes error-free autodocs
  30.  */
  31. define('MIME_PARSER_BODY_DATA',    6);
  32. /**
  33.  * dummy docblock makes error-free autodocs
  34.  */
  35. define('MIME_PARSER_BODY_DONE',    7);
  36. /**
  37.  * dummy docblock makes error-free autodocs
  38.  */
  39. define('MIME_PARSER_END',          8);
  40.  
  41. /**
  42.  * dummy docblock makes error-free autodocs
  43.  */
  44. define('MIME_MESSAGE_START',            1);
  45. /**
  46.  * dummy docblock makes error-free autodocs
  47.  */
  48. define('MIME_MESSAGE_GET_HEADER_NAME',  2);
  49. /**
  50.  * dummy docblock makes error-free autodocs
  51.  */
  52. define('MIME_MESSAGE_GET_HEADER_VALUE'3);
  53. /**
  54.  * dummy docblock makes error-free autodocs
  55.  */
  56. define('MIME_MESSAGE_GET_BODY',         4);
  57. /**
  58.  * dummy docblock makes error-free autodocs
  59.  */
  60. define('MIME_MESSAGE_GET_BODY_PART',    5);
  61.  
  62. /**
  63.  * dummy docblock makes error-free autodocs
  64.  */
  65. define('MIME_ADDRESS_START',            1);
  66. /**
  67.  * dummy docblock makes error-free autodocs
  68.  */
  69. define('MIME_ADDRESS_FIRST',            2);
  70.  
  71. /**
  72.  *{metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?>
  73.  *<class>
  74.  *
  75.  *    <package>net.manuellemos.mimeparser</package>
  76.  *
  77.  *    <copyright>Copyright © (C) Manuel Lemos 2006</copyright>
  78.  *    <title>MIME parser</title>
  79.  *    <author>Manuel Lemos</author>
  80.  *    <authoraddress>mlemos-at-acm.org</authoraddress>
  81.  *
  82.  *    <documentation>
  83.  *        <idiom>en</idiom>
  84.  *        <purpose>Parse MIME encapsulated e-mail message data compliant with
  85.  *            the RFC 2822 or aggregated in mbox format.</purpose>
  86.  *        <usage>Use the function <functionlink>Decode</functionlink> function
  87.  *            to retrieve the structure of the messages to be parsed. Adjust its
  88.  *            parameters to tell how to return the decoded body data.
  89.  *            Use the <tt>SaveBody</tt> parameter to make the body parts be saved
  90.  *            to files when the message is larger than the available memory. Use
  91.  *            the <tt>SkipBody</tt> parameter to just retrieve the message
  92.  *            structure without returning the body data.<paragraphbreak />
  93.  *            If the message data is an archive that may contain multiple messages
  94.  *            aggregated in the mbox format, set the variable
  95.  *            <variablelink>mbox</variablelink> to <booleanvalue>1</booleanvalue>.</usage>
  96.  *    </documentation>
  97.  *
  98.  *{/metadocument}
  99.  *
  100.  * @package pond
  101.  */
  102.  
  103. {
  104. /*
  105. {metadocument}
  106.     <variable>
  107.         <name>error</name>
  108.         <type>STRING</type>
  109.         <value></value>
  110.         <documentation>
  111.             <purpose>Store the message that is returned when an error
  112.                 occurs.</purpose>
  113.             <usage>Check this variable to understand what happened when a call to
  114.                 any of the class functions has failed.<paragraphbreak />
  115.                 This class uses cumulative error handling. This means that if one
  116.                 class functions that may fail is called and this variable was
  117.                 already set to an error message due to a failure in a previous call
  118.                 to the same or other function, the function will also fail and does
  119.                 not do anything.<paragraphbreak />
  120.                 This allows programs using this class to safely call several
  121.                 functions that may fail and only check the failure condition after
  122.                 the last function call.<paragraphbreak />
  123.                 Just set this variable to an empty string to clear the error
  124.                 condition.</usage>
  125.         </documentation>
  126.     </variable>
  127. {/metadocument}
  128. */
  129.     var $error='';
  130.  
  131. /*
  132. {metadocument}
  133.     <variable>
  134.         <name>error_position</name>
  135.         <type>INTEGER</type>
  136.         <value>-1</value>
  137.         <documentation>
  138.             <purpose>Point to the position of the message data or file that
  139.                 refers to the last error that occurred.</purpose>
  140.             <usage>Check this variable to determine the relevant position of the
  141.                 message when a parsing error occurs.</usage>
  142.         </documentation>
  143.     </variable>
  144. {/metadocument}
  145. */
  146.     var $error_position = -1;
  147.  
  148. /*
  149. {metadocument}
  150.     <variable>
  151.         <name>mbox</name>
  152.         <type>BOOLEAN</type>
  153.         <value>0</value>
  154.         <documentation>
  155.             <purpose>Specify whether the message data to parse is a single RFC
  156.                 2822 message or it is an archive that contain multiple messages in
  157.                 the mbox format.</purpose>
  158.             <usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
  159.                 it is intended to parse an mbox message archive.<br />
  160.                 mbox archives may contain multiple messages. Each message starts
  161.                 with the header <tt>From</tt>. Since all valid RFC 2822 headers
  162.                 must with a colon, the class will fail to parse a mbox archive if
  163.                 this variable is set to <booleanvalue>0</booleanvalue>.</usage>
  164.         </documentation>
  165.     </variable>
  166. {/metadocument}
  167. */
  168.     var $mbox = 0;
  169.  
  170. /*
  171. {metadocument}
  172.     <variable>
  173.         <name>decode_headers</name>
  174.         <type>BOOLEAN</type>
  175.         <value>1</value>
  176.         <documentation>
  177.             <purpose>Specify whether the message headers should be decoded.</purpose>
  178.             <usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
  179.                 necessary to decode message headers that may have non-ASCII
  180.                 characters and use other character set encodings.</usage>
  181.         </documentation>
  182.     </variable>
  183. {/metadocument}
  184. */
  185.     var $decode_headers = 1;
  186.  
  187. /*
  188. {metadocument}
  189.     <variable>
  190.         <name>decode_bodies</name>
  191.         <type>BOOLEAN</type>
  192.         <value>1</value>
  193.         <documentation>
  194.             <purpose>Specify whether the message body parts should be decoded.</purpose>
  195.             <usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
  196.                 necessary to parse the message bodies and extract its part
  197.                 structure.</usage>
  198.         </documentation>
  199.     </variable>
  200. {/metadocument}
  201. */
  202.     var $decode_bodies = 1;
  203.  
  204. /*
  205. {metadocument}
  206.     <variable>
  207.         <name>extract_addresses</name>
  208.         <type>BOOLEAN</type>
  209.         <value>1</value>
  210.         <documentation>
  211.             <purpose>Specify whether the message headers that usually contain
  212.                 e-mail addresses should be parsed and the addresses should be
  213.                 extracted by the <functionlink>Decode</functionlink> function.</purpose>
  214.             <usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
  215.                 necessary to extract the e-mail addresses contained in certain
  216.                 message headers.<paragraphbreak />
  217.                 The headers to be parsed are defined by the
  218.                 <variablelink>address_headers</variablelink> variable.<paragraphbreak />
  219.                 The parsed addresses are returned by the
  220.                 <tt>ExtractedAddresses</tt> entry of the <argumentlink>
  221.                     <function>Decode</function>
  222.                     <argument>decoded</argument>
  223.                 </argumentlink> argument of the
  224.                 <functionlink>Decode</functionlink> function.</usage>
  225.         </documentation>
  226.     </variable>
  227. {/metadocument}
  228. */
  229.     var $extract_addresses = 1;
  230.  
  231. /*
  232. {metadocument}
  233.     <variable>
  234.         <name>address_headers</name>
  235.         <type>HASH</type>
  236.         <value></value>
  237.         <documentation>
  238.             <purpose>Specify which headers contain addresses that should be
  239.                 parsed and extracted.</purpose>
  240.             <usage>Change this variable if you need to extract e-mail addresses
  241.                 from a different list of message headers.<paragraphbreak />
  242.                 It must be set to an associative array with keys set to the names
  243.                 of the headers to be parsed including the colon. The array values
  244.                 must be set to a boolean flag to tell whether the headers with the
  245.                 respective name should be parsed. The header names must be in lower
  246.                 case.<paragraphbreak />
  247.                 By default the class addresses from the headers:
  248.                 <stringvalue>from:</stringvalue>, <stringvalue>to:</stringvalue>,
  249.                 <stringvalue>cc:</stringvalue>, <stringvalue>bcc:</stringvalue>,
  250.                 <stringvalue>return-path:</stringvalue>,
  251.                 <stringvalue>reply-to:</stringvalue> and
  252.                 <stringvalue>disposition-notification-to:</stringvalue>.</usage>
  253.         </documentation>
  254.     </variable>
  255. {/metadocument}
  256. */
  257.     var $address_headers = array(
  258.         'from:' => 1,
  259.         'to:' => 1,
  260.         'cc:' => 1,
  261.         'bcc:' => 1,
  262.         'return-path:'=>1,
  263.         'reply-to:'=>1,
  264.         'disposition-notification-to:'=>1
  265.     );
  266.  
  267. /*
  268. {metadocument}
  269.     <variable>
  270.         <name>ignore_syntax_errors</name>
  271.         <type>BOOLEAN</type>
  272.         <value>1</value>
  273.         <documentation>
  274.             <purpose>Specify whether the class should ignore syntax errors in
  275.                 malformed messages.</purpose>
  276.             <usage>Set this variable to <booleanvalue>0</booleanvalue> if it is
  277.                 necessary to verify whether message data may be corrupted due to
  278.                 to eventual bugs in the program that generated the
  279.                 message.<paragraphbreak />
  280.                 Currently the class only ignores some types of syntax errors.
  281.                 Other syntax errors may still cause the
  282.                 <functionlink>Decode</functionlink> to fail.</usage>
  283.         </documentation>
  284.     </variable>
  285. {/metadocument}
  286. */
  287.     var $ignore_syntax_errors=1;
  288.  
  289. /*
  290. {metadocument}
  291.     <variable>
  292.         <name>warnings</name>
  293.         <type>HASH</type>
  294.         <value></value>
  295.         <documentation>
  296.             <purpose>Return a list of positions of the original message that
  297.                 contain syntax errors.</purpose>
  298.             <usage>Check this variable to retrieve eventual message syntax
  299.                 errors that were ignored when the
  300.                 <variablelink>ignore_syntax_errors</variablelink> is set to
  301.                 <booleanvalue>1</booleanvalue>.<paragraphbreak />
  302.                 The indexes of this array are the positions of the errors. The
  303.                 array values are the corresponding syntax error messages.</usage>
  304.         </documentation>
  305.     </variable>
  306. {/metadocument}
  307. */
  308.     var $warnings=array();
  309.  
  310.     /* Private variables */
  311.     var $state = MIME_PARSER_START;
  312.     var $buffer = '';
  313.     var $buffer_position = 0;
  314.     var $offset = 0;
  315.     var $parts = array();
  316.     var $part_position = 0;
  317.     var $headers = array();
  318.     var $body_parser;
  319.     var $body_parser_state = MIME_PARSER_BODY_DONE;
  320.     var $body_buffer = '';
  321.     var $body_buffer_position = 0;
  322.     var $body_offset = 0;
  323.     var $current_header = '';
  324.     var $file;
  325.     var $body_file;
  326.     var $position = 0;
  327.     var $body_part_number = 1;
  328.     var $next_token = '';
  329.  
  330.     /* Private functions */
  331.  
  332. /**
  333.  * dummy docblock makes error-free autodocs
  334.  */
  335.     Function SetError($error)
  336.     {
  337.         $this->error = $error;
  338.         return(0);
  339.     }
  340.  
  341. /**
  342.  * dummy docblock makes error-free autodocs
  343.  */
  344.     Function SetErrorWithContact($error)
  345.     {
  346.         return($this->SetError($error.'. Please contact the author Manuel Lemos <mlemos@acm.org> and send a copy of this message to let him add support for this kind of messages'));
  347.     }
  348.  
  349. /**
  350.  * dummy docblock makes error-free autodocs
  351.  */
  352.     Function SetPositionedError($error$position)
  353.     {
  354.         $this->error_position = $position;
  355.         return($this->SetError($error));
  356.     }
  357.  
  358. /**
  359.  * dummy docblock makes error-free autodocs
  360.  */
  361.     Function SetPositionedWarning($error$position)
  362.     {
  363.         if(!$this->ignore_syntax_errors)
  364.             return($this->SetPositionedError($error$position));
  365.         $this->warnings[$position]=$error;
  366.         return(1);
  367.     }
  368.  
  369. /**
  370.  * dummy docblock makes error-free autodocs
  371.  */
  372.     Function SetPHPError($error&$php_error_message)
  373.     {
  374.         if(IsSet($php_error_message)
  375.         && strlen($php_error_message))
  376.             $error .= ': '.$php_error_message;
  377.         return($this->SetError($error));
  378.     }
  379.  
  380. /**
  381.  * dummy docblock makes error-free autodocs
  382.  */
  383.     Function ResetParserState()
  384.     {
  385.         $this->error='';
  386.         $this->error_position = -1;
  387.         $this->state = MIME_PARSER_START;
  388.         $this->buffer = '';
  389.         $this->buffer_position = 0;
  390.         $this->offset = 0;
  391.         $this->parts = array();
  392.         $this->part_position = 0;
  393.         $this->headers = array();
  394.         $this->body_parser_state = MIME_PARSER_BODY_DONE;
  395.         $this->body_buffer = '';
  396.         $this->body_buffer_position = 0;
  397.         $this->body_offset = 0;
  398.         $this->current_header = '';
  399.         $this->position = 0;
  400.         $this->body_part_number = 1;
  401.         $this->next_token = '';
  402.     }
  403.  
  404. /**
  405.  * dummy docblock makes error-free autodocs
  406.  */
  407.     Function Tokenize($string,$separator="")
  408.     {
  409.         if(!strcmp($separator,""))
  410.         {
  411.             $separator=$string;
  412.             $string=$this->next_token;
  413.         }
  414.         for($character=0;$character<strlen($separator);$character++)
  415.         {
  416.             if(GetType($position=strpos($string,$separator[$character]))=='integer')
  417.                 $found=(IsSet($foundmin($found,$position$position);
  418.         }
  419.         if(IsSet($found))
  420.         {
  421.             $this->next_token=substr($string,$found+1);
  422.             return(substr($string,0,$found));
  423.         }
  424.         else
  425.         {
  426.             $this->next_token='';
  427.             return($string);
  428.         }
  429.     }
  430.  
  431. /**
  432.  * dummy docblock makes error-free autodocs
  433.  */
  434.     Function ParseStructuredHeader($value&$type&$parameters&$character_sets&$languages)
  435.     {
  436.         $type strtolower(trim($this->Tokenize($value';')));
  437.         $p trim($this->Tokenize(''));
  438.         $parameters $character_sets $languages array();
  439.         while(strlen($p))
  440.         {
  441.             $parameter trim(strtolower($this->Tokenize($p'=')));
  442.             $value trim($this->Tokenize(';'));
  443.             if(!strcmp($value[0]'"')
  444.             && !strcmp($value[strlen($value1]'"'))
  445.                 $value substr($value1strlen($value2);
  446.             $p trim($this->Tokenize(''));
  447.             if(($l=strlen($parameter))
  448.             && !strcmp($parameter[$l 1],'*'))
  449.             {
  450.                 $parameter=$this->Tokenize($parameter'*');
  451.                 if(IsSet($parameters[$parameter])
  452.                 && IsSet($character_sets[$parameter]))
  453.                     $value $parameters[$parameterUrlDecode($value);
  454.                 else
  455.                 {
  456.                     $character_sets[$parameterstrtolower($this->Tokenize($value'\''));
  457.                     $languages[$parameter$this->Tokenize('\'');
  458.                     $value UrlDecode($this->Tokenize(''));
  459.                 }
  460.             }
  461.             $parameters[$parameter$value;
  462.         }
  463.     }
  464.  
  465. /**
  466.  * dummy docblock makes error-free autodocs
  467.  */
  468.     Function FindStringLineBreak($string$position&$break&$line_break)
  469.     {
  470.         if(GetType($line_break=strpos($string$break="\n"$position))=='integer')
  471.         {
  472.             if($line_break>$position
  473.             && $string[$line_break-1]=="\r")
  474.             {
  475.                 $line_break--;
  476.                 $break="\r\n";
  477.             }
  478.             return(1);
  479.         }
  480.         return(GetType($line_break=strpos($string$break="\r"$position))=='integer');
  481.     }
  482.  
  483. /**
  484.  * dummy docblock makes error-free autodocs
  485.  */
  486.     Function FindLineBreak($position&$break&$line_break)
  487.     {
  488.         if(GetType($line_break=strpos($this->buffer$break="\r"$position))=='integer')
  489.         {
  490.             if(($n $line_break 1strlen($this->buffer)
  491.             && $this->buffer[$n]=="\n")
  492.                 $break="\r\n";
  493.             return(1);
  494.         }
  495.         return(GetType($line_break=strpos($this->buffer$break="\n"$position))=='integer');
  496.     }
  497.  
  498. /**
  499.  * dummy docblock makes error-free autodocs
  500.  */
  501.     Function FindBodyLineBreak($position&$break&$line_break)
  502.     {
  503.         if(GetType($line_break=strpos($this->body_buffer$break="\r"$position))=='integer')
  504.         {
  505.             if(($n $line_break 1strlen($this->body_buffer)
  506.             && $this->body_buffer[$n]=="\n")
  507.                 $break="\r\n";
  508.             return(1);
  509.         }
  510.         return(GetType($line_break=strpos($this->body_buffer$break="\n"$position))=='integer');
  511.     }
  512.  
  513. /**
  514.  * dummy docblock makes error-free autodocs
  515.  */
  516.     Function ParseHeaderString($body&$position&$headers)
  517.     {
  518.         $l strlen($body);
  519.         $headers array();
  520.         for(;$position $l;)
  521.         {
  522.             if($this->FindStringLineBreak($body$position$break$line_break))
  523.             {
  524.                 $line substr($body$position$line_break $position);
  525.                 $position $line_break strlen($break);
  526.             }
  527.             else
  528.             {
  529.                 $line substr($body$position);
  530.                 $position $l;
  531.             }
  532.             if(strlen($line)==0)
  533.                 break;
  534.             $h strtolower(strtok($line,':'));
  535.             $headers[$htrim(strtok(''));
  536.         }
  537.     }
  538.  
  539. /**
  540.  * dummy docblock makes error-free autodocs
  541.  */
  542.     Function ParsePart($end&$part&$need_more_data)
  543.     {
  544.         $need_more_data 0;
  545.         switch($this->state)
  546.         {
  547.             case MIME_PARSER_START:
  548.                 $part=array(
  549.                     'Type'=>'MessageStart',
  550.                     'Position'=>$this->offset + $this->buffer_position
  551.                 );
  552.                 $this->state = MIME_PARSER_HEADER;
  553.                 break;
  554.             case MIME_PARSER_HEADER:
  555.                 if($this->FindLineBreak($this->buffer_position$break$line_break))
  556.                 {
  557.                     $next $line_break strlen($break);
  558.                     if(!strcmp($break,"\r")
  559.                     && strlen($this->buffer== $next
  560.                     && !$end)
  561.                     {
  562.                         $need_more_data 1;
  563.                         break;
  564.                     }
  565.                     if($line_break==$this->buffer_position)
  566.                     {
  567.                         $part=array(
  568.                             'Type'=>'BodyStart',
  569.                             'Position'=>$this->offset + $this->buffer_position
  570.                         );
  571.                         $this->buffer_position = $next;
  572.                         $this->state = MIME_PARSER_BODY;
  573.                         break;
  574.                     }
  575.                 }
  576.                 if(GetType($colon=strpos($this->buffer':'$this->buffer_position))=='integer')
  577.                 {
  578.                     if(GetType($space=strpos(substr($this->buffer$this->buffer_position$colon $this->buffer_position)' '))=='integer')
  579.                     {
  580.                         if((!$this->mbox
  581.                         || strcmp(strtolower(substr($this->buffer$this->buffer_position$space))'from'))
  582.                         && !$this->SetPositionedWarning('invalid header name line'$this->buffer_position))
  583.                             return(0);
  584.                         $next $this->buffer_position + $space 1;
  585.                     }
  586.                     else
  587.                         $next $colon+1;
  588.                 }
  589.                 else
  590.                 {
  591.                     $need_more_data 1;
  592.                     break;
  593.                 }
  594.                 $part=array(
  595.                     'Type'=>'HeaderName',
  596.                     'Name'=>substr($this->buffer$this->buffer_position$next $this->buffer_position),
  597.                     'Position'=>$this->offset + $this->buffer_position
  598.                 );
  599.                 $this->buffer_position = $next;
  600.                 $this->state = MIME_PARSER_HEADER_VALUE;
  601.                 break;
  602.             case MIME_PARSER_HEADER_VALUE:
  603.                 $position $this->buffer_position;
  604.                 $value '';
  605.                 for(;;)
  606.                 {
  607.                     if($this->FindLineBreak($position$break$line_break))
  608.                     {
  609.                         $next $line_break strlen($break);
  610.                         $line substr($this->buffer$position$line_break $position);
  611.                         if(strlen($this->buffer== $next)
  612.                         {
  613.                             if(!$end)
  614.                             {
  615.                                 $need_more_data 1;
  616.                                 break 2;
  617.                             }
  618.                             $value .= $line;
  619.                             $part=array(
  620.                                 'Type'=>'HeaderValue',
  621.                                 'Value'=>$value,
  622.                                 'Position'=>$this->offset + $this->buffer_position
  623.                             );
  624.                             $this->buffer_position = $next;
  625.                             $this->state = MIME_PARSER_END;
  626.                             break ;
  627.                         }
  628.                         else
  629.                         {
  630.                             $character $this->buffer[$next];
  631.                             if(!strcmp($character' ')
  632.                             || !strcmp($character"\t"))
  633.                             {
  634.                                 $value .= $line;
  635.                                 $position $next;
  636.                             }
  637.                             else
  638.                             {
  639.                                 $value .= $line;
  640.                                 $part=array(
  641.                                     'Type'=>'HeaderValue',
  642.                                     'Value'=>$value,
  643.                                     'Position'=>$this->offset + $this->buffer_position
  644.                                 );
  645.                                 $this->buffer_position = $next;
  646.                                 $this->state = MIME_PARSER_HEADER;
  647.                                 break 2;
  648.                             }
  649.                         }
  650.                     }
  651.                     else
  652.                     {
  653.                         if(!$end)
  654.                         {
  655.                             $need_more_data 1;
  656.                             break;
  657.                         }
  658.                         else
  659.                         {
  660.                             $value .= substr($this->buffer$position);
  661.                             $part=array(
  662.                                 'Type'=>'HeaderValue',
  663.                                 'Value'=>$value,
  664.                                 'Position'=>$this->offset + $this->buffer_position
  665.                             );
  666.                             $this->buffer_position = strlen($this->buffer);
  667.                             $this->state = MIME_PARSER_END;
  668.                             break;
  669.                         }
  670.                     }
  671.                 }
  672.                 break;
  673.             case MIME_PARSER_BODY:
  674.                 if($this->mbox)
  675.                 {
  676.                     $add 0;
  677.                     $append='';
  678.                     if($this->FindLineBreak($this->buffer_position$break$line_break))
  679.                     {
  680.                         $next $line_break strlen($break);
  681.                         $following $next strlen($break);
  682.                         if($following >= strlen($this->buffer)
  683.                         || GetType($line=strpos($this->buffer$break$following))!='integer')
  684.                         {
  685.                             if(!$end)
  686.                             {
  687.                                 $need_more_data 1;
  688.                                 break;
  689.                             }
  690.                         }
  691.                         $start strtolower(substr($this->buffer$nextstrlen($break.'from ')));
  692.                         if(!strcmp($break.'from '$start))
  693.                         {
  694.                             if($line_break == $this->buffer_position)
  695.                             {
  696.                                 $part=array(
  697.                                     'Type'=>'MessageEnd',
  698.                                     'Position'=>$this->offset + $this->buffer_position
  699.                                 );
  700.                                 $this->buffer_position = $following;
  701.                                 $this->state = MIME_PARSER_START;
  702.                                 break;
  703.                             }
  704.                             else
  705.                                 $add strlen($break);
  706.                             $next $line_break;
  707.                         }
  708.                         else
  709.                         {
  710.                             $start strtolower(substr($this->buffer$nextstrlen('>from ')));
  711.                             if(!strcmp('>from '$start))
  712.                             {
  713.                                 $part=array(
  714.                                     'Type'=>'BodyData',
  715.                                     'Data'=>substr($this->buffer$this->buffer_position$next $this->buffer_position),
  716.                                     'Position'=>$this->offset + $this->buffer_position
  717.                                 );
  718.                                 $this->buffer_position = $next 1;
  719.                                 break;
  720.                             }
  721.                         }
  722.                     }
  723.                     else
  724.                     {
  725.                         if(!$end)
  726.                         {
  727.                             $need_more_data 1;
  728.                             break;
  729.                         }
  730.                         $next strlen($this->buffer);
  731.                         $append="\r\n";
  732.                     }
  733.                     if($next $this->buffer_position)
  734.                     {
  735.                         $part=array(
  736.                             'Type'=>'BodyData',
  737.                             'Data'=>substr($this->buffer$this->buffer_position$next $add $this->buffer_position).$append,
  738.                             'Position'=>$this->offset + $this->buffer_position
  739.                         );
  740.                     }
  741.                     elseif($end)
  742.                     {
  743.                         $part=array(
  744.                             'Type'=>'MessageEnd',
  745.                             'Position'=>$this->offset + $this->buffer_position
  746.                         );
  747.                         $this->state = MIME_PARSER_END;
  748.                     }
  749.                     $this->buffer_position = $next;
  750.                 }
  751.                 else
  752.                 {
  753.                     if(strlen($this->buffer)-$this->buffer_position)
  754.                     {
  755.                         $data=substr($this->buffer$this->buffer_positionstrlen($this->buffer$this->buffer_position);
  756.                         $end_line (!strcmp(substr($data,-1),"\n"|| !strcmp(substr($data,-1),"\r"));
  757.                         if($end
  758.                         && !$end_line)
  759.                         {
  760.                             $data.="\n";
  761.                             $end_line 1;
  762.                         }
  763.                         $offset $this->offset + $this->buffer_position;
  764.                         $this->buffer_position = strlen($this->buffer);
  765.                         $need_more_data !$end;
  766.                         if(!$end_line)
  767.                         {
  768.                             if(GetType($line_break=strrpos($data"\n"))=='integer'
  769.                             || GetType($line_break=strrpos($data"\r"))=='integer')
  770.                             {
  771.                                 $line_break++;
  772.                                 $this->buffer_position -= strlen($data$line_break;
  773.                                 $data substr($data0$line_break);
  774.                             }
  775.                         }
  776.                         $part=array(
  777.                             'Type'=>'BodyData',
  778.                             'Data'=>$data,
  779.                             'Position'=>$offset
  780.                         );
  781.                     }
  782.                     else
  783.                     {
  784.                         if($end)
  785.                         {
  786.                             $part=array(
  787.                                 'Type'=>'MessageEnd',
  788.                                 'Position'=>$this->offset + $this->buffer_position
  789.                             );
  790.                             $this->state = MIME_PARSER_END;
  791.                         }
  792.                         else
  793.                             $need_more_data 1;
  794.                     }
  795.                 }
  796.                 break;
  797.             default:
  798.                 return($this->SetPositionedError($this->state.' is not a valid parser state'$this->buffer_position));
  799.         }
  800.         return(1);
  801.     }
  802.  
  803. /**
  804.  * dummy docblock makes error-free autodocs
  805.  */
  806.     Function QueueBodyParts()
  807.     {
  808.         for(;;)
  809.         {
  810.             if(!$this->body_parser->GetPart($part,$end))
  811.                 return($this->SetError($this->body_parser->error));
  812.             if($end)
  813.                 return(1);
  814.             if(!IsSet($part['Part']))
  815.                 $part['Part']=$this->headers['Boundary'];
  816.             $this->parts[]=$part;
  817.         }
  818.     }
  819.  
  820. /**
  821.  * dummy docblock makes error-free autodocs
  822.  */
  823.     Function ParseParameters($value&$first&$parameters$return)
  824.     {
  825.         $first strtolower(trim(strtok($value';')));
  826.         $values trim(strtok(''));
  827.         $parameters array();
  828.         $return_value '';
  829.         while(strlen($values))
  830.         {
  831.             $parameter trim(strtolower(strtok($values'=')));
  832.             $value trim(strtok(';'));
  833.             if(!strcmp($value[0]'"')
  834.             && !strcmp($value[strlen($value1]'"'))
  835.                 $value substr($value1strlen($value2);
  836.             $parameters[$parameter$value;
  837.             if(!strcmp($parameter$return))
  838.                 $return_value $value;
  839.             $values trim(strtok(''));
  840.         }
  841.         return($return_value);
  842.     }
  843.  
  844. /**
  845.  * dummy docblock makes error-free autodocs
  846.  */
  847.     Function DecodePart($part)
  848.     {
  849.         switch($part['Type'])
  850.         {
  851.             case 'MessageStart':
  852.                 $this->headers=array();
  853.                 break;
  854.             case 'HeaderName':
  855.                 if($this->decode_bodies)
  856.                     $this->current_header = strtolower($part['Name']);
  857.                 break;
  858.             case 'HeaderValue':
  859.                 if($this->decode_headers)
  860.                 {
  861.                     $value $part['Value'];
  862.                     $error '';
  863.                     for($decoded_header array()$position 0$position<strlen($value))
  864.                     {
  865.                         if(GetType($encoded=strpos($value,'=?'$position))!='integer')
  866.                         {
  867.                             if($position<strlen($value))
  868.                             {
  869.                                 if(count($decoded_header))
  870.                                     $decoded_header[count($decoded_header)-1]['Value'].=substr($value$position);
  871.                                 else
  872.                                 {
  873.                                     $decoded_header[]=array(
  874.                                         'Value'=>substr($value$position),
  875.                                         'Encoding'=>'ASCII'
  876.                                     );
  877.                                 }
  878.                             }
  879.                             break;
  880.                         }
  881.                         $set $encoded 2;
  882.                         if(GetType($method=strpos($value,'?'$set))!='integer')
  883.                         {
  884.                             $error 'invalid header encoding syntax '.$part['Value'];
  885.                             $error_position $part['Position'$set;
  886.                             break;
  887.                         }
  888.                         $encoding=strtoupper(substr($value$set$method $set));
  889.                         $method += 1;
  890.                         if(GetType($data=strpos($value,'?'$method))!='integer')
  891.                         {
  892.                             $error 'invalid header encoding syntax '.$part['Value'];
  893.                             $error_position $part['Position'$set;
  894.                             break;
  895.                         }
  896.                         $start $data 1;
  897.                         if(GetType($end=strpos($value,'?='$start))!='integer')
  898.                         {
  899.                             $error 'invalid header encoding syntax '.$part['Value'];
  900.                             $error_position $part['Position'$start;
  901.                             break;
  902.                         }
  903.                         if($encoded $position)
  904.                         {
  905.                             if(count($decoded_header))
  906.                                 $decoded_header[count($decoded_header)-1]['Value'].=substr($value$position$encoded $position);
  907.                             else
  908.                             {
  909.                                 $decoded_header[]=array(
  910.                                     'Value'=>substr($value$position$encoded $position),
  911.                                     'Encoding'=>'ASCII'
  912.                                 );
  913.                             }
  914.                         }
  915.                         switch(strtolower(substr($value$method$data $method)))
  916.                         {
  917.                             case 'q':
  918.                                 if($end>$start)
  919.                                 {
  920.                                     for($decoded ''$position $start$position $end )
  921.                                     {
  922.                                         switch($value[$position])
  923.                                         {
  924.                                             case '=':
  925.                                                 $h HexDec($hex strtolower(substr($value$position+12)));
  926.                                                 if($end $position 3
  927.                                                 || strcmp(sprintf('%02x'$h)$hex))
  928.                                                 {
  929.                                                     $warning 'the header specified an invalid encoded character';
  930.                                                     $warning_position $part['Position'$position 1;
  931.                                                     if($this->ignore_syntax_errors)
  932.                                                     {
  933.                                                         $this->SetPositionedWarning($warning$warning_position);
  934.                                                         $decoded .= '=';
  935.                                                         $position ++;
  936.                                                     }
  937.                                                     else
  938.                                                     {
  939.                                                         $error $warning;
  940.                                                         $error_position $warning_position;
  941.                                                         break 4;
  942.                                                     }
  943.                                                 }
  944.                                                 else
  945.                                                 {
  946.                                                     $decoded .= Chr($h);
  947.                                                     $position += 3;
  948.                                                 }
  949.                                                 break;
  950.                                             case '_':
  951.                                                 $decoded .= ' ';
  952.                                                 $position++;
  953.                                                 break;
  954.                                             default:
  955.                                                 $decoded .= $value[$position];
  956.                                                 $position++;
  957.                                                 break;
  958.                                         }
  959.                                     }
  960.                                     if(count($decoded_header)
  961.                                     && (!strcmp($decoded_header[$last count($decoded_header)-1]['Encoding']'ASCII'))
  962.                                     || !strcmp($decoded_header[$last]['Encoding']$encoding))
  963.                                     {
  964.                                         $decoded_header[$last]['Value'].= $decoded;
  965.                                         $decoded_header[$last]['Encoding']$encoding;
  966.                                     }
  967.                                     else
  968.                                     {
  969.                                         $decoded_header[]=array(
  970.                                             'Value'=>$decoded,
  971.                                             'Encoding'=>$encoding
  972.                                         );
  973.                                     }
  974.                                 }
  975.                                 break;
  976.                             case 'b':
  977.                                 $decoded=base64_decode(substr($value$start$end $start));
  978.                                 if($end <= $start
  979.                                 || GetType($decoded!= 'string'
  980.                                 || strlen($decoded== 0)
  981.                                 {
  982.                                     $warning 'the header specified an invalid base64 encoded text';
  983.                                     $warning_position $part['Position'$start;
  984.                                     if($this->ignore_syntax_errors)
  985.                                         $this->SetPositionedWarning($warning$warning_position);
  986.                                     else
  987.                                     {
  988.                                         $error $warning;
  989.                                         $error_position $warning_position;
  990.                                         break 2;
  991.                                     }
  992.                                 }
  993.                                 if(count($decoded_header)
  994.                                 && (!strcmp($decoded_header[$last count($decoded_header)-1]['Encoding']'ASCII'))
  995.                                 || !strcmp($decoded_header[$last]['Encoding']$encoding))
  996.                                 {
  997.                                     $decoded_header[$last]['Value'].= $decoded;
  998.                                     $decoded_header[$last]['Encoding']$encoding;
  999.                                 }
  1000.                                 else
  1001.                                 {
  1002.                                     $decoded_header[]=array(
  1003.                                         'Value'=>$decoded,
  1004.                                         'Encoding'=>$encoding
  1005.                                     );
  1006.                                 }
  1007.                                 break;
  1008.                             default:
  1009.                                 $error 'the header specified an unsupported encoding method';
  1010.                                 $error_position $part['Position'$method;
  1011.                                 break 2;
  1012.                         }
  1013.                         $position $end 2;
  1014.                     }
  1015.                     if(strlen($error)==0
  1016.                     && count($decoded_header))
  1017.                         $part['Decoded']=$decoded_header;
  1018.                 }
  1019.                 if($this->decode_bodies
  1020.                 || $this->decode_headers)
  1021.                 {
  1022.                     switch($this->current_header)
  1023.                     {
  1024.                         case 'content-type:':
  1025.                             $boundary $this->ParseParameters($part['Value']$type$parameters'boundary');
  1026.                             $this->headers['Type'$type;
  1027.                             if($this->decode_headers)
  1028.                             {
  1029.                                 $part['MainValue'$type;
  1030.                                 $part['Parameters'$parameters;
  1031.                             }
  1032.                             if(!strcmp(strtok($type'/')'multipart'))
  1033.                             {
  1034.                                 $this->headers['Multipart'1;
  1035.                                 if(strlen($boundary))
  1036.                                     $this->headers['Boundary'$boundary;
  1037.                                 else
  1038.                                     return($this->SetPositionedError('multipart content-type header does not specify the boundary parameter'$part['Position']));
  1039.                             }
  1040.                             break;
  1041.                         case 'content-transfer-encoding:':
  1042.                             switch($this->headers['Encoding']=strtolower(trim($part['Value'])))
  1043.                             {
  1044.                                 case 'quoted-printable':
  1045.                                     $this->headers['QuotedPrintable'1;
  1046.                                     break;
  1047.                                 case '7 bit':
  1048.                                 case '8 bit':
  1049.                                     if(!$this->SetPositionedWarning('"'.$this->headers['Encoding'].'" is an incorrect content transfer encoding type'$part['Position']))
  1050.                                         return(0);
  1051.                                 case '7bit':
  1052.                                 case '8bit':
  1053.                                 case 'binary':
  1054.                                     break;
  1055.                                 case 'base64':
  1056.                                     $this->headers['Base64']=1;
  1057.                                     break;
  1058.                                 default:
  1059.                                     if(!$this->SetPositionedWarning('decoding '.$this->headers['Encoding'].' encoded bodies is not yet supported'$part['Position']))
  1060.                                         return(0);
  1061.                             }
  1062.                             break;
  1063.                     }
  1064.                 }
  1065.                 break;
  1066.             case 'BodyStart':
  1067.                 if($this->decode_bodies
  1068.                 && IsSet($this->headers['Multipart']))
  1069.                 {
  1070.                     $this->body_parser_state = MIME_PARSER_BODY_START;
  1071.                     $this->body_buffer = '';
  1072.                     $this->body_buffer_position = 0;
  1073.                 }
  1074.                 break;
  1075.             case 'MessageEnd':
  1076.                 if($this->decode_bodies
  1077.                 && IsSet($this->headers['Multipart'])
  1078.                 && $this->body_parser_state != MIME_PARSER_BODY_DONE)
  1079.                     return($this->SetPositionedError('incomplete message body part'$part['Position']));
  1080.                 break;
  1081.             case 'BodyData':
  1082.                 if($this->decode_bodies)
  1083.                 {
  1084.                     if(strlen($this->body_buffer)==0)
  1085.                     {
  1086.                         $this->body_buffer = $part['Data'];
  1087.                         $this->body_offset = $part['Position'];
  1088.                     }
  1089.                     else
  1090.                         $this->body_buffer .= $part['Data'];
  1091.                     if(IsSet($this->headers['Multipart']))
  1092.                     {
  1093.                         $boundary '--'.$this->headers['Boundary'];
  1094.                         switch($this->body_parser_state)
  1095.                         {
  1096.                             case MIME_PARSER_BODY_START:
  1097.                                 for($position $this->body_buffer_position; ;)
  1098.                                 {
  1099.                                     if(!$this->FindBodyLineBreak($position$break$line_break))
  1100.                                         return(1);
  1101.                                     $next $line_break strlen($break);
  1102.                                     if(!strcmp(substr($this->body_buffer$position$line_break $position)$boundary))
  1103.                                     {
  1104.                                         $part=array(
  1105.                                             'Type'=>'StartPart',
  1106.                                             'Part'=>$this->headers['Boundary'],
  1107.                                             'Position'=>$this->body_offset + $next
  1108.                                         );
  1109.                                         $this->parts[]=$part;
  1110.                                         UnSet($this->body_parser);
  1111.                                         $this->body_parser = new mime_parser_class;
  1112.                                         $this->body_parser->decode_bodies 1;
  1113.                                         $this->body_parser->decode_headers $this->decode_headers;
  1114.                                         $this->body_parser->mbox 0;
  1115.                                         $this->body_parser_state = MIME_PARSER_BODY_DATA;
  1116.                                         $this->body_buffer = substr($this->body_buffer$next);
  1117.                                         $this->body_offset += $next;
  1118.                                         $this->body_buffer_position = 0;
  1119.                                         break;
  1120.                                     }
  1121.                                     else
  1122.                                         $position $next;
  1123.                                 }
  1124.                             case MIME_PARSER_BODY_DATA:
  1125.                                 for($position $this->body_buffer_position; ;)
  1126.                                 {
  1127.                                     if(!$this->FindBodyLineBreak($position$break$line_break))
  1128.                                     {
  1129.                                         if($position 0)
  1130.                                         {
  1131.                                             if(!$this->body_parser->Parse(substr($this->body_buffer0$position)0))
  1132.                                                 return($this->SetError($this->body_parser->error));
  1133.                                             if(!$this->QueueBodyParts())
  1134.                                                 return(0);
  1135.                                         }
  1136.                                         $this->body_buffer = substr($this->body_buffer$position);
  1137.                                         $this->body_buffer_position = 0;
  1138.                                         $this->body_offset += $position;
  1139.                                         return(1);
  1140.                                     }
  1141.                                     $next $line_break strlen($break);
  1142.                                     $line substr($this->body_buffer$position$line_break $position);
  1143.                                     if(!strcmp($line$boundary))
  1144.                                     {
  1145.                                         if(!$this->body_parser->Parse(substr($this->body_buffer0$position)1))
  1146.                                             return($this->SetError($this->body_parser->error));
  1147.                                         if(!$this->QueueBodyParts())
  1148.                                             return(0);
  1149.                                         $part=array(
  1150.                                             'Type'=>'EndPart',
  1151.                                             'Part'=>$this->headers['Boundary'],
  1152.                                             'Position'=>$this->body_offset + $position
  1153.                                         );
  1154.                                         $this->parts[$part;
  1155.                                         $part=array(
  1156.                                             'Type'=>'StartPart',
  1157.                                             'Part'=>$this->headers['Boundary'],
  1158.                                             'Position'=>$this->body_offset + $next
  1159.                                         );
  1160.                                         $this->parts[$part;
  1161.                                         UnSet($this->body_parser);
  1162.                                         $this->body_parser = new mime_parser_class;
  1163.                                         $this->body_parser->decode_bodies 1;
  1164.                                         $this->body_parser->decode_headers $this->decode_headers;
  1165.                                         $this->body_parser->mbox 0;
  1166.                                         $this->body_buffer = substr($this->body_buffer$next);
  1167.                                         $this->body_buffer_position = 0;
  1168.                                         $this->body_offset += $next;
  1169.                                         $position=0;
  1170.                                         continue;
  1171.                                     }
  1172.                                     elseif(!strcmp($line$boundary.'--'))
  1173.                                     {
  1174.                                         if(!$this->body_parser->Parse(substr($this->body_buffer0$position)1))
  1175.                                             return($this->SetError($this->body_parser->error));
  1176.                                         if(!$this->QueueBodyParts())
  1177.                                             return(0);
  1178.                                         $part=array(
  1179.                                             'Type'=>'EndPart',
  1180.                                             'Part'=>$this->headers['Boundary'],
  1181.                                             'Position'=>$this->body_offset + $position
  1182.                                         );
  1183.                                         $this->body_buffer = substr($this->body_buffer$next);
  1184.                                         $this->body_buffer_position = 0;
  1185.                                         $this->body_offset += $next;
  1186.                                         $this->body_parser_state = MIME_PARSER_BODY_DONE;
  1187.                                         break 2;
  1188.                                     }
  1189.                                     $position $next;
  1190.                                 }
  1191.                                 break;
  1192.                             case MIME_PARSER_BODY_DONE:
  1193.                                 return(1);
  1194.                             default:
  1195.                                 return($this->SetPositionedError($this->state.' is not a valid body parser state'$this->body_buffer_position));
  1196.                         }
  1197.                     }
  1198.                     elseif(IsSet($this->headers['QuotedPrintable']))
  1199.                     {
  1200.                         for($end strlen($this->body_buffer)$decoded ''$position $this->body_buffer_position$position $end)
  1201.                         {
  1202.                             if(GetType($equal strpos($this->body_buffer'='$position))!='integer')
  1203.                             {
  1204.                                 $decoded .= substr($this->body_buffer$position);
  1205.                                 $position $end;
  1206.                                 break;
  1207.                             }
  1208.                             $next $equal 1;
  1209.                             switch($end $equal)
  1210.                             {
  1211.                                 case 1:
  1212.                                     $decoded .= substr($this->body_buffer$position$equal $position);
  1213.                                     $position $equal;
  1214.                                     break 2;
  1215.                                 case 2:
  1216.                                     $decoded .= substr($this->body_buffer$position$equal $position);
  1217.                                     if(!strcmp($this->body_buffer[$next],"\n"))
  1218.                                         $position $end;
  1219.                                     else
  1220.                                         $position $equal;
  1221.                                     break 2;
  1222.                             }
  1223.                             if(!strcmp(substr($this->body_buffer$next2)$break="\r\n")
  1224.                             || !strcmp($this->body_buffer[$next]$break="\n")
  1225.                             || !strcmp($this->body_buffer[$next]$break="\r"))
  1226.                             {
  1227.                                 $decoded .= substr($this->body_buffer$position$equal $position);
  1228.                                 $position $next strlen($break);
  1229.                                 continue;
  1230.                             }
  1231.                             $decoded .= substr($this->body_buffer$position$equal $position);
  1232.                             $h HexDec($hex=strtolower(substr($this->body_buffer$next2)));
  1233.                             if(strcmp(sprintf('%02x'$h)$hex))
  1234.                             {
  1235.                                 if(!$this->SetPositionedWarning('the body specified an invalid quoted-printable encoded character'$this->body_offset + $next))
  1236.                                     return(0);
  1237.                                 $decoded.='=';
  1238.                                 $position=$next;
  1239.                             }
  1240.                             else
  1241.                             {
  1242.                                 $decoded .= Chr($h);
  1243.                                 $position $equal 3;
  1244.                             }
  1245.                         }
  1246.                         if(strlen($decoded)==0)
  1247.                         {
  1248.                             $this->body_buffer_position = $position;
  1249.                             return(1);
  1250.                         }
  1251.                         $part['Data'$decoded;
  1252.                         $this->body_buffer = substr($this->body_buffer$position);
  1253.                         $this->body_buffer_position = 0;
  1254.                         $this->body_offset += $position;
  1255.                     }
  1256.                     elseif(IsSet($this->headers['Base64']))
  1257.                     {
  1258.                         $part['Data'base64_decode($this->body_buffer_position ? substr($this->body_buffer,$this->body_buffer_position$this->body_buffer);
  1259.                         $this->body_offset += strlen($this->body_buffer$this->body_buffer_position;
  1260.                         $this->body_buffer_position = 0;
  1261.                         $this->body_buffer = '';
  1262.                     }
  1263.                     else
  1264.                     {
  1265.                         $part['Data'substr($this->body_buffer$this->body_buffer_position);
  1266.                         $this->body_buffer_position = 0;
  1267.                         $this->body_buffer = '';
  1268.                     }
  1269.                 }
  1270.                 break;
  1271.         }
  1272.         $this->parts[]=$part;
  1273.         return(1);
  1274.     }
  1275.  
  1276. /**
  1277.  * dummy docblock makes error-free autodocs
  1278.  */
  1279.     Function DecodeStream($parameters&$end_of_message&$decoded)
  1280.     {
  1281.         $end_of_message 1;
  1282.         $state MIME_MESSAGE_START;
  1283.         for(;;)
  1284.         {
  1285.             if(!$this->GetPart($part$end))
  1286.                 return(0);
  1287.             if($end)
  1288.             {
  1289.                 if(IsSet($parameters['File']))
  1290.                 {
  1291.                     $end_of_data feof($this->file);
  1292.                     if($end_of_data)
  1293.                         break;
  1294.                     $data @fread($this->file8000);
  1295.                     if(GetType($data)!='string')
  1296.                         return($this->SetPHPError('could not read the message file'$php_errormsg));
  1297.                     $end_of_data feof($this->file);
  1298.                 }
  1299.                 else
  1300.                 {
  1301.                     $end_of_data=($this->position>=strlen($parameters['Data']));
  1302.                     if($end_of_data)
  1303.                         break;
  1304.                     $data substr($parameters['Data']$this->position);
  1305.                     $end_of_data 1;
  1306.                     $this->position = strlen($parameters['Data']);
  1307.                 }
  1308.                 if(!$this->Parse($data$end_of_data))
  1309.                     return(0);
  1310.                 continue;
  1311.             }
  1312.             $type $part['Type'];
  1313.             switch($state)
  1314.             {
  1315.                 case MIME_MESSAGE_START:
  1316.                     switch($type)
  1317.                     {
  1318.                         case 'MessageStart':
  1319.                             $decoded=array(
  1320.                                 'Headers'=>array(),
  1321.                                 'Parts'=>array()
  1322.                             );
  1323.                             $end_of_message 0;
  1324.                             $state MIME_MESSAGE_GET_HEADER_NAME;
  1325.                             continue 3;
  1326.                     }
  1327.                     break;
  1328.  
  1329.                 case MIME_MESSAGE_GET_HEADER_NAME:
  1330.                     switch($type)
  1331.                     {
  1332.                         case 'HeaderName':
  1333.                             $header strtolower($part['Name']);
  1334.                             $state MIME_MESSAGE_GET_HEADER_VALUE;
  1335.                             continue 3;
  1336.                         case 'BodyStart':
  1337.                             $state MIME_MESSAGE_GET_BODY;
  1338.                             $part_number 0;
  1339.                             continue 3;
  1340.                     }
  1341.                     break;
  1342.  
  1343.                 case MIME_MESSAGE_GET_HEADER_VALUE:
  1344.                     switch($type)
  1345.                     {
  1346.                         case 'HeaderValue':
  1347.                             $value trim($part['Value']);
  1348.                             if(!IsSet($decoded['Headers'][$header]))
  1349.                             {
  1350.                                 $h 0;
  1351.                                 $decoded['Headers'][$header]=$value;
  1352.                                 if($this->extract_addresses
  1353.                                 && IsSet($this->address_headers[$header]))
  1354.                                     $decoded['HeaderPositions'][$header$part['Position'];
  1355.                             }
  1356.                             elseif(GetType($decoded['Headers'][$header])=='string')
  1357.                             {
  1358.                                 $h 1;
  1359.                                 $decoded['Headers'][$header]=array($decoded['Headers'][$header]$value);
  1360.                             }
  1361.                             else
  1362.                             {
  1363.                                 $h count($decoded['Headers'][$header]);
  1364.                                 $decoded['Headers'][$header][]=$value;
  1365.                             }
  1366.                             if(IsSet($part['Decoded'])
  1367.                             && (count($part['Decoded'])>1
  1368.                             || strcmp($part['Decoded'][0]['Encoding'],'ASCII')
  1369.                             || strcmp($valuetrim($part['Decoded'][0]['Value']))))
  1370.                             {
  1371.                                 $p=$part['Decoded'];
  1372.                                 $p[0]['Value']=ltrim($p[0]['Value']);
  1373.                                 $last=count($p)-1;
  1374.                                 $p[$last]['Value']=rtrim($p[$last]['Value']);
  1375.                                 $decoded['DecodedHeaders'][$header][$h]=$p;
  1376.                             }
  1377.                             switch($header)
  1378.                             {
  1379.                                 case 'content-disposition:':
  1380.                                     $filename='filename';
  1381.                                     break;
  1382.                                 case 'content-type:':
  1383.                                     if(!IsSet($decoded['FileName']))
  1384.                                     {
  1385.                                         $filename='name';
  1386.                                         break;
  1387.                                     }
  1388.                                 default:
  1389.                                     $filename='';
  1390.                                     break;
  1391.                             }
  1392.                             if(strlen($filename))
  1393.                             {
  1394.                                 $this->ParseStructuredHeader($value$type$header_parameters$character_sets$languages);
  1395.                                 if(IsSet($header_parameters[$filename]))
  1396.                                 {
  1397.                                     $decoded['FileName']=$header_parameters[$filename];
  1398.                                     if(IsSet($character_sets[$filename])
  1399.                                     && strlen($character_sets[$filename]))
  1400.                                         $decoded['FileNameCharacterSet']=$character_sets[$filename];
  1401.                                     if(IsSet($character_sets['language'])
  1402.                                     && strlen($character_sets['language']))
  1403.                                         $decoded['FileNameCharacterSet']=$character_sets[$filename];
  1404.                                     if(!strcmp($header'content-disposition:'))
  1405.                                         $decoded['FileDisposition']=$type;
  1406.                                 }
  1407.                             }
  1408.                             $state MIME_MESSAGE_GET_HEADER_NAME;
  1409.                             continue 3;
  1410.                     }
  1411.                     break;
  1412.  
  1413.                 case MIME_MESSAGE_GET_BODY:
  1414.                     switch($type)
  1415.                     {
  1416.                         case 'BodyData':
  1417.                             if(IsSet($parameters['SaveBody']))
  1418.                             {
  1419.                                 if(!IsSet($decoded['BodyFile']))
  1420.                                 {
  1421.                                     $directory_separator=(defined('DIRECTORY_SEPARATOR'DIRECTORY_SEPARATOR '/');
  1422.                                     $path (strlen($parameters['SaveBody']($parameters['SaveBody'].(strcmp($parameters['SaveBody'][strlen($parameters['SaveBody'])-1]$directory_separator$directory_separator '')) '').strval($this->body_part_number);
  1423.                                     if(!($this->body_file = fopen($path'wb')))
  1424.                                         return($this->SetPHPError('could not create file '.$path.' to save the message body part'$php_errormsg));
  1425.                                     $decoded['BodyFile'$path;
  1426.                                     $decoded['BodyPart'$this->body_part_number;
  1427.                                     $decoded['BodyLength'0;
  1428.                                     $this->body_part_number++;
  1429.                                 }
  1430.                                 if(strlen($part['Data'])
  1431.                                 && !fwrite($this->body_file$part['Data']))
  1432.                                 {
  1433.                                     $this->SetPHPError('could not save the message body part to file '.$decoded['BodyFile']$php_errormsg);
  1434.                                     fclose($this->body_file);
  1435.                                     @unlink($decoded['BodyFile']);
  1436.                                     return(0);
  1437.                                 }
  1438.                             }
  1439.                             elseif(IsSet($parameters['SkipBody']))
  1440.                             {
  1441.                                 if(!IsSet($decoded['BodyPart']))
  1442.                                 {
  1443.                                     $decoded['BodyPart'$this->body_part_number;
  1444.                                     $decoded['BodyLength'0;
  1445.                                     $this->body_part_number++;
  1446.                                 }
  1447.                             }
  1448.                             else
  1449.                             {
  1450.                                 if(IsSet($decoded['Body']))
  1451.                                     $decoded['Body'].=$part['Data'];
  1452.                                 else
  1453.                                 {
  1454.                                     $decoded['Body']=$part['Data'];
  1455.                                     $decoded['BodyPart'$this->body_part_number;
  1456.                                     $decoded['BodyLength'0;
  1457.                                     $this->body_part_number++;
  1458.                                 }
  1459.                             }
  1460.                             $decoded['BodyLength'+= strlen($part['Data']);
  1461.                             continue 3;
  1462.                         case 'StartPart':
  1463.                             if(!$this->DecodeStream($parameters$end_of_part$decoded_part))
  1464.                                 return(0);
  1465.                             $decoded['Parts'][$part_number]=$decoded_part;
  1466.                             $part_number++;
  1467.                             $state MIME_MESSAGE_GET_BODY_PART;
  1468.                             continue 3;
  1469.                         case 'MessageEnd':
  1470.                             if(IsSet($decoded['BodyFile']))
  1471.                                 fclose($this->body_file);
  1472.                             return(1);
  1473.                     }
  1474.                     break;
  1475.  
  1476.                 case MIME_MESSAGE_GET_BODY_PART:
  1477.                     switch($type)
  1478.                     {
  1479.                         case 'EndPart':
  1480.                             $state MIME_MESSAGE_GET_BODY;
  1481.                             continue 3;
  1482.                     }
  1483.                     break;
  1484.             }
  1485.             return($this->SetError('unexpected decoded message part type '.$type.' in state '.$state));
  1486.         }
  1487.         return(1);
  1488.     }
  1489.  
  1490.     /* Public functions */
  1491.  
  1492. /**
  1493.  * dummy docblock makes error-free autodocs
  1494.  */
  1495.     Function Parse($data$end)
  1496.     {
  1497.         if(strlen($this->error))
  1498.             return(0);
  1499.         if($this->state==MIME_PARSER_END)
  1500.             return($this->SetError('the parser already reached the end'));
  1501.         $this->buffer .= $data;
  1502.         do
  1503.         {
  1504.             Unset($part);
  1505.             if(!$this->ParsePart($end$part$need_more_data))
  1506.                 return(0);
  1507.             if(IsSet($part)
  1508.             && !$this->DecodePart($part))
  1509.                 return(0);
  1510.         }
  1511.         while(!$need_more_data
  1512.         && $this->state!=MIME_PARSER_END);
  1513.         if($end
  1514.         && $this->state!=MIME_PARSER_END)
  1515.             return($this->SetError('reached a premature end of data'));
  1516.         if($this->buffer_position>0)
  1517.         {
  1518.             $this->offset += $this->buffer_position;
  1519.             $this->buffer = substr($this->buffer$this->buffer_position);
  1520.             $this->buffer_position = 0;
  1521.         }
  1522.         return(1);
  1523.     }
  1524.  
  1525. /**
  1526.  * dummy docblock makes error-free autodocs
  1527.  */
  1528.     Function ParseFile($file)
  1529.     {
  1530.         if(strlen($this->error))
  1531.             return(0);
  1532.         if(!($stream @fopen($file'r')))
  1533.             return($this->SetPHPError('Could not open the file '.$file$php_errormsg));
  1534.         for($end 0;!$end;)
  1535.         {
  1536.             if(!($data @fread($stream8000)))
  1537.             {
  1538.                 $this->SetPHPError('Could not open the file '.$file$php_errormsg);
  1539.                 fclose($stream);
  1540.                 return(0);
  1541.             }
  1542.             $end=feof($stream);
  1543.             if(!$this->Parse($data$end))
  1544.             {
  1545.                 fclose($stream);
  1546.                 return(0);
  1547.             }
  1548.         }
  1549.         fclose($stream);
  1550.         return(1);
  1551.     }
  1552.  
  1553. /**
  1554.  * dummy docblock makes error-free autodocs
  1555.  */
  1556.     Function GetPart(&$part&$end)
  1557.     {
  1558.         $end ($this->part_position >= count($this->parts));
  1559.         if($end)
  1560.         {
  1561.             if($this->part_position)
  1562.             {
  1563.                 $this->part_position = 0;
  1564.                 $this->parts = array();
  1565.             }
  1566.         }
  1567.         else
  1568.         {
  1569.             $part $this->parts[$this->part_position];
  1570.             $this->part_position ++;
  1571.         }
  1572.         return(1);
  1573.     }
  1574.  
  1575. /*
  1576. {metadocument}
  1577.     <function>
  1578.         <name>Decode</name>
  1579.         <type>BOOLEAN</type>
  1580.         <documentation>
  1581.             <purpose>Parse and decode message data and retrieve its structure.</purpose>
  1582.             <usage>Pass an array to the <argumentlink>
  1583.                     <function>Decode</function>
  1584.                     <argument>parameters</argument>
  1585.                 </argumentlink>
  1586.                 parameter to define whether the message data should be read and
  1587.                 parsed from a file or a data string, as well additional parsing
  1588.                 options. The <argumentlink>
  1589.                     <function>Decode</function>
  1590.                     <argument>decoded</argument>
  1591.                 </argumentlink> returns the
  1592.                 data structure of the parsed messages.</usage>
  1593.             <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
  1594.                 the specified message data is parsed successfully. Otherwise,
  1595.                 check the variables <variablelink>error</variablelink> and
  1596.                 <variablelink>error_position</variablelink> to determine what
  1597.                 error occurred and the relevant message position.</returnvalue>
  1598.         </documentation>
  1599.         <argument>
  1600.             <name>parameters</name>
  1601.             <type>HASH</type>
  1602.             <documentation>
  1603.                 <purpose>Associative array to specify parameters for the message
  1604.                     data parsing and decoding operation. Here follows the list of
  1605.                     supported parameters that should be used as indexes of the
  1606.                     array:<paragraphbreak />
  1607.                     <tt>File</tt><paragraphbreak />
  1608.                     Name of the file from which the message data will be read. It
  1609.                     may be the name of a file stream or a remote URL, as long as
  1610.                     your PHP installation is configured to allow accessing remote
  1611.                     files with the <tt>fopen()</tt> function.<paragraphbreak />
  1612.                     <tt>Data</tt><paragraphbreak />
  1613.                     String that specifies the message data. This should be used
  1614.                     as alternative data source for passing data available in memory,
  1615.                     like for instance messages stored in a database that was queried
  1616.                     dynamically and the message data was fetched into a string
  1617.                     variable.<paragraphbreak />
  1618.                     <tt>SaveBody</tt><paragraphbreak />
  1619.                     If this parameter is specified, the message body parts are saved
  1620.                     to files. The path of the directory where the files are saved is
  1621.                     defined by this parameter value. The information about the
  1622.                     message body part structure is returned by the <argumentlink>
  1623.                         <function>Decode</function>
  1624.                         <argument>decoded</argument>
  1625.                     </argumentlink> argument, but it just returns the body data part
  1626.                     file name instead of the actual body data. It is recommended for
  1627.                     retrieving messages larger than the available memory. The names
  1628.                     of the body part files are numbers starting from
  1629.                     <stringvalue>1</stringvalue>.<paragraphbreak />
  1630.                     <tt>SkipBody</tt><paragraphbreak />
  1631.                     If this parameter is specified, the message body parts are
  1632.                     skipped. This means the information about the message body part
  1633.                     structure is returned by the <argumentlink>
  1634.                         <function>Decode</function>
  1635.                         <argument>decoded</argument>
  1636.                     </argumentlink> but it does not return any body data. It is
  1637.                     recommended just for parsing messages without the need to
  1638.                     retrieve the message body part data.</purpose>
  1639.             </documentation>
  1640.         </argument>
  1641.         <argument>
  1642.             <name>decoded</name>
  1643.             <type>ARRAY</type>
  1644.             <out />
  1645.             <documentation>
  1646.                 <purpose>Retrieve the structure of the parsed message headers and
  1647.                     body data.<paragraphbreak />
  1648.                     The argument is used to return by reference an array of message
  1649.                     structure definitions. Each array entry refers to the structure
  1650.                     of each message that is found and parsed successfully.<paragraphbreak />
  1651.                     Each message entry consists of an associative array with several
  1652.                     entries that describe the message structure. Here follows the
  1653.                     list of message structure entries names and the meaning of the
  1654.                     respective values:<paragraphbreak />
  1655.                     <tt>Headers</tt><paragraphbreak />
  1656.                     Associative array that returns the list of all the message
  1657.                     headers. The array entries are the header names mapped to
  1658.                     lower case, including the end colon. The array values are the
  1659.                     respective header raw values without any start or trailing white
  1660.                     spaces. Long header values split between multiple message lines
  1661.                     are gathered in single string without line breaks. If an header
  1662.                     with the same name appears more than once in the message, the
  1663.                     respective value is an array with the values of all of the
  1664.                     header occurrences.<paragraphbreak />
  1665.                     <tt>DecodedHeaders</tt><paragraphbreak />
  1666.                     Associative array that returns the list of all the encoded
  1667.                     message headers when the
  1668.                     <variablelink>decode_headers</variablelink> variable is set. The
  1669.                     array entries are the header names mapped to lower case,
  1670.                     including the end colon. The array values are also arrays that
  1671.                     list only the occurrences of the header that originally were
  1672.                     encoded. Each entry of the decoded header array contains more
  1673.                     associative arrays that describe each part of the decoded
  1674.                     header. Each of those associative arrays have an entry named
  1675.                     <tt>Value</tt> that contains the decoded header part value, and
  1676.                     another entry named <tt>Encoding</tt> that specifies the
  1677.                     character set encoding of the value in upper case.<paragraphbreak />
  1678.                     <tt>ExtractedAddresses</tt><paragraphbreak />
  1679.                     If the <variablelink>extract_addresses</variablelink> variable
  1680.                     is set to <booleanvalue>1</booleanvalue>, this entry is set to an
  1681.                     associative array with the addresses found in the headers
  1682.                     specified by the <variablelink>address_headers</variablelink>
  1683.                     variable.<paragraphbreak />
  1684.                     The parsed addresses found on each header are returned as an
  1685.                     array with the format of the <link>
  1686.                         <data>addresses</data>
  1687.                         <url>rfc822_addresses_class.html#argument_ParseAddressList_addresses</url>
  1688.                     </link> argument of the <link>
  1689.                         <data>ParseAddressList</data>
  1690.                         <url>rfc822_addresses_class.html#function_ParseAddressList</url>
  1691.                     </link> function of the <link>
  1692.                         <data>RFC 822 addresses</data>
  1693.                         <url>rfc822_addresses_class.html</url>
  1694.                     </link> class.<paragraphbreak />
  1695.                     <tt>Parts</tt><paragraphbreak />
  1696.                     If this message content type is multipart, this entry is an
  1697.                     array that describes each of the parts contained in the message
  1698.                     body. Each message part is described by an associative array
  1699.                     with the same structure of a complete message
  1700.                     definition.<paragraphbreak />
  1701.                     <tt>Body</tt><paragraphbreak />
  1702.                     String with the decoded data contained in the message body. If
  1703.                     the <tt>SaveBody</tt> or <tt>SkipBody</tt> parameters are
  1704.                     defined, the <tt>Body</tt> entry is not set.<paragraphbreak />
  1705.                     <tt>BodyFile</tt><paragraphbreak />
  1706.                     Name of the file to which the message body data was saved when
  1707.                     the <tt>SaveBody</tt> parameter is defined.<paragraphbreak />
  1708.                     <tt>BodyLength</tt><paragraphbreak />
  1709.                     Length of the current decoded body part.<paragraphbreak />
  1710.                     <tt>BodyPart</tt><paragraphbreak />
  1711.                     Number of the current message body part.<paragraphbreak />
  1712.                     <tt>FileName</tt><paragraphbreak />
  1713.                     Name of the file for body parts composed from
  1714.                     files.<paragraphbreak />
  1715.                     <tt>FileNameCharacterSet</tt><paragraphbreak />
  1716.                     Character set encoding for file parts with names that may
  1717.                     include non-ASCII characters.<paragraphbreak />
  1718.                     <tt>FileNameLanguage</tt><paragraphbreak />
  1719.                     Language of file parts with names that may include non-ASCII
  1720.                     characters.<paragraphbreak />
  1721.                     <tt>FileDisposition</tt><paragraphbreak />
  1722.                     Disposition of parts that files. It may be either
  1723.                     <tt><stringvalue>inline</stringvalue></tt> for file parts to be
  1724.                     displayed with the message, or
  1725.                     <tt><stringvalue>attachment</stringvalue></tt> otherwise.</purpose>
  1726.             </documentation>
  1727.         </argument>
  1728.         <do>
  1729. {/metadocument}
  1730. */
  1731. /**
  1732.  * dummy docblock makes error-free autodocs
  1733.  */
  1734.     Function Decode($parameters&$decoded)
  1735.     {
  1736.         if(IsSet($parameters['File']))
  1737.         {
  1738.             if(!($this->file = @fopen($parameters['File']'r')))
  1739.                 return($this->SetPHPError('could not open the message file to decode '.$parameters['File']$php_errormsg));
  1740.         }
  1741.         elseif(IsSet($parameters['Data']))
  1742.             $this->position = 0;
  1743.         else
  1744.             return($this->SetError('it was not specified a valid message to decode'));
  1745.         $this->warnings = $decoded array();
  1746.         $this->ResetParserState();
  1747.         $addresses new rfc822_addresses_class;
  1748.         $addresses->ignore_syntax_errors $this->ignore_syntax_errors;
  1749.         for($message 0($success $this->DecodeStream($parameters$end_of_message$decoded_message)) && !$end_of_message$message++)
  1750.         {
  1751.             if($this->extract_addresses)
  1752.             {
  1753.                 $headers $decoded_message['Headers'];
  1754.                 $positions $decoded_message['HeaderPositions'];
  1755.                 $th count($headers);
  1756.                 for(Reset($headers)$h 0$h<$thNext($headers)++$h)
  1757.                 {
  1758.                     $header Key($headers);
  1759.                     if(IsSet($this->address_headers[$header])
  1760.                     && $this->address_headers[$header])
  1761.                     {
  1762.                         $values (GetType($headers[$header]== 'array' $headers[$headerarray($headers[$header]));
  1763.                         $p (GetType($positions[$header]== 'array' $positions[$headerarray($positions[$header]));
  1764.                         $tv count($values);
  1765.                         for($v 0$v<$tv++$v)
  1766.                         {
  1767.                             if($addresses->ParseAddressList($values[$v]$a))
  1768.                             {
  1769.                                 if($v==0)
  1770.                                     $decoded_message['ExtractedAddresses'][$header$a;
  1771.                                 else
  1772.                                 {
  1773.                                     $tl count($a);
  1774.                                     for($l 0$l<$tl++$l)
  1775.                                         $decoded_message['ExtractedAddresses'][$header][$a[$l];
  1776.                                 }
  1777.                                 $tw count($addresses->warnings);
  1778.                                 for($w 0Reset($addresses->warnings)$w $twNext($addresses->warnings)$w++)
  1779.                                 {
  1780.                                     $warning Key($addresses->warnings);
  1781.                                     if(!$this->SetPositionedWarning('Address extraction warning from header '.$header.' '.$addresses->warnings[$warning]$warning $p[$v]))
  1782.                                         return(0);
  1783.                                 }
  1784.                             }
  1785.                             elseif(!$this->SetPositionedWarning('Address extraction error from header '.$header.' '.$addresses->error$addresses->error_position $p[$v]))
  1786.                                 return(0);
  1787.                         }
  1788.                     }
  1789.                 }
  1790.                 UnSet($decoded_message['HeaderPositions']);
  1791.             }
  1792.             $decoded[$message]=$decoded_message;
  1793.         }
  1794.         if(IsSet($parameters['File']))
  1795.             fclose($this->file);
  1796.         return($success);
  1797.     }
  1798.  
  1799. /**
  1800.  * dummy docblock makes error-free autodocs
  1801.  */
  1802.     Function CopyAddresses($message&$results$header)
  1803.     {
  1804.         if(!IsSet($message['Headers'][$header]))
  1805.             return;
  1806.         if(!IsSet($message['ExtractedAddresses'][$header]))
  1807.         {
  1808.             $parser new rfc822_addresses_class;
  1809.             $parser->ignore_syntax_errors $this->ignore_syntax_errors;
  1810.             $values (GetType($message['Headers'][$header]== 'array' $message['Headers'][$headerarray($message['Headers'][$header]));
  1811.             $tv count($values);
  1812.             $addresses array();
  1813.             for($v 0$v<$tv++$v)
  1814.             {
  1815.                 if($parser->ParseAddressList($values[$v]$a))
  1816.                 {
  1817.                     if($v==0)
  1818.                         $addresses $a;
  1819.                     else
  1820.                     {
  1821.                         $tl count($a);
  1822.                         for($l 0$l<$tl++$l)
  1823.                             $addresses[$a[$l];
  1824.                     }
  1825.                 }
  1826.             }
  1827.         }
  1828.         else
  1829.             $addresses $message['ExtractedAddresses'][$header];
  1830.         if(count($addresses))
  1831.             $results[ucfirst(substr($header0strlen($header-1))$addresses;
  1832.     }
  1833.  
  1834. /**
  1835.  * dummy docblock makes error-free autodocs
  1836.  */
  1837.     Function ReadMessageBody($message&$body$prefix)
  1838.     {
  1839.         if(IsSet($message[$prefix]))
  1840.             $body $message[$prefix];
  1841.         elseif(IsSet($message[$prefix.'File']))
  1842.         {
  1843.             $path $message[$prefix.'File'];
  1844.             if(!($file @fopen($path'rb')))
  1845.                 return($this->SetPHPError('could not open the message body file '.$path$php_errormsg));
  1846.             for($body ''$end 0;!$end;)
  1847.             {
  1848.                 if(!($data @fread($file8000)))
  1849.                 {
  1850.                     $this->SetPHPError('Could not open the message body file '.$path$php_errormsg);
  1851.                     fclose($stream);
  1852.                     return(0);
  1853.                 }
  1854.                 $end=feof($file);
  1855.                 $body.=$data;
  1856.             }
  1857.             fclose($file);
  1858.         }
  1859.         else
  1860.             $body '';
  1861.         return(1);
  1862.     }
  1863. /*
  1864. {metadocument}
  1865.     <function>
  1866.         <name>Analyze</name>
  1867.         <type>BOOLEAN</type>
  1868.         <documentation>
  1869.             <purpose>Analyze a parsed message to describe its contents.</purpose>
  1870.             <usage>Pass an array to the <argumentlink>
  1871.                     <function>Analyze</function>
  1872.                     <argument>message</argument>
  1873.                 </argumentlink>
  1874.                 parameter with the decoded message array structure returned by the
  1875.                 <functionlink>Decode</functionlink> function. The <argumentlink>
  1876.                     <function>Analyze</function>
  1877.                     <argument>results</argument>
  1878.                 </argumentlink> returns details about the type of message that was
  1879.                 analyzed and its contents.</usage>
  1880.             <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
  1881.                 the specified message is analyzed successfully. Otherwise,
  1882.                 check the variables <variablelink>error</variablelink> and
  1883.                 <variablelink>error_position</variablelink> to determine what
  1884.                 error occurred.</returnvalue>
  1885.         </documentation>
  1886.         <argument>
  1887.             <name>message</name>
  1888.             <type>HASH</type>
  1889.             <documentation>
  1890.                 <purpose>Pass an associative array with the definition of an
  1891.                     individual message returned by the <argumentlink>
  1892.                     <function>Decode</function>
  1893.                     <argument>decoded</argument>
  1894.                 </argumentlink> argument of the
  1895.                 <functionlink>Decode</functionlink> function..</purpose>
  1896.             </documentation>
  1897.         </argument>
  1898.         <argument>
  1899.             <name>results</name>
  1900.             <type>HASH</type>
  1901.             <out />
  1902.             <documentation>
  1903.                 <purpose>Returns an associative array with the results of the
  1904.                     analysis. Some types of entries are returned for all types of
  1905.                     analyzed messages. Other entries are specific to each type of
  1906.                     message.<paragraphbreak />
  1907.                     <tt>Type</tt><paragraphbreak />
  1908.                     Type of message that was analyzed. Currently it supports the
  1909.                     types: <tt>binary</tt>, <tt>text</tt>, <tt>html</tt>,
  1910.                     <tt>video</tt>, <tt>image</tt>, <tt>audio</tt>, <tt>zip</tt>,
  1911.                     <tt>pdf</tt>, <tt>postscript</tt>, <tt>ms-word</tt>,
  1912.                     <tt>ms-excel</tt>, <tt>ms-powerpoint</tt>, <tt>ms-tnef</tt>,
  1913.                     <tt>odf-writer</tt>, <tt>signature</tt>, <tt>report-type</tt>,
  1914.                     <tt>delivery-status</tt> and <tt>message</tt>.<paragraphbreak />
  1915.                     <tt>SubType</tt><paragraphbreak />
  1916.                     Name of the variant of the message type format.<paragraphbreak />
  1917.                     <tt>Description</tt><paragraphbreak />
  1918.                     Human readable description in English of the message type.<paragraphbreak />
  1919.                     <paragraphbreak />
  1920.                     <paragraphbreak />
  1921.                     <paragraphbreak />
  1922.                     <b>From message headers:</b><paragraphbreak />
  1923.                     <tt>Encoding</tt><paragraphbreak />
  1924.                     Character set encoding of the message part.<paragraphbreak />
  1925.                     <tt>Subject</tt><paragraphbreak />
  1926.                     The message subject.<paragraphbreak />
  1927.                     <tt>SubjectEncoding</tt><paragraphbreak />
  1928.                     Character set encoding of the message subject.<paragraphbreak />
  1929.                     <tt>Date</tt><paragraphbreak />
  1930.                     The message date.<paragraphbreak />
  1931.                     <tt>From</tt><paragraphbreak />
  1932.                     <tt>To</tt><paragraphbreak />
  1933.                     <tt>Cc</tt><paragraphbreak />
  1934.                     <tt>Bcc</tt><paragraphbreak />
  1935.                     Array of e-mail addresses found in the <tt>From</tt>,
  1936.                     <tt>To</tt>, <tt>Cc</tt>, <tt>Bcc</tt>.<paragraphbreak />
  1937.                     Each of the entries consists of an associative array with an
  1938.                     entry named <tt>address</tt> with the e-mail address and
  1939.                     optionally another named <tt>name</tt> with the associated
  1940.                     name.<paragraphbreak />
  1941.                     <paragraphbreak />
  1942.                     <paragraphbreak />
  1943.                     <b>For content message parts:</b><paragraphbreak />
  1944.                     <paragraphbreak />
  1945.                     <tt>Data</tt><paragraphbreak />
  1946.                     String of data of the message part.<paragraphbreak />
  1947.                     <tt>DataFile</tt><paragraphbreak />
  1948.                     File with data of the message part.<paragraphbreak />
  1949.                     <tt>DataLength</tt><paragraphbreak />
  1950.                     Length of the data of the message part.<paragraphbreak />
  1951.                     <paragraphbreak />
  1952.                     <paragraphbreak />
  1953.                     <paragraphbreak />
  1954.                     <b>For message with embedded files:</b><paragraphbreak />
  1955.                     <paragraphbreak />
  1956.                     <tt>FileName</tt><paragraphbreak />
  1957.                     Original name of the file.<paragraphbreak />
  1958.                     <tt>ContentID</tt><paragraphbreak />
  1959.                     Content identifier of the file to be used in references from
  1960.                     other message parts.<paragraphbreak />
  1961.                     For instance, an HTML message may reference images embedded in
  1962.                     the message using URLs that start with the
  1963.                     <stringvalue>cid:</stringvalue> followed by the content
  1964.                     identifier of the embedded image file part.<paragraphbreak />
  1965.                     <tt>Disposition</tt><paragraphbreak />
  1966.                     Information of whether the embedded file should be displayed
  1967.                     inline when the message is presented, or it is an attachment
  1968.                     file.<paragraphbreak />
  1969.                     <paragraphbreak />
  1970.                     <paragraphbreak />
  1971.                     <b>For composite message:</b><paragraphbreak />
  1972.                     <paragraphbreak />
  1973.                     <tt>Attachments</tt><paragraphbreak />
  1974.                     List of files attached to the message.<paragraphbreak />
  1975.                     <tt>Alternative</tt><paragraphbreak />
  1976.                     List of alternative message parts that can be displayed if the
  1977.                     main message type is not supported by the program displaying
  1978.                     the message.<paragraphbreak />
  1979.                     <tt>Related</tt><paragraphbreak />
  1980.                     List of message parts related with the main message type.<paragraphbreak />
  1981.                     It may list for instance embedded images or CSS files related
  1982.                     with an HTML message type.<paragraphbreak />
  1983.                     <paragraphbreak />
  1984.                     <paragraphbreak />
  1985.                     <b>For bounced messages or other types of delivery status report
  1986.                     messages:</b><paragraphbreak />
  1987.                     <paragraphbreak />
  1988.                     <tt>Recipients</tt><paragraphbreak />
  1989.                     List of recipients of the original message.<paragraphbreak />
  1990.                     Each entry contains an associative array that may have the
  1991.                     entries: <tt>Recipient</tt> with the original recipient address,
  1992.                     <tt>Action</tt> with the name action that triggered the delivery
  1993.                     status report, <tt>Status</tt> with the code of the status of
  1994.                     the message delivery.<paragraphbreak />
  1995.                     <tt>Response</tt><paragraphbreak />
  1996.                     Human readable response sent by the server the originated the
  1997.                     report.<paragraphbreak />
  1998.                     </purpose>
  1999.             </documentation>
  2000.         </argument>
  2001.         <do>
  2002. {/metadocument}
  2003. */
  2004. /**
  2005.  * dummy docblock makes error-free autodocs
  2006.  */
  2007.     Function Analyze($message&$results)
  2008.     {
  2009.         $results array();
  2010.         $content_type (IsSet($message['Headers']['content-type:']$message['Headers']['content-type:''text/plain');
  2011.         $disposition $this->ParseParameters($content_type$content_type$parameters'disposition');
  2012.         $type $this->Tokenize($content_type'/');
  2013.         $sub_type $this->Tokenize(';');
  2014.         $copy_body 1;
  2015.         $tolerate_unrecognized 1;
  2016.         switch($type)
  2017.         {
  2018.             case 'multipart':
  2019.                 $tolerate_unrecognized 0;
  2020.                 $copy_body 0;
  2021.                 $lp count($message['Parts']);
  2022.                 if($lp == 0)
  2023.                     return($this->SetError($this->decode_bodies ? 'No parts were found in the '.$content_type.' part message' 'It is not possible to analyze multipart messages without parsing the contained message parts. Please set the decode_bodies variable to 1 before parsing the message'));
  2024.                 $parts array();
  2025.                 for($p 0$p $lp++$p)
  2026.                 {
  2027.                     if(!$this->Analyze($message['Parts'][$p]$parts[$p]))
  2028.                         return(0);
  2029.                 }
  2030.                 switch($sub_type)
  2031.                 {
  2032.                     case 'alternative':
  2033.                         $p $lp;
  2034.                         $results $parts[--$p];
  2035.                         for(--$p $p >=--$p)
  2036.                             $results['Alternative'][$parts[$p];
  2037.                         break;
  2038.  
  2039.                     case 'related':
  2040.                         $results $parts[0];
  2041.                         for($p 1$p $lp++$p)
  2042.                             $results['Related'][$parts[$p];
  2043.                         break;
  2044.  
  2045.                     case 'mixed':
  2046.                         $results $parts[0];
  2047.                         for($p 1$p $lp++$p)
  2048.                             $results['Attachments'][$parts[$p];
  2049.                         break;
  2050.  
  2051.                     case 'report':
  2052.                         if(IsSet($parameters['report-type']))
  2053.                         {
  2054.                             switch($parameters['report-type'])
  2055.                             {
  2056.                                 case 'delivery-status':
  2057.                                     for($p 1$p $lp++$p)
  2058.                                     {
  2059.                                         if(!strcmp($parts[$p]['Type']$parameters['report-type']))
  2060.                                         {
  2061.                                             $results $parts[$p];
  2062.                                             break;
  2063.                                         }
  2064.                                     }
  2065.                                     if(!$this->ReadMessageBody($parts[0]$body'Data'))
  2066.                                         return(0);
  2067.                                     if(strlen($body))
  2068.                                         $results['Response'$body;
  2069.                                     break;
  2070.                             }
  2071.                         }
  2072.                         $results['Type'$parameters['report-type'];
  2073.                         break;
  2074.  
  2075.                     case 'signed':
  2076.                         if($lp != 2)
  2077.                             return($this->SetError('this '.$content_type.' message does not have just 2 parts'));
  2078.                         if(strcmp($parts[1]['Type']'signature'))
  2079.                         {
  2080.                             $this->SetErrorWithContact('this '.$content_type.' message does not contain a signature');
  2081.                             $this->error = '';
  2082.                         }
  2083.                         $results $parts[0];
  2084.                         $results['Signature'$parts[1];
  2085.                         break;
  2086.                 }
  2087.                 break;
  2088.             case 'text':
  2089.                 switch($sub_type)
  2090.                 {
  2091.                     case 'plain':
  2092.                         $results['Type''text';
  2093.                         $results['Description''Text message';
  2094.                         break;
  2095.                     case 'html':
  2096.                         $results['Type''html';
  2097.                         $results['Description''HTML message';
  2098.                         break;
  2099.                     default:
  2100.                         $results['Type'$type;
  2101.                         $results['SubType'$sub_type;
  2102.                         $results['Description''Text file in the '.strtoupper($sub_type).' format';
  2103.                         break;
  2104.                 }
  2105.                 break;
  2106.             case 'video':
  2107.                 $results['Type'$type;
  2108.                 $results['SubType'$sub_type;
  2109.                 $results['Description''Video file in the '.strtoupper($sub_type).' format';
  2110.                 break;
  2111.             case 'image':
  2112.                 $results['Type'$type;
  2113.                 $results['SubType'$sub_type;
  2114.                 $results['Description''Image file in the '.strtoupper($sub_type).' format';
  2115.                 break;
  2116.             case 'audio':
  2117.                 $results['Type'$type;
  2118.                 $results['SubType'$sub_type;
  2119.                 $results['Description''Audio file in the '.strtoupper($sub_type).' format';
  2120.                 break;
  2121.             case 'application':
  2122.                 switch($sub_type)
  2123.                 {
  2124.                     case 'octet-stream':
  2125.                     case 'x-msdownload':
  2126.                         $results['Type''binary';
  2127.                         $results['Description''Binary file';
  2128.                         break;
  2129.                     case 'pdf':
  2130.                         $results['Type'$sub_type;
  2131.                         $results['Description''Document in PDF format';
  2132.                         break;
  2133.                     case 'postscript':
  2134.                         $results['Type'$sub_type;
  2135.                         $results['Description''Document in Postscript format';
  2136.                         break;
  2137.                     case 'msword':
  2138.                         $results['Type''ms-word';
  2139.                         $results['Description''Word processing document in Microsoft Word format';
  2140.                         break;
  2141.                     case 'vnd.ms-powerpoint':
  2142.                         $results['Type''ms-powerpoint';
  2143.                         $results['Description''Presentation in Microsoft PowerPoint format';
  2144.                         break;
  2145.                     case 'vnd.ms-excel':
  2146.                         $results['Type''ms-excel';
  2147.                         $results['Description''Spreadsheet in Microsoft Excel format';
  2148.                         break;
  2149.                     case 'zip':
  2150.                     case 'x-zip':
  2151.                     case 'x-zip-compressed':
  2152.                         $results['Type''zip';
  2153.                         $results['Description''ZIP archive with compressed files';
  2154.                         break;
  2155.                     case 'ms-tnef':
  2156.                         $results['Type'$sub_type;
  2157.                         $results['Description''Microsoft Exchange data usually sent by Microsoft Outlook';
  2158.                         break;
  2159.                     case 'pgp-signature':
  2160.                         $results['Type''signature';
  2161.                         $results['SubType'$sub_type;
  2162.                         $results['Description''Message signature for PGP';
  2163.                         break;
  2164.                     case 'x-pkcs7-signature':
  2165.                     case 'pkcs7-signature':
  2166.                         $results['Type''signature';
  2167.                         $results['SubType'$sub_type;
  2168.                         $results['Description''PKCS message signature';
  2169.                         break;
  2170.                     case 'vnd.oasis.opendocument.text':
  2171.                         $results['Type''odf-writer';
  2172.                         $results['Description''Word processing document in ODF text format used by OpenOffice Writer';
  2173.                         break;
  2174.                 }
  2175.                 break;
  2176.             case 'message':
  2177.                 $tolerate_unrecognized 0;
  2178.                 switch($sub_type)
  2179.                 {
  2180.                     case 'delivery-status':
  2181.                         $results['Type'$sub_type;
  2182.                         $results['Description''Notification of the status of delivery of a message';
  2183.                         if(!$this->ReadMessageBody($message$body'Body'))
  2184.                             return(0);
  2185.                         if(($l strlen($body)))
  2186.                         {
  2187.                             $position 0;
  2188.                             $this->ParseHeaderString($body$position$headers);
  2189.                             $recipients array();
  2190.                             for(;$position<$l;)
  2191.                             {
  2192.                                 $this->ParseHeaderString($body$position$headers);
  2193.                                 if(count($headers))
  2194.                                 {
  2195.                                     $r count($recipients);
  2196.                                     if(IsSet($headers['action']))
  2197.                                         $recipients[$r]['Action'$headers['action'];
  2198.                                     if(IsSet($headers['status']))
  2199.                                         $recipients[$r]['Status'$headers['status'];
  2200.                                     if(IsSet($headers['original-recipient']))
  2201.                                     {
  2202.                                         strtok($headers['original-recipient']';');
  2203.                                         $recipients[$r]['Address'trim(strtok(''));
  2204.                                     }
  2205.                                     elseif(IsSet($headers['final-recipient']))
  2206.                                     {
  2207.                                         strtok($headers['final-recipient']';');
  2208.                                         $recipients[$r]['Address'trim(strtok(''));
  2209.                                     }
  2210.                                 }
  2211.                             }
  2212.                             $results['Recipients'$recipients;
  2213.                         }
  2214.                         $copy_body 0;
  2215.                         break;
  2216.                     case 'rfc822':
  2217.                         $results['Type''message';
  2218.                         $results['Description''E-mail message';
  2219.                         break;
  2220.                 }
  2221.                 break;
  2222.             default:
  2223.                 $tolerate_unrecognized 0;
  2224.                 break;
  2225.         }
  2226.         if(!IsSet($results['Type']))
  2227.         {
  2228.             $this->SetErrorWithContact($content_type.' message parts are not yet recognized');
  2229.             $results['Type'$this->error;
  2230.             $this->error = '';
  2231.         }
  2232.         if(IsSet($parameters['charset']))
  2233.             $results['Encoding'strtolower($parameters['charset']);
  2234.         if(IsSet($message['Headers']['subject:']))
  2235.         {
  2236.             if(IsSet($message['DecodedHeaders']['subject:'])
  2237.             && count($message['DecodedHeaders']['subject:']== 1
  2238.             && count($message['DecodedHeaders']['subject:'][0]== 1)
  2239.             {
  2240.                 $results['Subject'$message['DecodedHeaders']['subject:'][0][0]['Value'];
  2241.                 $results['SubjectEncoding'strtolower($message['DecodedHeaders']['subject:'][0][0]['Encoding']);
  2242.             }
  2243.             else
  2244.                 $results['Subject'$message['Headers']['subject:'];
  2245.         }
  2246.         if(IsSet($message['Headers']['date:']))
  2247.         {
  2248.             if(IsSet($message['DecodedHeaders']['date:'])
  2249.             && count($message['DecodedHeaders']['date:']== 1
  2250.             && count($message['DecodedHeaders']['date:'][0]== 1)
  2251.                 $results['Date'$message['DecodedHeaders']['date:'][0][0]['Value'];
  2252.             else
  2253.                 $results['Date'$message['Headers']['date:'];
  2254.         }
  2255.         $l count($this->address_headers);
  2256.         for(Reset($this->address_headers)$h 0$h<$lNext($this->address_headers)++$h)
  2257.             $this->CopyAddresses($message$resultsKey($this->address_headers));
  2258.         if($copy_body)
  2259.         {
  2260.             if(IsSet($message['Body']))
  2261.                 $results['Data'$message['Body'];
  2262.             elseif(IsSet($message['BodyFile']))
  2263.                 $results['DataFile'$message['BodyFile'];
  2264.             elseif(IsSet($message['BodyLength']))
  2265.                 $results['DataLength'$message['BodyLength'];
  2266.             if(IsSet($message['FileName']))
  2267.                 $results['FileName'$message['FileName'];
  2268.             if(IsSet($message['FileDisposition']))
  2269.                 $results['FileDisposition'$message['FileDisposition'];
  2270.             if(IsSet($message['Headers']['content-id:']))
  2271.             {
  2272.                 $content_id trim($message['Headers']['content-id:']);
  2273.                 $l strlen($content_id);
  2274.                 if(!strcmp($content_id[0]'<')
  2275.                 && !strcmp($content_id[$l 1]'>'))
  2276.                     $results['ContentID'substr($content_id1$l 2);
  2277.             }
  2278.         }
  2279.         return(1);
  2280.     }
  2281.  
  2282. };
  2283.  
  2284.  
  2285. ?>