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

Documentation generated on Sat, 20 Jan 2018 04:26:52 +0100 by phpDocumentor 1.4.3