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

Documentation generated on Wed, 13 Dec 2017 04:24:14 +0100 by phpDocumentor 1.4.3