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 14845 2020-01-07 08:09:34Z 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.      * MessageHeader object
  35.      * @var object 
  36.      */
  37.     var $mime_header = '';
  38.     /**
  39.      * @var mixed 
  40.      */
  41.     var $flags = '';
  42.     /**
  43.      * Media type
  44.      * @var string 
  45.      */
  46.     var $type0='';
  47.     /**
  48.      * Media subtype
  49.      * @var string 
  50.      */
  51.     var $type1='';
  52.     /**
  53.      * Nested mime parts
  54.      * @var array 
  55.      */
  56.     var $entities = array();
  57.     /**
  58.      * Message part id
  59.      * @var string 
  60.      */
  61.     var $entity_id = '';
  62.     /**
  63.      * Parent message part id
  64.      * @var string 
  65.      */
  66.     var $parent_ent;
  67.     /**
  68.      * @var mixed 
  69.      */
  70.     var $entity;
  71.     /**
  72.      * @var mixed 
  73.      */
  74.     var $parent = '';
  75.     /**
  76.      * @var string 
  77.      */
  78.     var $decoded_body='';
  79.     /**
  80.      * Message \seen status
  81.      * @var boolean 
  82.      */
  83.     var $is_seen = 0;
  84.     /**
  85.      * Message \answered status
  86.      * @var boolean 
  87.      */
  88.     var $is_answered = 0;
  89.     /**
  90.      * Message forward status
  91.      * @var boolean 
  92.      */
  93.     var $is_forwarded = 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 $this->handleRfc2231($properties);
  549.     }
  550.  
  551.     /**
  552.      * Joins RFC-2231 continuations, converts encoding to RFC-2047 style
  553.      * @param array $properties 
  554.      * @return array 
  555.      */
  556.     function handleRfc2231($properties{
  557.  
  558.         /* STAGE 1: look for multi-line parameters, convert to the single line
  559.            form, and normalize values */
  560.  
  561.         $cont array();
  562.         foreach($properties as $key=>$value{
  563.             /* Look for parameters followed by "*", a number, and an optional "*"
  564.                at the end. */
  565.             if (preg_match('/^(.*\*)(\d+)(|\*)$/'$key$matches)) {
  566.                 unset($properties[$key]);
  567.                 $prop_name $matches[1];
  568.                 if (!isset($cont[$prop_name])) $cont[$prop_namearray();
  569.  
  570.                 /* An asterisk at the end of parameter name indicates that there
  571.                    may be an encoding information present, and the parameter
  572.                    value is percent-hex encoded. If parameter is not encoded, we
  573.                    encode it to simplify further processing.
  574.                 */
  575.                 if ($matches[3== ''$value rawurlencode($value);
  576.                 /* Use the number from parameter name as segment index */
  577.                 $cont[$prop_name][$matches[2]] $value;
  578.             }
  579.         }
  580.         foreach($cont as $key=>$values{
  581.             /* Sort segments of multi-line parameters by index number. */
  582.             ksort($values);
  583.             /* Join segments. We can do it safely, because:
  584.                - All segments are encoded.
  585.                - Per RFC-2231, chapter 4.1 notes.
  586.             */
  587.             $value implode(''$values);
  588.             /* Add language and character set field delimiters if not present,
  589.                as required per RFC-2231, chapter 4.1, note #5. */
  590.             if (strpos($value"'"=== false$value "''".$value;
  591.             $properties[$key$value;
  592.         }
  593.  
  594.         /* STAGE 2: Convert single line RFC-2231 encoded parameters, and
  595.            previously converted multi-line parameters, to RFC-2047 encoding */
  596.  
  597.         foreach($properties as $key=>$value{
  598.             if ($idx strpos($key'*')) {
  599.                 unset($properties[$key]);
  600.                 /* Extract the charset & language. */
  601.                 $charset substr($value,0,strpos($value,"'"));
  602.                 $value substr($value,strlen($charset)+1);
  603.                 $language substr($value,0,strpos($value,"'"));
  604.                 $value substr($value,strlen($language)+1);
  605.                 /* No character set defaults to US-ASCII */
  606.                 if (!$charset$charset 'US-ASCII';
  607.                 if ($language$language '*'.$language;
  608.                 /* Convert to RFC-2047 base64 encoded string. */
  609.                 $properties[substr($key0$idx)'=?'.$charset.$language.'?B?'.base64_encode(rawurldecode($value)).'?=';
  610.             }
  611.         }
  612.         return $properties;
  613.     }
  614.  
  615.     /**
  616.      * @param string $read 
  617.      * @param integer $i 
  618.      * @param object $hdr MessageHeader object
  619.      * @return object MessageHeader object
  620.      */
  621.     function parseEnvelope($read&$i$hdr{
  622.         $arg_no 0;
  623.         $arg_a array();
  624.         ++$i;
  625.         for ($cnt strlen($read)($i $cnt&& ($read{$i!= ')')++$i{
  626.             $char strtoupper($read{$i});
  627.             switch ($char{
  628.                 case '"':
  629.                     $arg_a[$this->parseQuote($read$i);
  630.                     ++$arg_no;
  631.                     break;
  632.                 case '{':
  633.                     $arg_a[$this->parseLiteral($read$i);
  634.             /* temp bugfix (SM 1.5 will have a working clean version)
  635.                too much work to implement that version right now */
  636. //            --$i;
  637.                     ++$arg_no;
  638.                     break;
  639.                 case 'N':
  640.                     /* probably NIL argument */
  641.                     if (strtoupper(substr($read$i3)) == 'NIL'{
  642.                         $arg_a['';
  643.                         ++$arg_no;
  644.                         $i += 2;
  645.                     }
  646.                     break;
  647.                 case '(':
  648.                     /* Address structure (with group support)
  649.                      * Note: Group support is useless on SMTP connections
  650.                      *       because the protocol doesn't support it
  651.                      */
  652.                     $addr_a array();
  653.                     $group '';
  654.                     $a=0;
  655.                     for ($i $cnt && $read{$i!= ')'++$i{
  656.                         if ($read{$i== '('{
  657.                             $addr $this->parseAddress($read$i);
  658.                             if (($addr->host == ''&& ($addr->mailbox != '')) {
  659.                                 /* start of group */
  660.                                 $group $addr->mailbox;
  661.                                 $group_addr $addr;
  662.                                 $j $a;
  663.                             else if ($group && ($addr->host == ''&& ($addr->mailbox == '')) {
  664.                                /* end group */
  665.                                 if ($a == ($j+1)) /* no group members */
  666.                                     $group_addr->group $group;
  667.                                     $group_addr->mailbox '';
  668.                                     $group_addr->personal "$group: Undisclosed recipients;";
  669.                                     $addr_a[$group_addr;
  670.                                     $group ='';
  671.                                 }
  672.                             else {
  673.                                 $addr->group $group;
  674.                                 $addr_a[$addr;
  675.                             }
  676.                             ++$a;
  677.                         }
  678.                     }
  679.                     $arg_a[$addr_a;
  680.                     break;
  681.                 defaultbreak;
  682.             }
  683.         }
  684.  
  685.         if (count($arg_a9{
  686.             $d strtr($arg_a[0]array('  ' => ' '));
  687.             $d_parts explode(' '$d);
  688.             if (!$arg_a[1]$arg_a[1_("(no subject)");
  689.  
  690.             $hdr->date getTimeStamp($d_parts)/* argument 1: date */
  691.             $hdr->date_unparsed strtr($d,'<>','  ')/* original date */
  692.             $hdr->subject $arg_a[1];     /* argument 2: subject */
  693.             $hdr->from is_array($arg_a[2]$arg_a[2][0'';     /* argument 3: from        */
  694.             $hdr->sender is_array($arg_a[3]$arg_a[3][0'';   /* argument 4: sender      */
  695.             $hdr->reply_to is_array($arg_a[4]$arg_a[4][0'';  /* argument 5: reply-to    */
  696.             $hdr->to $arg_a[5];          /* argument 6: to          */
  697.             $hdr->cc $arg_a[6];          /* argument 7: cc          */
  698.             $hdr->bcc $arg_a[7];         /* argument 8: bcc         */
  699.             $hdr->in_reply_to $arg_a[8];   /* argument 9: in-reply-to */
  700.             $hdr->message_id $arg_a[9];  /* argument 10: message-id */
  701.         }
  702.         return $hdr;
  703.     }
  704.  
  705.     /**
  706.      * @param string $read 
  707.      * @param integer $i 
  708.      * @return string 
  709.      * @todo document me
  710.      */
  711.     function parseLiteral($read&$i{
  712.         $lit_cnt '';
  713.         ++$i;
  714.         $iPos strpos($read,'}',$i);
  715.         if ($iPos{
  716.             $lit_cnt substr($read$i$iPos $i);
  717.             $i += strlen($lit_cnt3/* skip } + \r + \n */
  718.             /* Now read the literal */
  719.             $s ($lit_cnt substr($read,$i,$lit_cnt)'');
  720.             $i += $lit_cnt;
  721.             /* temp bugfix (SM 1.5 will have a working clean version)
  722.                too much work to implement that version right now */
  723.             --$i;
  724.         else /* should never happen */
  725.             $i += 3/* } + \r + \n */
  726.             $s '';
  727.         }
  728.         return $s;
  729.     }
  730.  
  731.     /**
  732.      * function parseQuote
  733.      *
  734.      * This extract the string value from a quoted string. After the end-quote
  735.      * character is found it returns the string. The offset $i when calling
  736.      * this function points to the first double quote. At the end it points to
  737.      * The ending quote. This function takes care of escaped double quotes.
  738.      * "some \"string\""
  739.      * ^               ^
  740.      * initial $i      end position $i
  741.      *
  742.      * @param string $read 
  743.      * @param integer $i offset in $read
  744.      * @return string string inbetween the double quotes
  745.      * @author Marc Groot Koerkamp
  746.      */
  747.     function parseQuote($read&$i{
  748.         $s '';
  749.         $iPos = ++$i;
  750.         $iPosStart $iPos;
  751.         while (true{
  752.             $iPos strpos($read,'"',$iPos);
  753.             if (!$iPosbreak;
  754.             if ($iPos && $read{$iPos -1!= '\\'{
  755.                 $s substr($read,$i,($iPos-$i));
  756.                 $i $iPos;
  757.                 break;
  758.             else if ($iPos && $read{$iPos -1== '\\' && $read{$iPos-2== '\\'{
  759.                 // This is an unique situation where the fast detection of the string
  760.                 // fails. If the quote string ends with \\ then we need to iterate
  761.                 // through the entire string to make sure we detect the unexcaped
  762.                 // double quotes correctly.
  763.                 $s '';
  764.                 $bEscaped false;
  765.                 $k 0;
  766.                  for ($j=$iPosStart,$iCnt=strlen($read);$j<$iCnt;++$j{
  767.                     $cChar $read{$j};
  768.                     switch ($cChar{
  769.                         case '\\':
  770.                            $bEscaped !$bEscaped;
  771.                             $s .= $cChar;
  772.                             break;
  773.                          case '"':
  774.                             if ($bEscaped{
  775.                                 $s .= $cChar;
  776.                                 $bEscaped false;
  777.                             else {
  778.                                 $i $j;
  779.                                 break 3;
  780.                             }
  781.                             break;
  782.                          default:
  783.                             if ($bEscaped{
  784.                                $bEscaped false;
  785.                             }
  786.                             $s .= $cChar;
  787.                             break;
  788.                     }
  789.                 }
  790.             }
  791.             ++$iPos;
  792.             if ($iPos strlen($read)) {
  793.                 break;
  794.             }
  795.         }
  796.         return $s;
  797.     }
  798.  
  799.     /**
  800.      * @param string $read 
  801.      * @param integer $i 
  802.      * @return object AddressStructure object
  803.      */
  804.     function parseAddress($read&$i{
  805.         $arg_a array();
  806.         for ($read{$i!= ')'++$i{
  807.             $char strtoupper($read{$i});
  808.             switch ($char{
  809.                 case '"'$arg_a[$this->parseQuote($read$i)break;
  810.                 case '{'$arg_a[$this->parseLiteral($read$i)break;
  811.                 case 'n':
  812.                 case 'N':
  813.                     if (strtoupper(substr($read$i3)) == 'NIL'{
  814.                         $arg_a['';
  815.                         $i += 2;
  816.                     }
  817.                     break;
  818.                 defaultbreak;
  819.             }
  820.         }
  821.  
  822.         if (count($arg_a== 4{
  823.             $adr new AddressStructure();
  824.             $adr->personal $arg_a[0];
  825.             $adr->adl $arg_a[1];
  826.             $adr->mailbox $arg_a[2];
  827.             $adr->host $arg_a[3];
  828.         else {
  829.             $adr '';
  830.         }
  831.         return $adr;
  832.     }
  833.  
  834.     /**
  835.      * @param string $read 
  836.      * @param integer $i 
  837.      * @param object Disposition object or empty string
  838.      */
  839.     function parseDisposition($read&$i{
  840.         $arg_a array();
  841.         for ($read{$i!= ')'++$i{
  842.             switch ($read{$i}{
  843.                 case '"'$arg_a[$this->parseQuote($read$i)break;
  844.                 case '{'$arg_a[$this->parseLiteral($read$i)break;
  845.                 case '('$arg_a[$this->parseProperties($read$i)break;
  846.                 defaultbreak;
  847.             }
  848.         }
  849.  
  850.         if (isset($arg_a[0])) {
  851.             $disp new Disposition($arg_a[0]);
  852.             if (isset($arg_a[1])) {
  853.                 $disp->properties $arg_a[1];
  854.             }
  855.         }
  856.         return (is_object($disp$disp '');
  857.     }
  858.  
  859.     /**
  860.      * @param string $read 
  861.      * @param integer $i 
  862.      * @return object Language object or empty string
  863.      */
  864.     function parseLanguage($read&$i{
  865.         /* no idea how to process this one without examples */
  866.         $arg_a array();
  867.  
  868.         for ($read{$i!= ')'++$i{
  869.             switch ($read{$i}{
  870.                 case '"'$arg_a[$this->parseQuote($read$i)break;
  871.                 case '{'$arg_a[$this->parseLiteral($read$i)break;
  872.                 case '('$arg_a[$this->parseProperties($read$i)break;
  873.                 defaultbreak;
  874.             }
  875.         }
  876.  
  877.         if (isset($arg_a[0])) {
  878.             $lang new Language($arg_a[0]);
  879.             if (isset($arg_a[1])) {
  880.                 $lang->properties $arg_a[1];
  881.             }
  882.         }
  883.         return (is_object($lang$lang '');
  884.     }
  885.  
  886.     /**
  887.      * Parse message text enclosed in parenthesis
  888.      * @param string $read 
  889.      * @param integer $i 
  890.      * @return integer 
  891.      */
  892.     function parseParenthesis($read$i{
  893.         for ($i++$read{$i!= ')'++$i{
  894.             switch ($read{$i}{
  895.                 case '"'$this->parseQuote($read$i)break;
  896.                 case '{'$this->parseLiteral($read$i)break;
  897.                 case '('$this->parseProperties($read$i)break;
  898.                 defaultbreak;
  899.             }
  900.         }
  901.         return $i;
  902.     }
  903.  
  904.     /**
  905.      * Function to fill the message structure in case the
  906.      * bodystructure is not available
  907.      * NOT FINISHED YET
  908.      * @param string $read 
  909.      * @param string $type0 message part type
  910.      * @param string $type1 message part subtype
  911.      * @return string (only when type0 is not message or multipart)
  912.      */
  913.     function parseMessage($read$type0$type1{
  914.         switch ($type0{
  915.             case 'message':
  916.                 $rfc822_header true;
  917.                 $mime_header false;
  918.                 break;
  919.             case 'multipart':
  920.                 $rfc822_header false;
  921.                 $mime_header true;
  922.                 break;
  923.             defaultreturn $read;
  924.         }
  925.  
  926.         for ($i 1$i $count++$i{
  927.             $line trim($body[$i]);
  928.             if (($mime_header || $rfc822_header&&
  929.                 (preg_match("/^.*boundary=\"?(.+(?=\")|.+).*/i"$line$reg))) {
  930.                 $bnd $reg[1];
  931.                 $bndreg $bnd;
  932.                 $bndreg str_replace("\\""\\\\"$bndreg);
  933.                 $bndreg str_replace("?""\\?"$bndreg);
  934.                 $bndreg str_replace("+""\\+"$bndreg);
  935.                 $bndreg str_replace(".""\\."$bndreg);
  936.                 $bndreg str_replace("/""\\/"$bndreg);
  937.                 $bndreg str_replace("-""\\-"$bndreg);
  938.                 $bndreg str_replace("(""\\("$bndreg);
  939.                 $bndreg str_replace(")""\\)"$bndreg);
  940.             else if ($rfc822_header && $line == ''{
  941.                 $rfc822_header false;
  942.                 if ($msg->type0 == 'multipart'{
  943.                     $mime_header true;
  944.                 }
  945.             }
  946.  
  947.             if ((($line{0== '-'|| $rfc822_header)  && isset($boundaries[0])) {
  948.                 $cnt count($boundaries)-1;
  949.                 $bnd $boundaries[$cnt]['bnd'];
  950.                 $bndreg $boundaries[$cnt]['bndreg'];
  951.  
  952.                 $regstr '/^--'."($bndreg)".".*".'/';
  953.                 if (preg_match($regstr$line$reg)) {
  954.                     $bndlen strlen($reg[1]);
  955.                     $bndend false;
  956.                     if (strlen($line($bndlen 3)) {
  957.                         if (($line{$bndlen+2== '-'&& ($line{$bndlen+3== '-')) {
  958.                             $bndend true;
  959.                         }
  960.                     }
  961.                     if ($bndend{
  962.                         /* calc offset and return $msg */
  963.                         //$entStr = CalcEntity("$entStr", -1);
  964.                         array_pop($boundaries);
  965.                         $mime_header true;
  966.                         $bnd_end true;
  967.                     else {
  968.                         $mime_header true;
  969.                          $bnd_end false;
  970.                         //$entStr = CalcEntity("$entStr", 0);
  971.                         ++$content_indx;
  972.                     }
  973.                 else {
  974.                     if ($header}
  975.                 }
  976.             }
  977.         }
  978.     }
  979.  
  980.     /**
  981.      * @param array $entity 
  982.      * @param array $alt_order 
  983.      * @param boolean $strict 
  984.      * @return array 
  985.      */
  986.     function findDisplayEntity($entity array()$alt_order array('text/plain''text/html')$strict=false{
  987.         $found false;
  988.         if ($this->type0 == 'multipart'{
  989.             if($this->type1 == 'alternative'{
  990.                 $msg $this->findAlternativeEntity($alt_order);
  991.                 if is_null($msg) ) {
  992.                     if (count($msg->entities== 0{
  993.                         $entity[$msg->entity_id;
  994.                     else {
  995.                         $entity $msg->findDisplayEntity($entity$alt_order$strict);
  996.                     }
  997.                     $found true;
  998.                 }
  999.             else if ($this->type1 == 'related'/* RFC 2387 */
  1000.                 $msgs $this->findRelatedEntity();
  1001.                 foreach ($msgs as $msg{
  1002.                     if (count($msg->entities== 0{
  1003.                         $entity[$msg->entity_id;
  1004.                     else {
  1005.                         $entity $msg->findDisplayEntity($entity$alt_order$strict);
  1006.                     }
  1007.                 }
  1008.                 if (count($msgs0{
  1009.                     $found true;
  1010.                 }
  1011.             else /* Treat as multipart/mixed */
  1012.                 foreach ($this->entities as $ent{
  1013.                     if(!(is_object($ent->header->disposition&& strtolower($ent->header->disposition->name== 'attachment'&&
  1014.                             (!isset($ent->header->parameters['filename'])) &&
  1015.                             (!isset($ent->header->parameters['name'])) &&
  1016.                             (($ent->type0 != 'message'&& ($ent->type1 != 'rfc822'))) {
  1017.                         $entity $ent->findDisplayEntity($entity$alt_order$strict);
  1018.                         $found true;
  1019.                     }
  1020.                 }
  1021.             }
  1022.         else /* If not multipart, then just compare with each entry from $alt_order */
  1023.             $type $this->type0.'/'.$this->type1;
  1024. //        $alt_order[] = "message/rfc822";
  1025.             foreach ($alt_order as $alt{
  1026.                 if( ($alt == $type&& isset($this->entity_id) ) {
  1027.                     if ((count($this->entities== 0&&
  1028.                             (!isset($this->header->parameters['filename'])) &&
  1029.                             (!isset($this->header->parameters['name'])) &&
  1030.                             isset($this->header->disposition&& is_object($this->header->disposition&&
  1031.                             !(is_object($this->header->disposition&& strtolower($this->header->disposition->name== 'attachment')) {
  1032.                         $entity[$this->entity_id;
  1033.                         $found true;
  1034.                     }
  1035.                 }
  1036.             }
  1037.         }
  1038.         if(!$found{
  1039.             foreach ($this->entities as $ent{
  1040.                 if(!(is_object($ent->header->disposition&& strtolower($ent->header->disposition->name== 'attachment'&&
  1041.                    (($ent->type0 != 'message'&& ($ent->type1 != 'rfc822'))) {
  1042.                     $entity $ent->findDisplayEntity($entity$alt_order$strict);
  1043.                     $found true;
  1044.                 }
  1045.             }
  1046.         }
  1047.         if(!$strict && !$found{
  1048.             if (($this->type0 == 'text'&&
  1049.                 in_array($this->type1array('plain''html''message')) &&
  1050.                 isset($this->entity_id)) {
  1051.                 if (count($this->entities== 0{
  1052.                     if (!is_object($this->header->disposition|| strtolower($this->header->disposition->name!= 'attachment'{
  1053.                         $entity[$this->entity_id;
  1054.                     }
  1055.                 }
  1056.             }
  1057.         }
  1058.         return $entity;
  1059.     }
  1060.  
  1061.     /**
  1062.      * @param array $alt_order 
  1063.      * @return entity 
  1064.      */
  1065.     function findAlternativeEntity($alt_order{
  1066.         /* If we are dealing with alternative parts then we  */
  1067.         /* choose the best viewable message supported by SM. */
  1068.         $best_view 0;
  1069.         $entity null;
  1070.         foreach($this->entities as $ent{
  1071.             $type $ent->header->type0 '/' $ent->header->type1;
  1072.             if ($type == 'multipart/related'{
  1073.                 $type $ent->header->getParameter('type');
  1074.                 // Mozilla bug. Mozilla does not provide the parameter type.
  1075.                 if (!$type$type 'text/html';
  1076.             }
  1077.             $altCount count($alt_order);
  1078.             for ($j $best_view$j $altCount++$j{
  1079.                 if (($alt_order[$j== $type&& ($j >= $best_view)) {
  1080.                     $best_view $j;
  1081.                     $entity $ent;
  1082.                 }
  1083.             }
  1084.         }
  1085.         return $entity;
  1086.     }
  1087.  
  1088.     /**
  1089.      * @return array 
  1090.      */
  1091.     function findRelatedEntity({
  1092.         $msgs array();
  1093.         $related_type $this->header->getParameter('type');
  1094.         // Mozilla bug. Mozilla does not provide the parameter type.
  1095.         if (!$related_type$related_type 'text/html';
  1096.         $entCount count($this->entities);
  1097.         for ($i 0$i $entCount++$i{
  1098.             $type $this->entities[$i]->header->type0.'/'.$this->entities[$i]->header->type1;
  1099.             if ($related_type == $type{
  1100.                 $msgs[$this->entities[$i];
  1101.             }
  1102.         }
  1103.         return $msgs;
  1104.     }
  1105.  
  1106.     /**
  1107.      * @param array $exclude_id 
  1108.      * @param array $result 
  1109.      * @return array 
  1110.      */
  1111.     function getAttachments($exclude_id=array()$result array()) {
  1112. /*
  1113.         if (($this->type0 == 'message') &&
  1114.         ($this->type1 == 'rfc822') &&
  1115.         ($this->entity_id) ) {
  1116.             $this = $this->entities[0];
  1117.         }
  1118. */
  1119.         if (count($this->entities)) {
  1120.             foreach ($this->entities as $entity{
  1121.                 $exclude false;
  1122.                 foreach ($exclude_id as $excl{
  1123.                     if ($entity->entity_id === $excl{
  1124.                         $exclude true;
  1125.                     }
  1126.                 }
  1127.  
  1128.                 if (!$exclude{
  1129.                     if ($entity->type0 == 'multipart'{
  1130.                         $result $entity->getAttachments($exclude_id$result);
  1131.                     else if ($entity->type0 != 'multipart'{
  1132.                         $result[$entity;
  1133.                     }
  1134.                 }
  1135.             }
  1136.         else {
  1137.             $exclude false;
  1138.             foreach ($exclude_id as $excl{
  1139.                 $exclude $exclude || ($this->entity_id == $excl);
  1140.             }
  1141.  
  1142.             if (!$exclude{
  1143.                 $result[$this;
  1144.             }
  1145.         }
  1146.         return $result;
  1147.     }
  1148.  
  1149.     /**
  1150.      * Add attachment to message object
  1151.      * @param string $type attachment type
  1152.      * @param string $name attachment name
  1153.      * @param string $location path to attachment
  1154.      */
  1155.     function initAttachment($type$name$location{
  1156.         $attachment new Message();
  1157.         $mime_header new MessageHeader();
  1158.         $mime_header->setParameter('name'$name);
  1159.         // FIXME: duplicate code. see ContentType class
  1160.         $pos strpos($type'/');
  1161.         if ($pos 0{
  1162.             $mime_header->type0 substr($type0$pos);
  1163.             $mime_header->type1 substr($type$pos+1);
  1164.         else {
  1165.             $mime_header->type0 $type;
  1166.         }
  1167.         $attachment->att_local_name $location;
  1168.         $disposition new Disposition('attachment');
  1169.         $disposition->properties['filename'$name;
  1170.         $mime_header->disposition $disposition;
  1171.         $attachment->mime_header $mime_header;
  1172.         $this->entities[]=$attachment;
  1173.     }
  1174.  
  1175.     /**
  1176.      * Delete all attachments from this object from disk.
  1177.      * @since 1.5.1
  1178.      */
  1179.     function purgeAttachments({
  1180.         if ($this->att_local_name{
  1181.             global $username$attachment_dir;
  1182.             $hashed_attachment_dir getHashedDir($username$attachment_dir);
  1183.             if file_exists($hashed_attachment_dir '/' $this->att_local_name) ) {
  1184.                 unlink($hashed_attachment_dir '/' $this->att_local_name);
  1185.             }
  1186.         }
  1187.         // recursively delete attachments from entities contained in this object
  1188.         for ($i=0$entCount=count($this->entities);$i$entCount++$i{
  1189.             $this->entities[$i]->purgeAttachments();
  1190.         }
  1191.     }
  1192. }

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