Source for file Message.class.php

Documentation is available at Message.class.php

  1. <?php
  2.  
  3. /**
  4.  * Message.class.php
  5.  *
  6.  * This file contains functions needed to handle mime messages.
  7.  *
  8.  * @copyright 2003-2020 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: Message.class.php 14840 2020-01-07 07:42:38Z pdontthink $
  11.  * @package squirrelmail
  12.  * @subpackage mime
  13.  * @since 1.3.2
  14.  */
  15.  
  16. /**
  17.  * The object that contains a message.
  18.  *
  19.  * message is the object that contains messages. It is a recursive object in
  20.  * that through the $entities variable, it can contain more objects of type
  21.  * message. See documentation in mime.txt for a better description of how this
  22.  * works.
  23.  * @package squirrelmail
  24.  * @subpackage mime
  25.  * @since 1.3.0
  26.  */
  27. class Message {
  28.     /**
  29.      * rfc822header object
  30.      * @var object 
  31.      */
  32.     var $rfc822_header = '';
  33.     /**
  34.      * Headers from original email in reply
  35.      * @var string 
  36.      */
  37.     var $reply_rfc822_header = '';
  38.     /**
  39.      * MessageHeader object
  40.      * @var object 
  41.      */
  42.     var $mime_header = '';
  43.     /**
  44.      * @var mixed 
  45.      */
  46.     var $flags = '';
  47.     /**
  48.      * Media type
  49.      * @var string 
  50.      */
  51.     var $type0='';
  52.     /**
  53.      * Media subtype
  54.      * @var string 
  55.      */
  56.     var $type1='';
  57.     /**
  58.      * Nested mime parts
  59.      * @var array 
  60.      */
  61.     var $entities = array();
  62.     /**
  63.      * Message part id
  64.      * @var string 
  65.      */
  66.     var $entity_id = '';
  67.     /**
  68.      * Parent message part id
  69.      * @var string 
  70.      */
  71.     var $parent_ent;
  72.     /**
  73.      * @var mixed 
  74.      */
  75.     var $entity;
  76.     /**
  77.      * @var mixed 
  78.      */
  79.     var $parent = '';
  80.     /**
  81.      * @var string 
  82.      */
  83.     var $decoded_body='';
  84.     /**
  85.      * Message \seen status
  86.      * @var boolean 
  87.      */
  88.     var $is_seen = 0;
  89.     /**
  90.      * Message \answered status
  91.      * @var boolean 
  92.      */
  93.     var $is_answered = 0;
  94.     /**
  95.      * Message \deleted status
  96.      * @var boolean 
  97.      */
  98.     var $is_deleted = 0;
  99.     /**
  100.      * Message \flagged status
  101.      * @var boolean 
  102.      */
  103.     var $is_flagged = 0;
  104.     /**
  105.      * Message mdn status
  106.      * @var boolean 
  107.      */
  108.     var $is_mdnsent = 0;
  109.     /**
  110.      * Message text body
  111.      * @var string 
  112.      */
  113.     var $body_part = '';
  114.     /**
  115.      * Message part offset
  116.      * for fetching body parts out of raw messages
  117.      * @var integer 
  118.      */
  119.     var $offset = 0;
  120.     /**
  121.      * Message part length
  122.      * for fetching body parts out of raw messages
  123.      * @var integer 
  124.      */
  125.     var $length = 0;
  126.     /**
  127.      * Local attachment filename location where the tempory attachment is
  128.      * stored. For use in delivery class.
  129.      * @var string 
  130.      */
  131.     var $att_local_name = '';
  132.  
  133.     /**
  134.      * @param string $ent entity id
  135.      */
  136.     function setEnt($ent{
  137.         $this->entity_id$ent;
  138.     }
  139.  
  140.     /**
  141.      * Add nested message part
  142.      * @param object $msg 
  143.      */
  144.     function addEntity ($msg{
  145.         $this->entities[$msg;
  146.     }
  147.  
  148.     /**
  149.      * Get file name used for mime part
  150.      * @return string file name
  151.      * @since 1.3.2
  152.      */
  153.     function getFilename({
  154.          $filename '';
  155.          $header $this->header;
  156.          if (is_object($header->disposition)) {
  157.               $filename $header->disposition->getProperty('filename');
  158.               if (trim($filename== ''{
  159.                   $name decodeHeader($header->disposition->getProperty('name'));
  160.                   if (!trim($name)) {
  161.                       $name $header->getParameter('name');
  162.                       if(!trim($name)) {
  163.                           if (!trim$header->id )) {
  164.                               $filename 'untitled-[' $this->entity_id . ']' '.' strtolower($header->type1);
  165.                           else {
  166.                               $filename 'cid: ' $header->id '.' strtolower($header->type1);
  167.                           }
  168.                       else {
  169.                           $filename $name;
  170.                       }
  171.                   else {
  172.                       $filename $name;
  173.                   }
  174.               }
  175.          else {
  176.               $filename $header->getParameter('filename');
  177.               if (!trim($filename)) {
  178.                   $filename $header->getParameter('name');
  179.                   if (!trim($filename)) {
  180.                       if (!trim$header->id )) {
  181.                           $filename 'untitled-[' $this->entity_id . ']' '.' strtolower($header->type1);
  182.                       else {
  183.                           $filename 'cid: ' $header->id '.' strtolower($header->type1);
  184.                       }
  185.                   }
  186.               }
  187.          }
  188.          return $filename;
  189.     }
  190.  
  191.     /**
  192.      * Add header object to message object.
  193.      * WARNING: Unfinished code. Don't expect it to work in older sm versions.
  194.      * @param mixed $read array or string with message headers
  195.      * @todo FIXME: rfc822header->parseHeader() does not return rfc822header object
  196.      */
  197.     function addRFC822Header($read{
  198.         $header new Rfc822Header();
  199.         $this->rfc822_header = $header->parseHeader($read);
  200.     }
  201.  
  202.     /**
  203.      * @param string $ent 
  204.      * @return mixed (object or string?)
  205.      */
  206.     function getEntity($ent{
  207.         $cur_ent $this->entity_id;
  208.         $msg $this;
  209.         if (($cur_ent == ''|| ($cur_ent == '0')) {
  210.             $cur_ent_a array();
  211.         else {
  212.             $cur_ent_a explode('.'$this->entity_id);
  213.         }
  214.         $ent_a explode('.'$ent);
  215.  
  216.         for ($i 0,$entCount count($ent_a1$i $entCount++$i{
  217.             if (isset($cur_ent_a[$i]&& ($cur_ent_a[$i!= $ent_a[$i])) {
  218.                 $msg $msg->parent;
  219.                 $cur_ent_a explode('.'$msg->entity_id);
  220.                 --$i;
  221.             else if (!isset($cur_ent_a[$i])) {
  222.                 if (isset($msg->entities[($ent_a[$i]-1)])) {
  223.                     $msg $msg->entities[($ent_a[$i]-1)];
  224.                 else {
  225.                     $msg $msg->entities[0];
  226.                 }
  227.             }
  228.             if (($msg->type0 == 'message'&& ($msg->type1 == 'rfc822')) {
  229.                 /*this is a header for a message/rfc822 entity */
  230.                 $msg $msg->entities[0];
  231.             }
  232.         }
  233.  
  234.         if (($msg->type0 == 'message'&& ($msg->type1 == 'rfc822')) {
  235.             /*this is a header for a message/rfc822 entity */
  236.             $msg $msg->entities[0];
  237.         }
  238.  
  239.         if (isset($msg->entities[($ent_a[$entCount])-1])) {
  240.             if (is_object($msg->entities[($ent_a[$entCount])-1])) {
  241.                 $msg $msg->entities[($ent_a[$entCount]-1)];
  242.             }
  243.         }
  244.  
  245.         return $msg;
  246.     }
  247.  
  248.     /**
  249.      * Set message body
  250.      * @param string $s message body
  251.      */
  252.     function setBody($s{
  253.         $this->body_part = $s;
  254.     }
  255.  
  256.     /**
  257.      * Clean message object
  258.      */
  259.     function clean_up({
  260.         $msg $this;
  261.         $msg->body_part '';
  262.  
  263.         foreach ($msg->entities as $m{
  264.             $m->clean_up();
  265.         }
  266.     }
  267.  
  268.     /**
  269.      * @return string 
  270.      */
  271.     function getMailbox({
  272.         $msg $this;
  273.         while (is_object($msg->parent)) {
  274.             $msg $msg->parent;
  275.         }
  276.         return $msg->mailbox;
  277.     }
  278.  
  279.     /*
  280.      * Bodystructure parser, a recursive function for generating the
  281.      * entity-tree with all the mime-parts.
  282.      *
  283.      * It follows RFC2060 and stores all the described fields in the
  284.      * message object.
  285.      *
  286.      * Question/Bugs:
  287.      *
  288.      * Ask for me (Marc Groot Koerkamp, [email protected])
  289.      * @param string $read
  290.      * @param integer $i
  291.      * @param mixed $sub_msg
  292.      * @return object Message object
  293.      * @todo define argument and return types
  294.      */
  295.     static function parseStructure($read&$i$sub_msg ''{
  296.         $msg Message::parseBodyStructure($read$i$sub_msg);
  297.         if($msg$msg->setEntIds($msg,false,0);
  298.         return $msg;
  299.     }
  300.  
  301.     /**
  302.      * @param object $msg 
  303.      * @param mixed $init 
  304.      * @param integer $i 
  305.      * @todo document me
  306.      * @since 1.4.0
  307.      */
  308.     function setEntIds(&$msg,$init=false,$i=0{
  309.         $iCnt count($msg->entities);
  310.         if ($init !==false{
  311.             $iEntSub $i+1;
  312.             if ($msg->parent->type0 == 'message' &&
  313.                 $msg->parent->type1 == 'rfc822' &&
  314.                 $msg->type0 == 'multipart'{
  315.                 $iEntSub '0';
  316.             }
  317.             if ($init{
  318.                 $msg->entity_id "$init.$iEntSub";
  319.             else {
  320.                 $msg->entity_id $iEntSub;
  321.             }
  322.         else if ($iCnt{
  323.             $msg->entity_id='0';
  324.         else {
  325.             $msg->entity_id='1';
  326.         }
  327.         for ($i=0;$i<$iCnt;++$i{
  328.             $msg->entities[$i]->parent =$msg;
  329.             if (strrchr($msg->entity_id'.'!= '.0'{
  330.                 $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->entity_id,$i);
  331.             else {
  332.                 $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->parent->entity_id,$i);
  333.             }
  334.         }
  335.     }
  336.  
  337.     /**
  338.      * @param string $read 
  339.      * @param integer $i 
  340.      * @param mixed $sub_msg 
  341.      * @return object Message object
  342.      * @todo document me
  343.      * @since 1.4.0 (code was part of parseStructure() in 1.3.x)
  344.      */
  345.     static function parseBodyStructure($read&$i$sub_msg ''{
  346.         $arg_no 0;
  347.         $arg_a  array();
  348.         if ($sub_msg{
  349.             $message $sub_msg;
  350.         else {
  351.             $message new Message();
  352.         }
  353.  
  354.         for ($cnt strlen($read)$i $cnt++$i{
  355.             $char strtoupper($read{$i});
  356.             switch ($char{
  357.                 case '(':
  358.                     switch($arg_no{
  359.                         case 0:
  360.                             if (!isset($msg)) {
  361.                                 $msg new Message();
  362.                                 $hdr new MessageHeader();
  363.                                 $hdr->type0 'text';
  364.                                 $hdr->type1 'plain';
  365.                                 $hdr->encoding '7bit';
  366.                                 $msg->header $hdr;
  367.                             else {
  368.                                 $msg->header->type0 'multipart';
  369.                                 $msg->type0 'multipart';
  370.                                 while ($read{$i== '('{
  371.                                     $msg->addEntity($msg->parseBodyStructure($read$i$msg));
  372.                                 }
  373.                             }
  374.                             break;
  375.                         case 1:
  376.                             /* multipart properties */
  377.                             ++$i;
  378.                             $arg_a[$msg->parseProperties($read$i);
  379.                             ++$arg_no;
  380.                             break;
  381.                         case 2:
  382.                             if (isset($msg->type0&& ($msg->type0 == 'multipart')) {
  383.                                 ++$i;
  384.                                 $arg_a[$msg->parseDisposition($read$i);
  385.                             else /* properties */
  386.                                 $arg_a[$msg->parseProperties($read$i);
  387.                             }
  388.                             ++$arg_no;
  389.                             break;
  390.                         case 3:
  391.                             if (isset($msg->type0&& ($msg->type0 == 'multipart')) {
  392.                                 ++$i;
  393.                                 $arg_a[]$msg->parseLanguage($read$i);
  394.                             }
  395.                         case 7:
  396.                             if (($arg_a[0== 'message'&& ($arg_a[1== 'rfc822')) {
  397.                                 $msg->header->type0 $arg_a[0];
  398.                                 $msg->header->type1 $arg_a[1];
  399.                                 $msg->type0 $arg_a[0];
  400.                                 $msg->type1 $arg_a[1];
  401.                                 $rfc822_hdr new Rfc822Header();
  402.                                 $msg->rfc822_header $msg->parseEnvelope($read$i$rfc822_hdr);
  403.                                 while (($i $cnt&& ($read{$i!= '(')) {
  404.                                     ++$i;
  405.                                 }
  406.                                 $msg->addEntity($msg->parseBodyStructure($read$i,$msg));
  407.                             }
  408.                             break;
  409.                         case 8:
  410.                             ++$i;
  411.                             $arg_a[$msg->parseDisposition($read$i);
  412.                             ++$arg_no;
  413.                             break;
  414.                         case 9:
  415.                             ++$i;
  416.                             if (($arg_a[0== 'text'|| (($arg_a[0== 'message'&& ($arg_a[1== 'rfc822'))) {
  417.                                 $arg_a[$msg->parseDisposition($read$i);
  418.                             else {
  419.                                 $arg_a[$msg->parseLanguage($read$i);
  420.                             }
  421.                             ++$arg_no;
  422.                             break;
  423.                        case 10:
  424.                            if (($arg_a[0== 'text'|| (($arg_a[0== 'message'&& ($arg_a[1== 'rfc822'))) {
  425.                                ++$i;
  426.                                $arg_a[$msg->parseLanguage($read$i);
  427.                            else {
  428.                                $i $msg->parseParenthesis($read$i);
  429.                                $arg_a[''/* not yet described in rfc2060 */
  430.                            }
  431.                            ++$arg_no;
  432.                            break;
  433.                        default:
  434.                            /* unknown argument, skip this part */
  435.                            $i $msg->parseParenthesis($read$i);
  436.                            $arg_a['';
  437.                            ++$arg_no;
  438.                            break;
  439.                    /* switch */
  440.                    break;
  441.                 case '"':
  442.                     /* inside an entity -> start processing */
  443.                     $arg_s $msg->parseQuote($read$i);
  444.                     ++$arg_no;
  445.                     if ($arg_no 3{
  446.                         $arg_s strtolower($arg_s)/* type0 and type1 */
  447.                     }
  448.                     $arg_a[$arg_s;
  449.                     break;
  450.                 case 'n':
  451.                 case 'N':
  452.                     /* probably NIL argument */
  453.                     $tmpnil strtoupper(substr($read$i4));
  454.                     if ($tmpnil == 'NIL ' || $tmpnil == 'NIL)'{
  455.                         $arg_a['';
  456.                         ++$arg_no;
  457.                         $i += 2;
  458.                     }
  459.                     break;
  460.                 case '{':
  461.                     /* process the literal value */
  462.                     $arg_a[$msg->parseLiteral($read$i);
  463.                     ++$arg_no;
  464.                     break;
  465.                 case '0':
  466.                 case is_numeric($read{$i}):
  467.                     /* process integers */
  468.                     if ($read{$i== ' 'break}
  469.                     ++$arg_no;
  470.                     if (preg_match('/^([0-9]+).*/',substr($read,$i)$regs)) {
  471.                         $i += strlen($regs[1])-1;
  472.                         $arg_a[$regs[1];
  473.                     else {
  474.                         $arg_a[0;
  475.                     }
  476.                     break;
  477.                 case ')':
  478.                     $multipart (isset($msg->type0&& ($msg->type0 == 'multipart'));
  479.                     if (!$multipart{
  480.                         $shifted_args (($arg_a[0== 'text'|| (($arg_a[0== 'message'&& ($arg_a[1== 'rfc822')));
  481.                         $hdr->type0 $arg_a[0];
  482.                         $hdr->type1 $arg_a[1];
  483.  
  484.                         $msg->type0 $arg_a[0];
  485.                         $msg->type1 $arg_a[1];
  486.                         $arr $arg_a[2];
  487.                         if (is_array($arr)) {
  488.                             $hdr->parameters $arg_a[2];
  489.                         }
  490.                         $hdr->id str_replace('<'''str_replace('>'''$arg_a[3]));
  491.                         $hdr->description $arg_a[4];
  492.                         $hdr->encoding strtolower($arg_a[5]);
  493.                         $hdr->entity_id $msg->entity_id;
  494.                         $hdr->size $arg_a[6];
  495.                         if ($shifted_args{
  496.                             $hdr->lines $arg_a[7];
  497.                             $s 1;
  498.                         else {
  499.                             $s 0;
  500.                         }
  501.                         $hdr->md5 (isset($arg_a[7+$s]$arg_a[7+$s$hdr->md5);
  502.                         $hdr->disposition (isset($arg_a[8+$s]$arg_a[8+$s$hdr->disposition);
  503.                         $hdr->language (isset($arg_a[9+$s]$arg_a[9+$s$hdr->language);
  504.                         $msg->header $hdr;
  505.                     else {
  506.                         $hdr->type0 'multipart';
  507.                         $hdr->type1 $arg_a[0];
  508.                         $msg->type0 'multipart';
  509.                         $msg->type1 $arg_a[0];
  510.                         $hdr->parameters (isset($arg_a[1]$arg_a[1$hdr->parameters);
  511.                         $hdr->disposition (isset($arg_a[2]$arg_a[2$hdr->disposition);
  512.                         $hdr->language (isset($arg_a[3]$arg_a[3$hdr->language);
  513.                         $msg->header $hdr;
  514.                     }
  515.                     return $msg;
  516.                 defaultbreak;
  517.             /* switch */
  518.         /* for */
  519.     /* parsestructure */
  520.  
  521.     /**
  522.      * @param string $read 
  523.      * @param integer $i 
  524.      * @return array 
  525.      */
  526.     function parseProperties($read&$i{
  527.         $properties array();
  528.         $prop_name '';
  529.  
  530.         for ($read{$i!= ')'++$i{
  531.             $arg_s '';
  532.             if ($read{$i== '"'{
  533.                 $arg_s $this->parseQuote($read$i);
  534.             else if ($read{$i== '{'{
  535.                 $arg_s $this->parseLiteral($read$i);
  536.             }
  537.  
  538.             if ($arg_s != ''{
  539.                 if ($prop_name == ''{
  540.                     $prop_name strtolower($arg_s);
  541.                     $properties[$prop_name'';
  542.                 else if ($prop_name != ''{
  543.                     $properties[$prop_name$arg_s;
  544.                     $prop_name '';
  545.                 }
  546.             }
  547.         }
  548.         return $properties;
  549.     }
  550.  
  551.     /**
  552.      * @param string $read 
  553.      * @param integer $i 
  554.      * @param object $hdr MessageHeader object
  555.      * @return object MessageHeader object
  556.      */
  557.     function parseEnvelope($read&$i$hdr{
  558.         $arg_no 0;
  559.         $arg_a array();
  560.         ++$i;
  561.         for ($cnt strlen($read)($i $cnt&& ($read{$i!= ')')++$i{
  562.             $char strtoupper($read{$i});
  563.             switch ($char{
  564.                 case '"':
  565.                     $arg_a[$this->parseQuote($read$i);
  566.                     ++$arg_no;
  567.                     break;
  568.                 case '{':
  569.                     $arg_a[$this->parseLiteral($read$i);
  570.             /* temp bugfix (SM 1.5 will have a working clean version)
  571.                too much work to implement that version right now */
  572. //            --$i;
  573.                     ++$arg_no;
  574.                     break;
  575.                 case 'N':
  576.                     /* probably NIL argument */
  577.                     if (strtoupper(substr($read$i3)) == 'NIL'{
  578.                         $arg_a['';
  579.                         ++$arg_no;
  580.                         $i += 2;
  581.                     }
  582.                     break;
  583.                 case '(':
  584.                     /* Address structure (with group support)
  585.                      * Note: Group support is useless on SMTP connections
  586.                      *       because the protocol doesn't support it
  587.                      */
  588.                     $addr_a array();
  589.                     $group '';
  590.                     $a=0;
  591.                     for ($i $cnt && $read{$i!= ')'++$i{
  592.                         if ($read{$i== '('{
  593.                             $addr $this->parseAddress($read$i);
  594.                             if (($addr->host == ''&& ($addr->mailbox != '')) {
  595.                                 /* start of group */
  596.                                 $group $addr->mailbox;
  597.                                 $group_addr $addr;
  598.                                 $j $a;
  599.                             else if ($group && ($addr->host == ''&& ($addr->mailbox == '')) {
  600.                                /* end group */
  601.                                 if ($a == ($j+1)) /* no group members */
  602.                                     $group_addr->group $group;
  603.                                     $group_addr->mailbox '';
  604.                                     $group_addr->personal "$group: Undisclosed recipients;";
  605.                                     $addr_a[$group_addr;
  606.                                     $group ='';
  607.                                 }
  608.                             else {
  609.                                 $addr->group $group;
  610.                                 $addr_a[$addr;
  611.                             }
  612.                             ++$a;
  613.                         }
  614.                     }
  615.                     $arg_a[$addr_a;
  616.                     break;
  617.                 defaultbreak;
  618.             }
  619.         }
  620.  
  621.         if (count($arg_a9{
  622.             $d strtr($arg_a[0]array('  ' => ' '));
  623.             $d_parts explode(' '$d);
  624.             if (!$arg_a[1]$arg_a[1_("(no subject)");
  625.  
  626.             $hdr->date getTimeStamp($d_parts)/* argument 1: date */
  627.             $hdr->date_unparsed strtr($d,'<>','  ')/* original date */
  628.             $hdr->subject $arg_a[1];     /* argument 2: subject */
  629.             $hdr->from is_array($arg_a[2]$arg_a[2][0'';     /* argument 3: from        */
  630.             $hdr->sender is_array($arg_a[3]$arg_a[3][0'';   /* argument 4: sender      */
  631.             $hdr->reply_to is_array($arg_a[4]$arg_a[4][0'';  /* argument 5: reply-to    */
  632.             $hdr->to $arg_a[5];          /* argument 6: to          */
  633.             $hdr->cc $arg_a[6];          /* argument 7: cc          */
  634.             $hdr->bcc $arg_a[7];         /* argument 8: bcc         */
  635.             $hdr->in_reply_to $arg_a[8];   /* argument 9: in-reply-to */
  636.             $hdr->message_id $arg_a[9];  /* argument 10: message-id */
  637.         }
  638.         return $hdr;
  639.     }
  640.  
  641.     /**
  642.      * @param string $read 
  643.      * @param integer $i 
  644.      * @return string 
  645.      * @todo document me
  646.      */
  647.     function parseLiteral($read&$i{
  648.         $lit_cnt '';
  649.         ++$i;
  650.         $iPos strpos($read,'}',$i);
  651.         if ($iPos{
  652.             $lit_cnt substr($read$i$iPos $i);
  653.             $i += strlen($lit_cnt3/* skip } + \r + \n */
  654.             /* Now read the literal */
  655.             $s ($lit_cnt substr($read,$i,$lit_cnt)'');
  656.             $i += $lit_cnt;
  657.             /* temp bugfix (SM 1.5 will have a working clean version)
  658.                too much work to implement that version right now */
  659.             --$i;
  660.         else /* should never happen */
  661.             $i += 3/* } + \r + \n */
  662.             $s '';
  663.         }
  664.         return $s;
  665.     }
  666.  
  667.     /**
  668.      * function parseQuote
  669.      *
  670.      * This extract the string value from a quoted string. After the end-quote
  671.      * character is found it returns the string. The offset $i when calling
  672.      * this function points to the first double quote. At the end it points to
  673.      * The ending quote. This function takes care of escaped double quotes.
  674.      * "some \"string\""
  675.      * ^               ^
  676.      * initial $i      end position $i
  677.      *
  678.      * @param string $read 
  679.      * @param integer $i offset in $read
  680.      * @return string string inbetween the double quotes
  681.      * @author Marc Groot Koerkamp
  682.      */
  683.     function parseQuote($read&$i{
  684.         $s '';
  685.         $iPos = ++$i;
  686.         $iPosStart $iPos;
  687.         while (true{
  688.             $iPos strpos($read,'"',$iPos);
  689.             if (!$iPosbreak;
  690.             if ($iPos && $read{$iPos -1!= '\\'{
  691.                 $s substr($read,$i,($iPos-$i));
  692.                 $i $iPos;
  693.                 break;
  694.             else if ($iPos && $read{$iPos -1== '\\' && $read{$iPos-2== '\\'{
  695.                 // This is an unique situation where the fast detection of the string
  696.                 // fails. If the quote string ends with \\ then we need to iterate
  697.                 // through the entire string to make sure we detect the unexcaped
  698.                 // double quotes correctly.
  699.                 $s '';
  700.                 $bEscaped false;
  701.                 $k 0;
  702.                  for ($j=$iPosStart,$iCnt=strlen($read);$j<$iCnt;++$j{
  703.                     $cChar $read{$j};
  704.                     switch ($cChar{
  705.                         case '\\':
  706.                            $bEscaped !$bEscaped;
  707.                             $s .= $cChar;
  708.                             break;
  709.                          case '"':
  710.                             if ($bEscaped{
  711.                                 $s .= $cChar;
  712.                                 $bEscaped false;
  713.                             else {
  714.                                 $i $j;
  715.                                 break 3;
  716.                             }
  717.                             break;
  718.                          default:
  719.                             if ($bEscaped{
  720.                                $bEscaped false;
  721.                             }
  722.                             $s .= $cChar;
  723.                             break;
  724.                     }
  725.                 }
  726.             }
  727.             ++$iPos;
  728.             if ($iPos strlen($read)) {
  729.                 break;
  730.             }
  731.         }
  732.         return $s;
  733.     }
  734.  
  735.     /**
  736.      * @param string $read 
  737.      * @param integer $i 
  738.      * @return object AddressStructure object
  739.      */
  740.     function parseAddress($read&$i{
  741.         $arg_a array();
  742.         for ($read{$i!= ')'++$i{
  743.             $char strtoupper($read{$i});
  744.             switch ($char{
  745.                 case '"'$arg_a[$this->parseQuote($read$i)break;
  746.                 case '{'$arg_a[$this->parseLiteral($read$i)break;
  747.                 case 'n':
  748.                 case 'N':
  749.                     if (strtoupper(substr($read$i3)) == 'NIL'{
  750.                         $arg_a['';
  751.                         $i += 2;
  752.                     }
  753.                     break;
  754.                 defaultbreak;
  755.             }
  756.         }
  757.  
  758.         if (count($arg_a== 4{
  759.             $adr new AddressStructure();
  760.             $adr->personal $arg_a[0];
  761.             $adr->adl $arg_a[1];
  762.             $adr->mailbox $arg_a[2];
  763.             $adr->host $arg_a[3];
  764.         else {
  765.             $adr '';
  766.         }
  767.         return $adr;
  768.     }
  769.  
  770.     /**
  771.      * @param string $read 
  772.      * @param integer $i 
  773.      * @param object Disposition object or empty string
  774.      */
  775.     function parseDisposition($read&$i{
  776.         $arg_a array();
  777.         for ($read{$i!= ')'++$i{
  778.             switch ($read{$i}{
  779.                 case '"'$arg_a[$this->parseQuote($read$i)break;
  780.                 case '{'$arg_a[$this->parseLiteral($read$i)break;
  781.                 case '('$arg_a[$this->parseProperties($read$i)break;
  782.                 defaultbreak;
  783.             }
  784.         }
  785.  
  786.         if (isset($arg_a[0])) {
  787.             $disp new Disposition($arg_a[0]);
  788.             if (isset($arg_a[1])) {
  789.                 $disp->properties $arg_a[1];
  790.             }
  791.         }
  792.         return (is_object($disp$disp '');
  793.     }
  794.  
  795.     /**
  796.      * @param string $read 
  797.      * @param integer $i 
  798.      * @return object Language object or empty string
  799.      */
  800.     function parseLanguage($read&$i{
  801.         /* no idea how to process this one without examples */
  802.         $arg_a array();
  803.  
  804.         for ($read{$i!= ')'++$i{
  805.             switch ($read{$i}{
  806.                 case '"'$arg_a[$this->parseQuote($read$i)break;
  807.                 case '{'$arg_a[$this->parseLiteral($read$i)break;
  808.                 case '('$arg_a[$this->parseProperties($read$i)break;
  809.                 defaultbreak;
  810.             }
  811.         }
  812.  
  813.         if (isset($arg_a[0])) {
  814.             $lang new Language($arg_a[0]);
  815.             if (isset($arg_a[1])) {
  816.                 $lang->properties $arg_a[1];
  817.             }
  818.         }
  819.         return (is_object($lang$lang '');
  820.     }
  821.  
  822.     /**
  823.      * Parse message text enclosed in parenthesis
  824.      * @param string $read 
  825.      * @param integer $i 
  826.      * @return integer 
  827.      */
  828.     function parseParenthesis($read$i{
  829.         for ($i++$read{$i!= ')'++$i{
  830.             switch ($read{$i}{
  831.                 case '"'$this->parseQuote($read$i)break;
  832.                 case '{'$this->parseLiteral($read$i)break;
  833.                 case '('$this->parseProperties($read$i)break;
  834.                 defaultbreak;
  835.             }
  836.         }
  837.         return $i;
  838.     }
  839.  
  840.     /**
  841.      * Function to fill the message structure in case the
  842.      * bodystructure is not available
  843.      * NOT FINISHED YET
  844.      * @param string $read 
  845.      * @param string $type0 message part type
  846.      * @param string $type1 message part subtype
  847.      * @return string (only when type0 is not message or multipart)
  848.      */
  849.     function parseMessage($read$type0$type1{
  850.         switch ($type0{
  851.             case 'message':
  852.                 $rfc822_header true;
  853.                 $mime_header false;
  854.                 break;
  855.             case 'multipart':
  856.                 $rfc822_header false;
  857.                 $mime_header true;
  858.                 break;
  859.             defaultreturn $read;
  860.         }
  861.  
  862.         for ($i 1$i $count++$i{
  863.             $line trim($body[$i]);
  864.             if (($mime_header || $rfc822_header&&
  865.                 (preg_match("/^.*boundary=\"?(.+(?=\")|.+).*/i"$line$reg))) {
  866.                 $bnd $reg[1];
  867.                 $bndreg $bnd;
  868.                 $bndreg str_replace("\\""\\\\"$bndreg);
  869.                 $bndreg str_replace("?""\\?"$bndreg);
  870.                 $bndreg str_replace("+""\\+"$bndreg);
  871.                 $bndreg str_replace(".""\\."$bndreg);
  872.                 $bndreg str_replace("/""\\/"$bndreg);
  873.                 $bndreg str_replace("-""\\-"$bndreg);
  874.                 $bndreg str_replace("(""\\("$bndreg);
  875.                 $bndreg str_replace(")""\\)"$bndreg);
  876.             else if ($rfc822_header && $line == ''{
  877.                 $rfc822_header false;
  878.                 if ($msg->type0 == 'multipart'{
  879.                     $mime_header true;
  880.                 }
  881.             }
  882.  
  883.             if ((($line{0== '-'|| $rfc822_header)  && isset($boundaries[0])) {
  884.                 $cnt count($boundaries)-1;
  885.                 $bnd $boundaries[$cnt]['bnd'];
  886.                 $bndreg $boundaries[$cnt]['bndreg'];
  887.  
  888.                 $regstr '/^--'."($bndreg)".".*".'/';
  889.                 if (preg_match($regstr$line$reg)) {
  890.                     $bndlen strlen($reg[1]);
  891.                     $bndend false;
  892.                     if (strlen($line($bndlen 3)) {
  893.                         if (($line{$bndlen+2== '-'&& ($line{$bndlen+3== '-')) {
  894.                             $bndend true;
  895.                         }
  896.                     }
  897.                     if ($bndend{
  898.                         /* calc offset and return $msg */
  899.                         //$entStr = CalcEntity("$entStr", -1);
  900.                         array_pop($boundaries);
  901.                         $mime_header true;
  902.                         $bnd_end true;
  903.                     else {
  904.                         $mime_header true;
  905.                          $bnd_end false;
  906.                         //$entStr = CalcEntity("$entStr", 0);
  907.                         ++$content_indx;
  908.                     }
  909.                 else {
  910.                     if ($header}
  911.                 }
  912.             }
  913.         }
  914.     }
  915.  
  916.     /**
  917.      * @param array $entity 
  918.      * @param array $alt_order 
  919.      * @param boolean $strict 
  920.      * @return array 
  921.      */
  922.     function findDisplayEntity($entity array()$alt_order array('text/plain''text/html')$strict=false{
  923.         $found false;
  924.         if ($this->type0 == 'multipart'{
  925.             if($this->type1 == 'alternative'{
  926.                 $msg $this->findAlternativeEntity($alt_order);
  927.                 if is_null($msg) ) {
  928.                     if (count($msg->entities== 0{
  929.                         $entity[$msg->entity_id;
  930.                     else {
  931.                         $entity $msg->findDisplayEntity($entity$alt_order$strict);
  932.                     }
  933.                     $found true;
  934.                 }
  935.             else if ($this->type1 == 'related'/* RFC 2387 */
  936.                 $msgs $this->findRelatedEntity();
  937.                 foreach ($msgs as $msg{
  938.                     if (count($msg->entities== 0{
  939.                         $entity[$msg->entity_id;
  940.                     else {
  941.                         $entity $msg->findDisplayEntity($entity$alt_order$strict);
  942.                     }
  943.                 }
  944.                 if (count($msgs0{
  945.                     $found true;
  946.                 }
  947.             else /* Treat as multipart/mixed */
  948.                 foreach ($this->entities as $ent{
  949.                     if(!(is_object($ent->header->disposition&& strtolower($ent->header->disposition->name== 'attachment'&&
  950.                             (!isset($ent->header->parameters['filename'])) &&
  951.                             (!isset($ent->header->parameters['name'])) &&
  952.                             (($ent->type0 != 'message'&& ($ent->type1 != 'rfc822'))) {
  953.                         $entity $ent->findDisplayEntity($entity$alt_order$strict);
  954.                         $found true;
  955.                     }
  956.                 }
  957.             }
  958.         else /* If not multipart, then just compare with each entry from $alt_order */
  959.             $type $this->type0.'/'.$this->type1;
  960. //        $alt_order[] = "message/rfc822";
  961.             foreach ($alt_order as $alt{
  962.                 if( ($alt == $type&& isset($this->entity_id) ) {
  963.                     if ((count($this->entities== 0&&
  964.                             (!isset($this->header->parameters['filename'])) &&
  965.                             (!isset($this->header->parameters['name'])) &&
  966.                             (isset($this->header->disposition&& is_object($this->header->disposition&&
  967.                              strtolower($this->header->disposition->name!= 'attachment')) {
  968.                         $entity[$this->entity_id;
  969.                         $found true;
  970.                     }
  971.                 }
  972.             }
  973.         }
  974.         if(!$found{
  975.             foreach ($this->entities as $ent{
  976.                 if(!(is_object($ent->header->disposition&& strtolower($ent->header->disposition->name== 'attachment'&&
  977.                    (($ent->type0 != 'message'&& ($ent->type1 != 'rfc822'))) {
  978.                     $entity $ent->findDisplayEntity($entity$alt_order$strict);
  979.                     $found true;
  980.                 }
  981.             }
  982.         }
  983.         if(!$strict && !$found{
  984.             if (($this->type0 == 'text'&&
  985.                 in_array($this->type1array('plain''html''message')) &&
  986.                 isset($this->entity_id)) {
  987.                 if (count($this->entities== 0{
  988.                     if (!is_object($this->header->disposition|| strtolower($this->header->disposition->name!= 'attachment'{
  989.                         $entity[$this->entity_id;
  990.                     }
  991.                 }
  992.             }
  993.         }
  994.         return $entity;
  995.     }
  996.  
  997.     /**
  998.      * @param array $alt_order 
  999.      * @return entity 
  1000.      */
  1001.     function findAlternativeEntity($alt_order{
  1002.         /* If we are dealing with alternative parts then we  */
  1003.         /* choose the best viewable message supported by SM. */
  1004.         $best_view 0;
  1005.         $entity null;
  1006.         foreach($this->entities as $ent{
  1007.             $type $ent->header->type0 '/' $ent->header->type1;
  1008.             if ($type == 'multipart/related'{
  1009.                 $type $ent->header->getParameter('type');
  1010.                 // Mozilla bug. Mozilla does not provide the parameter type.
  1011.                 if (!$type$type 'text/html';
  1012.             }
  1013.             $altCount count($alt_order);
  1014.             for ($j $best_view$j $altCount++$j{
  1015.                 if (($alt_order[$j== $type&& ($j >= $best_view)) {
  1016.                     $best_view $j;
  1017.                     $entity $ent;
  1018.                 }
  1019.             }
  1020.         }
  1021.         return $entity;
  1022.     }
  1023.  
  1024.     /**
  1025.      * @return array 
  1026.      */
  1027.     function findRelatedEntity({
  1028.         $msgs array();
  1029.         $related_type $this->header->getParameter('type');
  1030.         // Mozilla bug. Mozilla does not provide the parameter type.
  1031.         if (!$related_type$related_type 'text/html';
  1032.         $entCount count($this->entities);
  1033.         for ($i 0$i $entCount++$i{
  1034.             $type $this->entities[$i]->header->type0.'/'.$this->entities[$i]->header->type1;
  1035.             if ($related_type == $type{
  1036.                 $msgs[$this->entities[$i];
  1037.             }
  1038.         }
  1039.         return $msgs;
  1040.     }
  1041.  
  1042.     /**
  1043.      * @param array $exclude_id 
  1044.      * @param array $result 
  1045.      * @return array 
  1046.      */
  1047.     function getAttachments($exclude_id=array()$result array()) {
  1048. /*
  1049.         if (($this->type0 == 'message') &&
  1050.         ($this->type1 == 'rfc822') &&
  1051.         ($this->entity_id) ) {
  1052.             $this = $this->entities[0];
  1053.         }
  1054. */
  1055.         if (count($this->entities)) {
  1056.             foreach ($this->entities as $entity{
  1057.                 $exclude false;
  1058.                 foreach ($exclude_id as $excl{
  1059.                     if ($entity->entity_id === $excl{
  1060.                         $exclude true;
  1061.                     }
  1062.                 }
  1063.  
  1064.                 if (!$exclude{
  1065.                     if ($entity->type0 == 'multipart'{
  1066.                         $result $entity->getAttachments($exclude_id$result);
  1067.                     else if ($entity->type0 != 'multipart'{
  1068.                         $result[$entity;
  1069.                     }
  1070.                 }
  1071.             }
  1072.         else {
  1073.             $exclude false;
  1074.             foreach ($exclude_id as $excl{
  1075.                 $exclude $exclude || ($this->entity_id == $excl);
  1076.             }
  1077.  
  1078.             if (!$exclude{
  1079.                 $result[$this;
  1080.             }
  1081.         }
  1082.         return $result;
  1083.     }
  1084.  
  1085.     /**
  1086.      * Add attachment to message object
  1087.      * @param string $type attachment type
  1088.      * @param string $name attachment name
  1089.      * @param string $location path to attachment
  1090.      */
  1091.     function initAttachment($type$name$location{
  1092.         $attachment new Message();
  1093.         $mime_header new MessageHeader();
  1094.         $mime_header->setParameter('name'$name);
  1095.         $pos strpos($type'/');
  1096.         if ($pos 0{
  1097.             $mime_header->type0 substr($type0$pos);
  1098.             $mime_header->type1 substr($type$pos+1);
  1099.         else {
  1100.             $mime_header->type0 $type;
  1101.         }
  1102.         $attachment->att_local_name $location;
  1103.         $disposition new Disposition('attachment');
  1104.         $disposition->properties['filename'$name;
  1105.         $mime_header->disposition $disposition;
  1106.         $attachment->mime_header $mime_header;
  1107.         $this->entities[]=$attachment;
  1108.     }
  1109.  
  1110.     /**
  1111.      * Delete all attachments from this object from disk.
  1112.      * @since 1.4.6
  1113.      */
  1114.     function purgeAttachments({
  1115.         if ($this->att_local_name{
  1116.             global $username$attachment_dir;
  1117.             $hashed_attachment_dir getHashedDir($username$attachment_dir);
  1118.             if file_exists($hashed_attachment_dir '/' $this->att_local_name) ) {
  1119.                 unlink($hashed_attachment_dir '/' $this->att_local_name);
  1120.             }
  1121.         }
  1122.         // recursively delete attachments from entities contained in this object
  1123.         for ($i=0$entCount=count($this->entities);$i$entCount++$i{
  1124.             $this->entities[$i]->purgeAttachments();
  1125.         }
  1126.     }
  1127. }

Documentation generated on Mon, 13 Jan 2020 04:24:56 +0100 by phpDocumentor 1.4.3