Source for file Rfc822Header.class.php

Documentation is available at Rfc822Header.class.php

  1. <?php
  2.  
  3. /**
  4.  * Rfc822Header.class.php
  5.  *
  6.  * This file contains functions needed to handle headers in 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: Rfc822Header.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.  * MIME header class
  18.  * input: header_string or array
  19.  * You must call parseHeader() function after creating object in order to fill object's
  20.  * parameters.
  21.  * @todo FIXME: there is no constructor function and class should ignore all input args.
  22.  * @package squirrelmail
  23.  * @subpackage mime
  24.  * @since 1.3.0
  25.  */
  26. class Rfc822Header {
  27.     /**
  28.      * All headers, unparsed
  29.      * @var array 
  30.      */
  31.     var $raw_headers = array();
  32.     /**
  33.      * Date header
  34.      * @var mixed 
  35.      */
  36.     var $date = -1;
  37.     /**
  38.      * Original date header as fallback for unparsable dates
  39.      * @var mixed 
  40.      */
  41.     var $date_unparsed = '';
  42.     /**
  43.      * Subject header
  44.      * @var string 
  45.      */
  46.     var $subject = '';
  47.     /**
  48.      * From header
  49.      * @var array 
  50.      */
  51.     var $from = array();
  52.     /**
  53.      * @var mixed 
  54.      */
  55.     var $sender = '';
  56.     /**
  57.      * Reply-To header
  58.      * @var array 
  59.      */
  60.     var $reply_to = array();
  61.     /**
  62.      * Mail-Followup-To header
  63.      * @var array 
  64.      */
  65.     var $mail_followup_to = array();
  66.     /**
  67.      * To header
  68.      * @var array 
  69.      */
  70.     var $to = array();
  71.     /**
  72.      * Cc header
  73.      * @var array 
  74.      */
  75.     var $cc = array();
  76.     /**
  77.      * Bcc header
  78.      * @var array 
  79.      */
  80.     var $bcc = array();
  81.     /**
  82.      * In-reply-to header
  83.      * @var string 
  84.      */
  85.     var $in_reply_to = '';
  86.     /**
  87.      * Message-ID header
  88.      * @var string 
  89.      */
  90.     var $message_id = '';
  91.     /**
  92.      * References header
  93.      * @var string 
  94.      */
  95.     var $references = '';
  96.     /**
  97.      * @var mixed 
  98.      */
  99.     var $mime = false;
  100.     /**
  101.      * Content Type object
  102.      * @var object 
  103.      */
  104.     var $content_type = '';
  105.     /**
  106.      * @var mixed 
  107.      */
  108.     var $disposition = '';
  109.     /**
  110.      * X-Mailer header
  111.      * @var string 
  112.      */
  113.     var $xmailer = '';
  114.     /**
  115.      * Priority header
  116.      * @var integer 
  117.      */
  118.     var $priority = 3;
  119.     /**
  120.      * Disposition notification for requesting message delivery notification (MDN)
  121.      * @var mixed 
  122.      */
  123.     var $dnt = '';
  124.     /**
  125.      * Delivery notification (DR)
  126.      * @var mixed 
  127.      */
  128.     var $drnt = '';
  129.     /**
  130.      * @var mixed 
  131.      */
  132.     var $encoding = '';
  133.     /**
  134.      * @var mixed 
  135.      */
  136.     var $content_id = '';
  137.     /**
  138.      * @var mixed 
  139.      */
  140.     var $content_desc = '';
  141.     /**
  142.      * @var mixed 
  143.      */
  144.     var $mlist = array();
  145.     /**
  146.      * SpamAssassin 'x-spam-status' header
  147.      * @var mixed 
  148.      */
  149.     var $x_spam_status = array();
  150.     /**
  151.      * Extra header
  152.      * only needed for constructing headers in delivery class
  153.      * @var array 
  154.      */
  155.     var $more_headers = array();
  156.  
  157.     /**
  158.      * @param mixed $hdr string or array with message headers
  159.      */
  160.     function parseHeader($hdr{
  161.         if (is_array($hdr)) {
  162.             $hdr implode(''$hdr);
  163.         }
  164.         /* First we replace \r\n by \n and unfold the header */
  165.         /* FIXME: unfolding header with multiple spaces "\n( +)" */
  166.         $hdr trim(str_replace(array("\r\n""\n\t""\n "),array("\n"' '' ')$hdr));
  167.  
  168.         /* Now we can make a new header array with */
  169.         /* each element representing a headerline  */
  170.         $hdr explode("\n" $hdr);
  171.         foreach ($hdr as $line{
  172.             $pos strpos($line':');
  173.             if ($pos 0{
  174.                 $this->raw_headers[$line;
  175.                 $field substr($line0$pos);
  176.                 if (!strstr($field,' ')) /* valid field */
  177.                         $value trim(substr($line$pos+1));
  178.                         $this->parseField($field$value);
  179.                 }
  180.             }
  181.         }
  182.         if (!is_object($this->content_type)) {
  183.             $this->parseContentType('text/plain; charset=us-ascii');
  184.         }
  185.     }
  186.  
  187.     /**
  188.      * @param string $value 
  189.      * @return string 
  190.      */
  191.     function stripComments($value{
  192.         $result '';
  193.         $cnt strlen($value);
  194.         for ($i 0$i $cnt++$i{
  195.             switch ($value{$i}{
  196.                 case '"':
  197.                     $result .= '"';
  198.                     while ((++$i $cnt&& ($value{$i!= '"')) {
  199.                         if ($value{$i== '\\'{
  200.                             $result .= '\\';
  201.                             ++$i;
  202.                         }
  203.                         $result .= $value{$i};
  204.                     }
  205.                     if($i $cnt{
  206.                         $result .= $value{$i};
  207.                     }
  208.                     break;
  209.                 case '(':
  210.                     $depth 1;
  211.                     while (($depth 0&& (++$i $cnt)) {
  212.                         switch($value{$i}{
  213.                             case '\\':
  214.                                 ++$i;
  215.                                 break;
  216.                             case '(':
  217.                                 ++$depth;
  218.                                 break;
  219.                             case ')':
  220.                                 --$depth;
  221.                                 break;
  222.                             default:
  223.                                 break;
  224.                         }
  225.                     }
  226.                     break;
  227.                 default:
  228.                     $result .= $value{$i};
  229.                     break;
  230.             }
  231.         }
  232.         return $result;
  233.     }
  234.  
  235.     /**
  236.      * Parse header field according to field type
  237.      * @param string $field field name
  238.      * @param string $value field value
  239.      */
  240.     function parseField($field$value{
  241.         $field strtolower($field);
  242.         switch($field{
  243.             case 'date':
  244.                 $value $this->stripComments($value);
  245.                 $d strtr($valuearray('  ' => ' '));
  246.                 $d explode(' '$d);
  247.                 $this->date = getTimeStamp($d);
  248.                 $this->date_unparsed = strtr($value,'<>','  ');
  249.                 break;
  250.             case 'subject':
  251.                 $this->subject = $value;
  252.                 break;
  253.             case 'from':
  254.                 $this->from = $this->parseAddress($value,true);
  255.                 break;
  256.             case 'sender':
  257.                 $this->sender = $this->parseAddress($value);
  258.                 break;
  259.             case 'reply-to':
  260.                 $this->reply_to = $this->parseAddress($valuetrue);
  261.                 break;
  262.             case 'mail-followup-to':
  263.                 $this->mail_followup_to = $this->parseAddress($valuetrue);
  264.                 break;
  265.             case 'to':
  266.                 $this->to = $this->parseAddress($valuetrue);
  267.                 break;
  268.             case 'cc':
  269.                 $this->cc = $this->parseAddress($valuetrue);
  270.                 break;
  271.             case 'bcc':
  272.                 $this->bcc = $this->parseAddress($valuetrue);
  273.                 break;
  274.             case 'in-reply-to':
  275.                 $this->in_reply_to = $value;
  276.                 break;
  277.             case 'message-id':
  278.                 $value $this->stripComments($value);
  279.                 $this->message_id = $value;
  280.                 break;
  281.             case 'references':
  282.                 $value $this->stripComments($value);
  283.                 $this->references = $value;
  284.                 break;
  285.             case 'x-confirm-reading-to':
  286.             case 'disposition-notification-to':
  287.                 $value $this->stripComments($value);
  288.                 $this->dnt = $this->parseAddress($value);
  289.                 break;
  290.             case 'return-receipt-to':
  291.                 $value $this->stripComments($value);
  292.                 $this->drnt = $this->parseAddress($value);
  293.                 break;
  294.             case 'mime-version':
  295.                 $value $this->stripComments($value);
  296.                 $value str_replace(' '''$value);
  297.                 $this->mime = ($value == '1.0' true $this->mime);
  298.                 break;
  299.             case 'content-type':
  300.                 $value $this->stripComments($value);
  301.                 $this->parseContentType($value);
  302.                 break;
  303.             case 'content-disposition':
  304.                 $value $this->stripComments($value);
  305.                 $this->parseDisposition($value);
  306.                 break;
  307.             case 'content-transfer-encoding':
  308.                 $this->encoding = $value;
  309.                 break;
  310.             case 'content-description':
  311.                 $this->content_desc = $value;
  312.                 break;
  313.             case 'content-id':
  314.                 $value $this->stripComments($value);
  315.                 $this->content_id = $value;
  316.                 break;
  317.             case 'user-agent':
  318.             case 'x-mailer':
  319.                 $this->xmailer = $value;
  320.                 break;
  321.             case 'x-priority':
  322.             case 'importance':
  323.             case 'priority':
  324.                 $this->priority = $this->parsePriority($value);
  325.                 break;
  326.             case 'list-post':
  327.                 $value $this->stripComments($value);
  328.                 $this->mlist('post'$value);
  329.                 break;
  330.             case 'list-reply':
  331.                 $value $this->stripComments($value);
  332.                 $this->mlist('reply'$value);
  333.                 break;
  334.             case 'list-subscribe':
  335.                 $value $this->stripComments($value);
  336.                 $this->mlist('subscribe'$value);
  337.                 break;
  338.             case 'list-unsubscribe':
  339.                 $value $this->stripComments($value);
  340.                 $this->mlist('unsubscribe'$value);
  341.                 break;
  342.             case 'list-archive':
  343.                 $value $this->stripComments($value);
  344.                 $this->mlist('archive'$value);
  345.                 break;
  346.             case 'list-owner':
  347.                 $value $this->stripComments($value);
  348.                 $this->mlist('owner'$value);
  349.                 break;
  350.             case 'list-help':
  351.                 $value $this->stripComments($value);
  352.                 $this->mlist('help'$value);
  353.                 break;
  354.             case 'list-id':
  355.                 $value $this->stripComments($value);
  356.                 $this->mlist('id'$value);
  357.                 break;
  358.             case 'x-spam-status':
  359.             case 'x-spam-score':
  360.                 $this->x_spam_status = $this->parseSpamStatus($value);
  361.                 break;
  362.             case 'x-sm-flag-reply':
  363.                 $this->x_sm_flag_reply $value;
  364.                 break;
  365.             default:
  366.                 break;
  367.         }
  368.     }
  369.  
  370.     /**
  371.      * @param string $address 
  372.      * @return array 
  373.      */
  374.     function getAddressTokens($address{
  375.         $aTokens array();
  376.         $aSpecials array('(' ,'<' ,',' ,';' ,':');
  377.         $aReplace =  array(' (',' <',' ,',' ;',' :');
  378.         $address str_replace($aSpecials,$aReplace,$address);
  379.         $iCnt strlen($address);
  380.         $i 0;
  381.         while ($i $iCnt{
  382.             $cChar $address{$i};
  383.             switch($cChar)
  384.             {
  385.             case '<':
  386.                 $iEnd strpos($address,'>',$i+1);
  387.                 if (!$iEnd{
  388.                    $sToken substr($address,$i);
  389.                    $i $iCnt;
  390.                 else {
  391.                    $sToken substr($address,$i,$iEnd $i +1);
  392.                    $i $iEnd;
  393.                 }
  394.                 $sToken str_replace($aReplace$aSpecials,$sToken);
  395.                 if ($sToken$aTokens[$sToken;
  396.                 break;
  397.             case '"':
  398.                 $iEnd strpos($address,$cChar,$i+1);
  399.                 if ($iEnd{
  400.                    // skip escaped quotes
  401.                    $prev_char $address{$iEnd-1};
  402.                    while ($prev_char === '\\' && substr($address,$iEnd-2,2!== '\\\\'{
  403.                        $iEnd strpos($address,$cChar,$iEnd+1);
  404.                        if ($iEnd{
  405.                           $prev_char $address{$iEnd-1};
  406.                        else {
  407.                           $prev_char false;
  408.                        }
  409.                    }
  410.                 }
  411.                 if (!$iEnd{
  412.                     $sToken substr($address,$i);
  413.                     $i $iCnt;
  414.                 else {
  415.                     // also remove the surrounding quotes
  416.                     $sToken substr($address,$i+1,$iEnd $i -1);
  417.                     $i $iEnd;
  418.                 }
  419.                 $sToken str_replace($aReplace$aSpecials,$sToken);
  420.                 if ($sToken$aTokens[$sToken;
  421.                 break;
  422.             case '(':
  423.                 array_pop($aTokens)//remove inserted space
  424.                 $iEnd strpos($address,')',$i);
  425.                 if (!$iEnd{
  426.                     $sToken substr($address,$i);
  427.                     $i $iCnt;
  428.                 else {
  429.                     $iDepth 1;
  430.                     $iComment $i;
  431.                     while (($iDepth 0&& (++$iComment $iCnt)) {
  432.                         $cCharComment $address{$iComment};
  433.                         switch($cCharComment{
  434.                             case '\\':
  435.                                 ++$iComment;
  436.                                 break;
  437.                             case '(':
  438.                                 ++$iDepth;
  439.                                 break;
  440.                             case ')':
  441.                                 --$iDepth;
  442.                                 break;
  443.                             default:
  444.                                 break;
  445.                         }
  446.                     }
  447.                     if ($iDepth == 0{
  448.                         $sToken substr($address,$i,$iComment $i +1);
  449.                         $i $iComment;
  450.                     else {
  451.                         $sToken substr($address,$i,$iEnd $i 1);
  452.                         $i $iEnd;
  453.                     }
  454.                 }
  455.                 // check the next token in case comments appear in the middle of email addresses
  456.                 $prevToken end($aTokens);
  457.                 if (!in_array($prevToken,$aSpecials,true)) {
  458.                     if ($i+1<strlen($address&& !in_array($address{$i+1},$aSpecials,true)) {
  459.                         $iEnd strpos($address,' ',$i+1);
  460.                         if ($iEnd{
  461.                             $sNextToken trim(substr($address,$i+1,$iEnd $i -1));
  462.                             $i $iEnd-1;
  463.                         else {
  464.                             $sNextToken trim(substr($address,$i+1));
  465.                             $i $iCnt;
  466.                         }
  467.                         // remove the token
  468.                         array_pop($aTokens);
  469.                         // create token and add it again
  470.                         $sNewToken $prevToken $sNextToken;
  471.                         if($sNewToken$aTokens[$sNewToken;
  472.                     }
  473.                 }
  474.                 $sToken str_replace($aReplace$aSpecials,$sToken);
  475.                 if ($sToken$aTokens[$sToken;
  476.                 break;
  477.             case ',':
  478.             case ':':
  479.             case ';':
  480.             case ' ':
  481.                 $aTokens[$cChar;
  482.                 break;
  483.             default:
  484.                 $iEnd strpos($address,' ',$i+1);
  485.                 if ($iEnd{
  486.                     $sToken trim(substr($address,$i,$iEnd $i));
  487.                     $i $iEnd-1;
  488.                 else {
  489.                     $sToken trim(substr($address,$i));
  490.                     $i $iCnt;
  491.                 }
  492.                 if ($sToken$aTokens[$sToken;
  493.             }
  494.             ++$i;
  495.         }
  496.         return $aTokens;
  497.     }
  498.  
  499.     /**
  500.      * @param array $aStack 
  501.      * @param array $aComment 
  502.      * @param string $sEmail 
  503.      * @param string $sGroup 
  504.      * @return object AddressStructure object
  505.      */
  506.     function createAddressObject(&$aStack,&$aComment,&$sEmail,$sGroup=''{
  507.         //$aStack=explode(' ',implode('',$aStack));
  508.         if (!$sEmail{
  509.             while (count($aStack&& !$sEmail{
  510.                 $sEmail trim(array_pop($aStack));
  511.             }
  512.         }
  513.         if (count($aStack)) {
  514.             $sPersonal trim(implode('',$aStack));
  515.         else {
  516.             $sPersonal '';
  517.         }
  518.         if (!$sPersonal && count($aComment)) {
  519.             $sComment trim(implode(' ',$aComment));
  520.             $sPersonal .= $sComment;
  521.         }
  522.         $oAddr new AddressStructure();
  523.         if ($sPersonal && substr($sPersonal,0,2== '=?'{
  524.             $oAddr->personal encodeHeader($sPersonal);
  525.         else {
  526.             $oAddr->personal $sPersonal;
  527.         }
  528.  //       $oAddr->group = $sGroup;
  529.         $iPosAt strpos($sEmail,'@');
  530.         if ($iPosAt{
  531.            $oAddr->mailbox substr($sEmail0$iPosAt);
  532.            $oAddr->host substr($sEmail$iPosAt+1);
  533.         else {
  534.            $oAddr->mailbox $sEmail;
  535.            $oAddr->host false;
  536.         }
  537.         $sEmail '';
  538.         $aStack $aComment array();
  539.         return $oAddr;
  540.     }
  541.  
  542.     /**
  543.      * recursive function for parsing address strings and storing them in an address stucture object.
  544.      *  personal name: encoded: =?charset?Q|B?string?=
  545.      *                 quoted:  "string"
  546.      *                 normal:  string
  547.      *  email        : <mailbox@host>
  548.      *               : mailbox@host
  549.      *  This function is also used for validating addresses returned from compose
  550.      *  That's also the reason that the function became a little bit huge
  551.      * @param string $address 
  552.      * @param boolean $ar return array instead of only the first element
  553.      * @param array $addr_ar (obsolete) array with parsed addresses
  554.      * @param string $group (obsolete)
  555.      * @param string $host default domainname in case of addresses without a domainname
  556.      * @param string $lookup (since) callback function for lookup of address strings which are probably nicks (without @)
  557.      * @return mixed array with AddressStructure objects or only one address_structure object.
  558.      */
  559.     function parseAddress($address,$ar=false,$aAddress=array(),$sGroup='',$sHost='',$lookup=false{
  560.         $aTokens $this->getAddressTokens($address);
  561.         $sPersonal $sEmail $sGroup '';
  562.         $aStack $aComment array();
  563.         foreach ($aTokens as $sToken{
  564.             $cChar $sToken{0};
  565.             switch ($cChar)
  566.             {
  567.             case '=':
  568.             case '"':
  569.             case ' ':
  570.                 $aStack[$sToken;
  571.                 break;
  572.             case '(':
  573.                 $aComment[substr($sToken,1,-1);
  574.                 break;
  575.             case ';':
  576.                 if ($sGroup{
  577.                     $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
  578.                     $oAddr end($aAddress);
  579.                     if(!$oAddr || ((isset($oAddr)) && !strlen($oAddr->mailbox&& !$oAddr->personal)) {
  580.                         $sEmail $sGroup ':;';
  581.                     }
  582.                     $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
  583.                     $sGroup '';
  584.                     $aStack $aComment array();
  585.                     break;
  586.                 }
  587.             case ',':
  588.                 $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
  589.                 break;
  590.             case ':':
  591.                 $sGroup trim(implode(' ',$aStack));
  592.                 $sGroup preg_replace('/\s+/',' ',$sGroup);
  593.                 $aStack array();
  594.                 break;
  595.             case '<':
  596.                $sEmail trim(substr($sToken,1,-1));
  597.                break;
  598.             case '>':
  599.                /* skip */
  600.                break;
  601.             default$aStack[$sTokenbreak;
  602.             }
  603.         }
  604.         /* now do the action again for the last address */
  605.         $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail);
  606.         /* try to lookup the addresses in case of invalid email addresses */
  607.         $aProcessedAddress array();
  608.         foreach ($aAddress as $oAddr{
  609.           $aAddrBookAddress array();
  610.           if (!$oAddr->host{
  611.             $grouplookup false;
  612.             if ($lookup{
  613.                  $aAddr call_user_func_array($lookup,array($oAddr->mailbox));
  614.                  if (isset($aAddr['email'])) {
  615.                      if (strpos($aAddr['email'],',')) {
  616.                          $grouplookup true;
  617.                          $aAddrBookAddress $this->parseAddress($aAddr['email'],true);
  618.                      else {
  619.                          $iPosAt strpos($aAddr['email']'@');
  620.                          if ($iPosAt === FALSE{
  621.                              $oAddr->mailbox $aAddr['email'];
  622.                              $oAddr->host FALSE;
  623.                          else {
  624.                              $oAddr->mailbox substr($aAddr['email']0$iPosAt);
  625.                              $oAddr->host substr($aAddr['email']$iPosAt+1);
  626.                          
  627.                          if (isset($aAddr['name'])) {
  628.                              $oAddr->personal $aAddr['name'];
  629.                          else {
  630.                              $oAddr->personal encodeHeader($sPersonal);
  631.                          }
  632.                      }
  633.                  }
  634.             }
  635.             if (!$grouplookup && !strlen($oAddr->mailbox)) {
  636.                 $oAddr->mailbox trim($sEmail);
  637.                 if ($sHost && strlen($oAddr->mailbox)) {
  638.                     $oAddr->host $sHost;
  639.                 }
  640.             else if (!$grouplookup && !$oAddr->host{
  641.                 if ($sHost && strlen($oAddr->mailbox)) {
  642.                     $oAddr->host $sHost;
  643.                 }
  644.             }
  645.           }
  646.           if (!$aAddrBookAddress && strlen($oAddr->mailbox)) {
  647.               $aProcessedAddress[$oAddr;
  648.           else {
  649.               $aProcessedAddress array_merge($aProcessedAddress,$aAddrBookAddress);
  650.           }
  651.         }
  652.         if ($ar{
  653.             return $aProcessedAddress;
  654.         else {
  655.             if (isset($aProcessedAddress[0]))
  656.                 return $aProcessedAddress[0];
  657.             else
  658.                 return '';
  659.         }
  660.     }
  661.  
  662.     /**
  663.      * Normalise the different Priority headers into a uniform value,
  664.      * namely that of the X-Priority header (1, 3, 5). Supports:
  665.      * Priority, X-Priority, Importance.
  666.      * X-MS-Mail-Priority is not parsed because it always coincides
  667.      * with one of the other headers.
  668.      *
  669.      * NOTE: this is actually a duplicate from the code in
  670.      * functions/imap_messages:parseFetch().
  671.      * I'm not sure if it's ok here to call
  672.      * that function?
  673.      * @param string $sValue literal priority name
  674.      * @return integer 
  675.      */
  676.     function parsePriority($sValue{
  677.         // don't use function call inside array_shift.
  678.         $aValue preg_split('/\s/',trim($sValue));
  679.         $value strtolower(array_shift($aValue));
  680.  
  681.         if is_numeric($value) ) {
  682.             return $value;
  683.         }
  684.         if $value == 'urgent' || $value == 'high' {
  685.             return 1;
  686.         elseif $value == 'non-urgent' || $value == 'low' {
  687.             return 5;
  688.         }
  689.         // default is normal priority
  690.         return 3;
  691.     }
  692.  
  693.     /**
  694.      * @param string $value content type header
  695.      */
  696.     function parseContentType($value{
  697.         $pos strpos($value';');
  698.         $props '';
  699.         if ($pos 0{
  700.            $type trim(substr($value0$pos));
  701.            $props trim(substr($value$pos+1));
  702.         else {
  703.            $type $value;
  704.         }
  705.         $content_type new ContentType($type);
  706.         if ($props{
  707.             $properties $this->parseProperties($props);
  708.             if (!isset($properties['charset'])) {
  709.                 $properties['charset''us-ascii';
  710.             }
  711.             $content_type->properties $this->parseProperties($props);
  712.         }
  713.         $this->content_type = $content_type;
  714.     }
  715.  
  716.     /**
  717.      * RFC2184
  718.      * @param array $aParameters 
  719.      * @return array 
  720.      */
  721.     function processParameters($aParameters{
  722.         $aResults array();
  723.         $aCharset array();
  724.         // handle multiline parameters
  725.         foreach($aParameters as $key => $value{
  726.             if ($iPos strpos($key,'*')) {
  727.                 $sKey substr($key,0,$iPos);
  728.                 if (!isset($aResults[$sKey])) {
  729.                     $aResults[$sKey$value;
  730.                     if (substr($key,-1== '*'// parameter contains language/charset info
  731.                         $aCharset[$sKey;
  732.                     }
  733.                 else {
  734.                     $aResults[$sKey.= $value;
  735.                 }
  736.             else {
  737.                 $aResults[$key$value;
  738.             }
  739.         }
  740.         foreach ($aCharset as $key{
  741.             $value $aResults[$key];
  742.             // extract the charset & language
  743.             $charset substr($value,0,strpos($value,"'"));
  744.             $value substr($value,strlen($charset)+1);
  745.             $language substr($value,0,strpos($value,"'"));
  746.             $value substr($value,strlen($charset)+1);
  747.             /* FIXME: What's the status of charset decode with language information ????
  748.              * Maybe language information contains only ascii text and charset_decode() 
  749.              * only runs sm_encode_html_special_chars() on it. If it contains 8bit information, you 
  750.              * get html encoded text in charset used by selected translation.
  751.              */
  752.             $value charset_decode($charset,$value);
  753.             $aResults[$key$value;
  754.         }
  755.         return $aResults;
  756.     }
  757.  
  758.     /**
  759.      * @param string $value 
  760.      * @return array 
  761.      */
  762.     function parseProperties($value{
  763.         $propArray explode(';'$value);
  764.         $propResultArray array();
  765.         foreach ($propArray as $prop{
  766.             $prop trim($prop);
  767.             $pos strpos($prop'=');
  768.             if ($pos 0)  {
  769.                 $key trim(substr($prop0$pos));
  770.                 $val trim(substr($prop$pos+1));
  771.                 if (strlen($val&& $val{0== '"'{
  772.                     $val substr($val1-1);
  773.                 }
  774.                 $propResultArray[$key$val;
  775.             }
  776.         }
  777.         return $this->processParameters($propResultArray);
  778.     }
  779.  
  780.     /**
  781.      * Fills disposition object in rfc822Header object
  782.      * @param string $value 
  783.      */
  784.     function parseDisposition($value{
  785.         $pos strpos($value';');
  786.         $props '';
  787.         if ($pos 0{
  788.             $name trim(substr($value0$pos));
  789.             $props trim(substr($value$pos+1));
  790.         else {
  791.             $name $value;
  792.         }
  793.         $props_a $this->parseProperties($props);
  794.         $disp new Disposition($name);
  795.         $disp->properties $props_a;
  796.         $this->disposition = $disp;
  797.     }
  798.  
  799.     /**
  800.      * Fills mlist array keys in rfc822Header object
  801.      * @param string $field 
  802.      * @param string $value 
  803.      */
  804.     function mlist($field$value{
  805.         $res_a array();
  806.         $value_a explode(','$value);
  807.         foreach ($value_a as $val{
  808.             $val trim($val);
  809.             if ($val{0== '<'{
  810.                 $val substr($val1-1);
  811.             }
  812.             if (substr($val07== 'mailto:'{
  813.                 $res_a['mailto'substr($val7);
  814.             else {
  815.                 $res_a['href'$val;
  816.             }
  817.         }
  818.         $this->mlist[$field$res_a;
  819.     }
  820.  
  821.     /**
  822.      * Parses the X-Spam-Status or X-Spam-Score header
  823.      * @param string $value 
  824.      */
  825.     function parseSpamStatus($value{
  826.         // Header value looks like this:
  827.         // No, score=1.5 required=5.0 tests=MSGID_FROM_MTA_ID,NO_REAL_NAME,UPPERCASE_25_50 autolearn=disabled version=3.1.0-gr0
  828.         // Update circa 2018, this header can also be simply:
  829.         // No, score=1.5
  830.         // So we make the rest of the line optional (there are likely other permutations, so
  831.         // each element is made optional except the first two... maybe even that's not flexible enough)
  832.         //
  833.         // Also now allow parsing of X-Spam-Score header, whose value is just a float
  834.  
  835.         $spam_status array();
  836.  
  837.         if (preg_match ('/^(?:(No|Yes),\s+score=)?(-?\d+\.\d+)(?:\s+required=(-?\d+\.\d+))?(?:\s+tests=(.*?))?(?:\s+autolearn=(.*?))?(?:\s+version=(.+?))?$/i'$value$matches)) {
  838.  
  839.             // full header
  840.             $spam_status['bad_format'0;
  841.             $spam_status['value'$matches[0];
  842.  
  843.             // is_spam
  844.             if (!empty($matches[1])) {
  845.                 if (strtolower($matches[1]== 'yes')
  846.                     $spam_status['is_spam'true;
  847.                 else
  848.                     $spam_status['is_spam'false;
  849.             }
  850.  
  851.             // score
  852.             if (!empty($matches[2]))
  853.                 $spam_status['score'$matches[2];
  854.  
  855.             // required
  856.             if (!empty($matches[3]))
  857.                 $spam_status['required'$matches[3];
  858.  
  859.             // tests
  860.             if (isset($matches[4])) {
  861.                 $tests array();
  862.                 $tests explode(','$matches[4]);
  863.                 foreach ($tests as $test{
  864.                     $spam_status['tests'][trim($test);
  865.                 }
  866.             }
  867.  
  868.             // autolearn
  869.             if (isset($matches[5]))
  870.                 $spam_status['autolearn'$matches[5];
  871.  
  872.             // version
  873.             if (isset($matches[6]))
  874.                 $spam_status['version'$matches[6];
  875.  
  876.         else {
  877.             $spam_status['bad_format'1;
  878.             $spam_status['value'$value;
  879.         }
  880.         return $spam_status;
  881.     }
  882.  
  883.     /**
  884.      * function to get the address strings out of the header.
  885.      * example1: header->getAddr_s('to').
  886.      * example2: header->getAddr_s(array('to', 'cc', 'bcc'))
  887.      * @param mixed $arr string or array of strings
  888.      * @param string $separator 
  889.      * @param boolean $encoded (since 1.4.0) return encoded or plain text addresses
  890.      * @param boolean $unconditionally_quote (since 1.4.21/1.5.2) When TRUE, always
  891.      *                                                       quote the personal part,
  892.      *                                                       whether or not it is
  893.      *                                                       encoded, otherwise quoting
  894.      *                                                       is only added if the
  895.      *                                                       personal part is not encoded
  896.      * @return string 
  897.      */
  898.     function getAddr_s($arr$separator ', '$encoded=false$unconditionally_quote=FALSE{
  899.         $s '';
  900.  
  901.         if (is_array($arr)) {
  902.             foreach($arr as $arg{
  903.                 if ($this->getAddr_s($arg$separator$encoded$unconditionally_quote)) {
  904.                     $s .= $separator;
  905.                 }
  906.             }
  907.             $s ($s substr($s2$s);
  908.         else {
  909.             $addr $this->{$arr};
  910.             if (is_array($addr)) {
  911.                 foreach ($addr as $addr_o{
  912.                     if (is_object($addr_o)) {
  913.                         if ($encoded{
  914.                             $s .= $addr_o->getEncodedAddress($unconditionally_quote$separator;
  915.                         else {
  916.                             $s .= $addr_o->getAddress(TRUEFALSE$unconditionally_quote$separator;
  917.                         }
  918.                     }
  919.                 }
  920.                 $s substr($s0-strlen($separator));
  921.             else {
  922.                 if (is_object($addr)) {
  923.                     if ($encoded{
  924.                         $s .= $addr->getEncodedAddress($unconditionally_quote);
  925.                     else {
  926.                         $s .= $addr->getAddress(TRUEFALSE$unconditionally_quote);
  927.                     }
  928.                 }
  929.             }
  930.         }
  931.         return $s;
  932.     }
  933.  
  934.     /**
  935.      * function to get the array of addresses out of the header.
  936.      * @param mixed $arg string or array of strings
  937.      * @param array $excl_arr array of excluded email addresses
  938.      * @param array $arr array of added email addresses
  939.      * @return array 
  940.      */
  941.     function getAddr_a($arg$excl_arr array()$arr array()) {
  942.         if (is_array($arg)) {
  943.             foreach($arg as $argument{
  944.                 $arr $this->getAddr_a($argument$excl_arr$arr);
  945.             }
  946.         else {
  947.             $addr $this->{$arg};
  948.             if (is_array($addr)) {
  949.                 foreach ($addr as $next_addr{
  950.                     if (is_object($next_addr)) {
  951.                         if (isset($next_addr->host&& ($next_addr->host != '')) {
  952.                             $email $next_addr->mailbox '@' $next_addr->host;
  953.                         else {
  954.                             $email $next_addr->mailbox;
  955.                         }
  956.                         $email strtolower($email);
  957.                         if ($email && !isset($arr[$email]&& !isset($excl_arr[$email])) {
  958.                             $arr[$email$next_addr->personal;
  959.                         }
  960.                     }
  961.                 }
  962.             else {
  963.                 if (is_object($addr)) {
  964.                     $email  $addr->mailbox;
  965.                     $email .= (isset($addr->host'@' $addr->host '');
  966.                     $email  strtolower($email);
  967.                     if ($email && !isset($arr[$email]&& !isset($excl_arr[$email])) {
  968.                         $arr[$email$addr->personal;
  969.                     }
  970.                 }
  971.             }
  972.         }
  973.         return $arr;
  974.     }
  975.  
  976.     /**
  977. //FIXME: This needs some documentation (inside the function too)!  Don't code w/out comments!
  978.      * Looking at the code years after it was written,
  979.      * this is my (Paul) best guess as to what this
  980.      * function does (note that docs previously claimed
  981.      * that this function returns boolean or an array,
  982.      * but it no longer appears to return an array - an
  983.      * integer instead):
  984.      *
  985.      * Inspects the TO and CC headers of the message
  986.      * represented by this object, looking for the
  987.      * address(es) given by $address
  988.      *
  989.      * If $address is a string:
  990.      *    Serves as a test (returns boolean) as to
  991.      *    whether or not the given address is found
  992.      *    anywhere in the TO or CC headers
  993.      *
  994.      * If $address is an array:
  995.      *    Looks through this list of addresses and
  996.      *    returns the array index (an integer even
  997.      *    if the array is given with keys of a
  998.      *    different type) of the first matching
  999.      *    $address found in this message's
  1000.      *    TO or CC headers, unless there is an exact
  1001.      *    match (meaning that the "personal
  1002.      *    information" in addition to the email
  1003.      *    address also matches), in which case that
  1004.      *    index (the first one found) is returned
  1005.      *
  1006.      * @param mixed $address Address(es) to search for in this
  1007.      *                        message's TO and CC headers - please
  1008.      *                        see above how the format of this
  1009.      *                        argument affects the return value
  1010.      *                        of this function
  1011.      * @param boolean $recurs FOR INTERNAL USE ONLY
  1012.      *
  1013.      * @return mixed Boolean when $address is a scalar,
  1014.      *                indicating whether or not the address
  1015.      *                was found in the TO or CC headers.
  1016.      *                An integer when $address is an array,
  1017.      *                containing the index of the value in
  1018.      *                that array that was found in the TO
  1019.      *                or CC headers, or boolean FALSE if
  1020.      *                there were no matches at all
  1021.      *
  1022.      * @since 1.3.2
  1023.      */
  1024.     function findAddress($address$recurs false{
  1025.         $result false;
  1026.         if (is_array($address)) {
  1027.             $i=0;
  1028.             foreach($address as $argument{
  1029.                 $match $this->findAddress($argumenttrue);
  1030.                 if ($match[1]// this indicates when the personal information matched
  1031.                     return $i;
  1032.                 else {
  1033.                     if (count($match[0]&& $result === FALSE{
  1034.                         $result $i;
  1035.                     }
  1036.                 }
  1037.                 ++$i;
  1038.             }
  1039.         else {
  1040.             if (!is_array($this->cc)) $this->cc array();
  1041.             if (!is_array($this->to)) $this->to array();
  1042.             $srch_addr $this->parseAddress($address);
  1043.             $results array();
  1044.             foreach ($this->to as $to{
  1045.                 if (strtolower($to->host== strtolower($srch_addr->host)) {
  1046.                     if (strtolower($to->mailbox== strtolower($srch_addr->mailbox)) {
  1047.                         $results[$srch_addr;
  1048.                         if (strtolower($to->personal== strtolower($srch_addr->personal)) {
  1049.                             if ($recurs{
  1050.                                 return array($resultstrue);
  1051.                             else {
  1052.                                 return true;
  1053.                             }
  1054.                         }
  1055.                     }
  1056.                 }
  1057.             }
  1058.             foreach ($this->cc as $cc{
  1059.                 if (strtolower($cc->host== strtolower($srch_addr->host)) {
  1060.                     if (strtolower($cc->mailbox== strtolower($srch_addr->mailbox)) {
  1061.                         $results[$srch_addr;
  1062.                         if (strtolower($cc->personal== strtolower($srch_addr->personal)) {
  1063.                             if ($recurs{
  1064.                                 return array($resultstrue);
  1065.                             else {
  1066.                                 return true;
  1067.                             }
  1068.                         }
  1069.                     }
  1070.                 }
  1071.             }
  1072.             if ($recurs{
  1073.                 return array($resultsfalse);
  1074.             elseif (count($results)) {
  1075.                 return true;
  1076.             else {
  1077.                 return false;
  1078.             }
  1079.         }
  1080.         //exit;
  1081.         return $result;
  1082.     }
  1083.  
  1084.     /**
  1085.      * @param string $type0 media type
  1086.      * @param string $type1 media subtype
  1087.      * @return array media properties
  1088.      * @todo check use of media type arguments
  1089.      */
  1090.     function getContentType($type0$type1{
  1091.         $type0 $this->content_type->type0;
  1092.         $type1 $this->content_type->type1;
  1093.         return $this->content_type->properties;
  1094.     }
  1095. }

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