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-2014 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: Rfc822Header.class.php 14458 2014-04-23 19:15:52Z 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.             default:
  326.                 break;
  327.         }
  328.     }
  329.  
  330.     /**
  331.      * @param string $address 
  332.      * @return array 
  333.      */
  334.     function getAddressTokens($address{
  335.         $aTokens array();
  336.         $aAddress array();
  337.         $aSpecials array('(' ,'<' ,',' ,';' ,':');
  338.         $aReplace =  array(' (',' <',' ,',' ;',' :');
  339.         $address str_replace($aSpecials,$aReplace,$address);
  340.         $iCnt strlen($address);
  341.         $i 0;
  342.         while ($i $iCnt{
  343.             $cChar $address{$i};
  344.             switch($cChar)
  345.             {
  346.             case '<':
  347.                 $iEnd strpos($address,'>',$i+1);
  348.                 if (!$iEnd{
  349.                    $sToken substr($address,$i);
  350.                    $i $iCnt;
  351.                 else {
  352.                    $sToken substr($address,$i,$iEnd $i +1);
  353.                    $i $iEnd;
  354.                 }
  355.                 $sToken str_replace($aReplace$aSpecials,$sToken);
  356.                 if ($sToken$aTokens[$sToken;
  357.                 break;
  358.             case '"':
  359.                 $iEnd strpos($address,$cChar,$i+1);
  360.                 if ($iEnd{
  361.                    // skip escaped quotes
  362.                    $prev_char $address{$iEnd-1};
  363.                    while ($prev_char === '\\' && substr($address,$iEnd-2,2!== '\\\\'{
  364.                        $iEnd strpos($address,$cChar,$iEnd+1);
  365.                        if ($iEnd{
  366.                           $prev_char $address{$iEnd-1};
  367.                        else {
  368.                           $prev_char false;
  369.                        }
  370.                    }
  371.                 }
  372.                 if (!$iEnd{
  373.                     $sToken substr($address,$i);
  374.                     $i $iCnt;
  375.                 else {
  376.                     // also remove the surrounding quotes
  377.                     $sToken substr($address,$i+1,$iEnd $i -1);
  378.                     $i $iEnd;
  379.                 }
  380.                 $sToken str_replace($aReplace$aSpecials,$sToken);
  381.                 if ($sToken$aTokens[$sToken;
  382.                 break;
  383.             case '(':
  384.                 array_pop($aTokens)//remove inserted space
  385.                 $iEnd strpos($address,')',$i);
  386.                 if (!$iEnd{
  387.                     $sToken substr($address,$i);
  388.                     $i $iCnt;
  389.                 else {
  390.                     $iDepth 1;
  391.                     $iComment $i;
  392.                     while (($iDepth 0&& (++$iComment $iCnt)) {
  393.                         $cCharComment $address{$iComment};
  394.                         switch($cCharComment{
  395.                             case '\\':
  396.                                 ++$iComment;
  397.                                 break;
  398.                             case '(':
  399.                                 ++$iDepth;
  400.                                 break;
  401.                             case ')':
  402.                                 --$iDepth;
  403.                                 break;
  404.                             default:
  405.                                 break;
  406.                         }
  407.                     }
  408.                     if ($iDepth == 0{
  409.                         $sToken substr($address,$i,$iComment $i +1);
  410.                         $i $iComment;
  411.                     else {
  412.                         $sToken substr($address,$i,$iEnd $i 1);
  413.                         $i $iEnd;
  414.                     }
  415.                 }
  416.                 // check the next token in case comments appear in the middle of email addresses
  417.                 $prevToken end($aTokens);
  418.                 if (!in_array($prevToken,$aSpecials,true)) {
  419.                     if ($i+1<strlen($address&& !in_array($address{$i+1},$aSpecials,true)) {
  420.                         $iEnd strpos($address,' ',$i+1);
  421.                         if ($iEnd{
  422.                             $sNextToken trim(substr($address,$i+1,$iEnd $i -1));
  423.                             $i $iEnd-1;
  424.                         else {
  425.                             $sNextToken trim(substr($address,$i+1));
  426.                             $i $iCnt;
  427.                         }
  428.                         // remove the token
  429.                         array_pop($aTokens);
  430.                         // create token and add it again
  431.                         $sNewToken $prevToken $sNextToken;
  432.                         if($sNewToken$aTokens[$sNewToken;
  433.                     }
  434.                 }
  435.                 $sToken str_replace($aReplace$aSpecials,$sToken);
  436.                 if ($sToken$aTokens[$sToken;
  437.                 break;
  438.             case ',':
  439.             case ':':
  440.             case ';':
  441.             case ' ':
  442.                 $aTokens[$cChar;
  443.                 break;
  444.             default:
  445.                 $iEnd strpos($address,' ',$i+1);
  446.                 if ($iEnd{
  447.                     $sToken trim(substr($address,$i,$iEnd $i));
  448.                     $i $iEnd-1;
  449.                 else {
  450.                     $sToken trim(substr($address,$i));
  451.                     $i $iCnt;
  452.                 }
  453.                 if ($sToken$aTokens[$sToken;
  454.             }
  455.             ++$i;
  456.         }
  457.         return $aTokens;
  458.     }
  459.  
  460.     /**
  461.      * @param array $aStack 
  462.      * @param array $aComment 
  463.      * @param string $sEmail 
  464.      * @param string $sGroup 
  465.      * @return object AddressStructure object
  466.      */
  467.     function createAddressObject(&$aStack,&$aComment,&$sEmail,$sGroup=''{
  468.         //$aStack=explode(' ',implode('',$aStack));
  469.         if (!$sEmail{
  470.             while (count($aStack&& !$sEmail{
  471.                 $sEmail trim(array_pop($aStack));
  472.             }
  473.         }
  474.         if (count($aStack)) {
  475.             $sPersonal trim(implode('',$aStack));
  476.         else {
  477.             $sPersonal '';
  478.         }
  479.         if (!$sPersonal && count($aComment)) {
  480.             $sComment trim(implode(' ',$aComment));
  481.             $sPersonal .= $sComment;
  482.         }
  483.         $oAddr new AddressStructure();
  484.         if ($sPersonal && substr($sPersonal,0,2== '=?'{
  485.             $oAddr->personal encodeHeader($sPersonal);
  486.         else {
  487.             $oAddr->personal $sPersonal;
  488.         }
  489.  //       $oAddr->group = $sGroup;
  490.         $iPosAt strpos($sEmail,'@');
  491.         if ($iPosAt{
  492.            $oAddr->mailbox substr($sEmail0$iPosAt);
  493.            $oAddr->host substr($sEmail$iPosAt+1);
  494.         else {
  495.            $oAddr->mailbox $sEmail;
  496.            $oAddr->host false;
  497.         }
  498.         $sEmail '';
  499.         $aStack $aComment array();
  500.         return $oAddr;
  501.     }
  502.  
  503.     /**
  504.      * recursive function for parsing address strings and storing them in an address stucture object.
  505.      *  personal name: encoded: =?charset?Q|B?string?=
  506.      *                 quoted:  "string"
  507.      *                 normal:  string
  508.      *  email        : <mailbox@host>
  509.      *               : mailbox@host
  510.      *  This function is also used for validating addresses returned from compose
  511.      *  That's also the reason that the function became a little bit huge
  512.      * @param string $address 
  513.      * @param boolean $ar return array instead of only the first element
  514.      * @param array $addr_ar (obsolete) array with parsed addresses
  515.      * @param string $group (obsolete)
  516.      * @param string $host default domainname in case of addresses without a domainname
  517.      * @param string $lookup (since) callback function for lookup of address strings which are probably nicks (without @)
  518.      * @return mixed array with AddressStructure objects or only one address_structure object.
  519.      */
  520.     function parseAddress($address,$ar=false,$aAddress=array(),$sGroup='',$sHost='',$lookup=false{
  521.         $aTokens $this->getAddressTokens($address);
  522.         $sPersonal $sEmail $sComment $sGroup '';
  523.         $aStack $aComment array();
  524.         foreach ($aTokens as $sToken{
  525.             $cChar $sToken{0};
  526.             switch ($cChar)
  527.             {
  528.             case '=':
  529.             case '"':
  530.             case ' ':
  531.                 $aStack[$sToken;
  532.                 break;
  533.             case '(':
  534.                 $aComment[substr($sToken,1,-1);
  535.                 break;
  536.             case ';':
  537.                 if ($sGroup{
  538.                     $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
  539.                     $oAddr end($aAddress);
  540.                     if(!$oAddr || ((isset($oAddr)) && !strlen($oAddr->mailbox&& !$oAddr->personal)) {
  541.                         $sEmail $sGroup ':;';
  542.                     }
  543.                     $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
  544.                     $sGroup '';
  545.                     $aStack $aComment array();
  546.                     break;
  547.                 }
  548.             case ',':
  549.                 $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
  550.                 break;
  551.             case ':':
  552.                 $sGroup trim(implode(' ',$aStack));
  553.                 $sGroup preg_replace('/\s+/',' ',$sGroup);
  554.                 $aStack array();
  555.                 break;
  556.             case '<':
  557.                $sEmail trim(substr($sToken,1,-1));
  558.                break;
  559.             case '>':
  560.                /* skip */
  561.                break;
  562.             default$aStack[$sTokenbreak;
  563.             }
  564.         }
  565.         /* now do the action again for the last address */
  566.         $aAddress[$this->createAddressObject($aStack,$aComment,$sEmail);
  567.         /* try to lookup the addresses in case of invalid email addresses */
  568.         $aProcessedAddress array();
  569.         foreach ($aAddress as $oAddr{
  570.           $aAddrBookAddress array();
  571.           if (!$oAddr->host{
  572.             $grouplookup false;
  573.             if ($lookup{
  574.                  $aAddr call_user_func_array($lookup,array($oAddr->mailbox));
  575.                  if (isset($aAddr['email'])) {
  576.                      if (strpos($aAddr['email'],',')) {
  577.                          $grouplookup true;
  578.                          $aAddrBookAddress $this->parseAddress($aAddr['email'],true);
  579.                      else {
  580.                          $iPosAt strpos($aAddr['email']'@');
  581.                          if ($iPosAt === FALSE{
  582.                              $oAddr->mailbox $aAddr['email'];
  583.                              $oAddr->host FALSE;
  584.                          else {
  585.                              $oAddr->mailbox substr($aAddr['email']0$iPosAt);
  586.                              $oAddr->host substr($aAddr['email']$iPosAt+1);
  587.                          }
  588.                          if (isset($aAddr['name'])) {
  589.                              $oAddr->personal $aAddr['name'];
  590.                          else {
  591.                              $oAddr->personal encodeHeader($sPersonal);
  592.                          }
  593.                      }
  594.                  }
  595.             }
  596.             if (!$grouplookup && !strlen($oAddr->mailbox)) {
  597.                 $oAddr->mailbox trim($sEmail);
  598.                 if ($sHost && strlen($oAddr->mailbox)) {
  599.                     $oAddr->host $sHost;
  600.                 }
  601.             else if (!$grouplookup && !$oAddr->host{
  602.                 if ($sHost && strlen($oAddr->mailbox)) {
  603.                     $oAddr->host $sHost;
  604.                 }
  605.             }
  606.           }
  607.           if (!$aAddrBookAddress && strlen($oAddr->mailbox)) {
  608.               $aProcessedAddress[$oAddr;
  609.           else {
  610.               $aProcessedAddress array_merge($aProcessedAddress,$aAddrBookAddress);
  611.           }
  612.         }
  613.         if ($ar{
  614.             return $aProcessedAddress;
  615.         else {
  616.             if (isset($aProcessedAddress[0]))
  617.                 return $aProcessedAddress[0];
  618.             else
  619.                 return '';
  620.         }
  621.     }
  622.  
  623.     /**
  624.      * Normalise the different Priority headers into a uniform value,
  625.      * namely that of the X-Priority header (1, 3, 5). Supports:
  626.      * Priority, X-Priority, Importance.
  627.      * X-MS-Mail-Priority is not parsed because it always coincides
  628.      * with one of the other headers.
  629.      *
  630.      * NOTE: this is actually a duplicate from the function in
  631.      * functions/imap_messages. I'm not sure if it's ok here to call
  632.      * that function?
  633.      * @param string $sValue literal priority name
  634.      * @return integer 
  635.      */
  636.     function parsePriority($sValue{
  637.         // don't use function call inside array_shift.
  638.         $aValue preg_split('/\s/',trim($sValue));
  639.         $value strtolower(array_shift($aValue));
  640.  
  641.         if is_numeric($value) ) {
  642.             return $value;
  643.         }
  644.         if $value == 'urgent' || $value == 'high' {
  645.             return 1;
  646.         elseif $value == 'non-urgent' || $value == 'low' {
  647.             return 5;
  648.         }
  649.         // default is normal priority
  650.         return 3;
  651.     }
  652.  
  653.     /**
  654.      * @param string $value content type header
  655.      */
  656.     function parseContentType($value{
  657.         $pos strpos($value';');
  658.         $props '';
  659.         if ($pos 0{
  660.            $type trim(substr($value0$pos));
  661.            $props trim(substr($value$pos+1));
  662.         else {
  663.            $type $value;
  664.         }
  665.         $content_type new ContentType($type);
  666.         if ($props{
  667.             $properties $this->parseProperties($props);
  668.             if (!isset($properties['charset'])) {
  669.                 $properties['charset''us-ascii';
  670.             }
  671.             $content_type->properties $this->parseProperties($props);
  672.         }
  673.         $this->content_type = $content_type;
  674.     }
  675.  
  676.     /**
  677.      * RFC2184
  678.      * @param array $aParameters 
  679.      * @return array 
  680.      */
  681.     function processParameters($aParameters{
  682.         $aResults array();
  683.         $aCharset array();
  684.         // handle multiline parameters
  685.         foreach($aParameters as $key => $value{
  686.             if ($iPos strpos($key,'*')) {
  687.                 $sKey substr($key,0,$iPos);
  688.                 if (!isset($aResults[$sKey])) {
  689.                     $aResults[$sKey$value;
  690.                     if (substr($key,-1== '*'// parameter contains language/charset info
  691.                         $aCharset[$sKey;
  692.                     }
  693.                 else {
  694.                     $aResults[$sKey.= $value;
  695.                 }
  696.             else {
  697.                 $aResults[$key$value;
  698.             }
  699.         }
  700.         foreach ($aCharset as $key{
  701.             $value $aResults[$key];
  702.             // extract the charset & language
  703.             $charset substr($value,0,strpos($value,"'"));
  704.             $value substr($value,strlen($charset)+1);
  705.             $language substr($value,0,strpos($value,"'"));
  706.             $value substr($value,strlen($charset)+1);
  707.             /* FIXME: What's the status of charset decode with language information ????
  708.              * Maybe language information contains only ascii text and charset_decode() 
  709.              * only runs sm_encode_html_special_chars() on it. If it contains 8bit information, you 
  710.              * get html encoded text in charset used by selected translation.
  711.              */
  712.             $value charset_decode($charset,$value);
  713.             $aResults[$key$value;
  714.         }
  715.         return $aResults;
  716.     }
  717.  
  718.     /**
  719.      * @param string $value 
  720.      * @return array 
  721.      */
  722.     function parseProperties($value{
  723.         $propArray explode(';'$value);
  724.         $propResultArray array();
  725.         foreach ($propArray as $prop{
  726.             $prop trim($prop);
  727.             $pos strpos($prop'=');
  728.             if ($pos 0)  {
  729.                 $key trim(substr($prop0$pos));
  730.                 $val trim(substr($prop$pos+1));
  731.                 if (strlen($val&& $val{0== '"'{
  732.                     $val substr($val1-1);
  733.                 }
  734.                 $propResultArray[$key$val;
  735.             }
  736.         }
  737.         return $this->processParameters($propResultArray);
  738.     }
  739.  
  740.     /**
  741.      * Fills disposition object in rfc822Header object
  742.      * @param string $value 
  743.      */
  744.     function parseDisposition($value{
  745.         $pos strpos($value';');
  746.         $props '';
  747.         if ($pos 0{
  748.             $name trim(substr($value0$pos));
  749.             $props trim(substr($value$pos+1));
  750.         else {
  751.             $name $value;
  752.         }
  753.         $props_a $this->parseProperties($props);
  754.         $disp new Disposition($name);
  755.         $disp->properties $props_a;
  756.         $this->disposition = $disp;
  757.     }
  758.  
  759.     /**
  760.      * Fills mlist array keys in rfc822Header object
  761.      * @param string $field 
  762.      * @param string $value 
  763.      */
  764.     function mlist($field$value{
  765.         $res_a array();
  766.         $value_a explode(','$value);
  767.         foreach ($value_a as $val{
  768.             $val trim($val);
  769.             if ($val{0== '<'{
  770.                 $val substr($val1-1);
  771.             }
  772.             if (substr($val07== 'mailto:'{
  773.                 $res_a['mailto'substr($val7);
  774.             else {
  775.                 $res_a['href'$val;
  776.             }
  777.         }
  778.         $this->mlist[$field$res_a;
  779.     }
  780.  
  781.     /**
  782.      * Parses the X-Spam-Status header
  783.      * @param string $value 
  784.      */
  785.     function parseSpamStatus($value{
  786.         // Header value looks like this:
  787.         // 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
  788.  
  789.         $spam_status array();
  790.  
  791.         if (preg_match ('/^(No|Yes),\s+score=(-?\d+\.\d+)\s+required=(-?\d+\.\d+)\s+tests=(.*?)\s+autolearn=(.*?)\s+version=(.+?)$/'$value$matches)) {
  792.             // full header
  793.             $spam_status['bad_format'0;
  794.             $spam_status['value'$matches[0];
  795.             // is_spam
  796.             if (isset($matches[1])
  797.                 && strtolower($matches[1]== 'yes'{
  798.                 $spam_status['is_spam'true;
  799.             else {
  800.                 $spam_status['is_spam'false;
  801.             }
  802.  
  803.             // score
  804.             $spam_status['score'$matches[2];
  805.  
  806.             // required
  807.             $spam_status['required'$matches[3];
  808.  
  809.             // tests
  810.             $tests array();
  811.             $tests explode(','$matches[4]);
  812.             foreach ($tests as $test{
  813.                 $spam_status['tests'][trim($test);
  814.             }
  815.  
  816.             // autolearn
  817.             $spam_status['autolearn'$matches[5];
  818.  
  819.             // version
  820.             $spam_status['version'$matches[6];
  821.         else {
  822.             $spam_status['bad_format'1;
  823.             $spam_status['value'$value;
  824.         }
  825.         return $spam_status;
  826.     }
  827.  
  828.     /**
  829.      * function to get the address strings out of the header.
  830.      * example1: header->getAddr_s('to').
  831.      * example2: header->getAddr_s(array('to', 'cc', 'bcc'))
  832.      * @param mixed $arr string or array of strings
  833.      * @param string $separator 
  834.      * @param boolean $encoded (since 1.4.0) return encoded or plain text addresses
  835.      * @param boolean $unconditionally_quote (since 1.4.21/1.5.2) When TRUE, always
  836.      *                                                       quote the personal part,
  837.      *                                                       whether or not it is
  838.      *                                                       encoded, otherwise quoting
  839.      *                                                       is only added if the
  840.      *                                                       personal part is not encoded
  841.      * @return string 
  842.      */
  843.     function getAddr_s($arr$separator ',',$encoded=false,$unconditionally_quote=FALSE{
  844.         $s '';
  845.  
  846.         if (is_array($arr)) {
  847.             foreach($arr as $arg{
  848.                 if ($this->getAddr_s($arg$separator$encoded$unconditionally_quote)) {
  849.                     $s .= $separator;
  850.                 }
  851.             }
  852.             $s ($s substr($s2$s);
  853.         else {
  854.             $addr $this->{$arr};
  855.             if (is_array($addr)) {
  856.                 foreach ($addr as $addr_o{
  857.                     if (is_object($addr_o)) {
  858.                         if ($encoded{
  859.                             $s .= $addr_o->getEncodedAddress($unconditionally_quote$separator;
  860.                         else {
  861.                             $s .= $addr_o->getAddress(TRUEFALSE$unconditionally_quote$separator;
  862.                         }
  863.                     }
  864.                 }
  865.                 $s substr($s0-strlen($separator));
  866.             else {
  867.                 if (is_object($addr)) {
  868.                     if ($encoded{
  869.                         $s .= $addr->getEncodedAddress($unconditionally_quote);
  870.                     else {
  871.                         $s .= $addr->getAddress(TRUEFALSE$unconditionally_quote);
  872.                     }
  873.                 }
  874.             }
  875.         }
  876.         return $s;
  877.     }
  878.  
  879.     /**
  880.      * function to get the array of addresses out of the header.
  881.      * @param mixed $arg string or array of strings
  882.      * @param array $excl_arr array of excluded email addresses
  883.      * @param array $arr array of added email addresses
  884.      * @return array 
  885.      */
  886.     function getAddr_a($arg$excl_arr array()$arr array()) {
  887.         if (is_array($arg)) {
  888.             foreach($arg as $argument{
  889.                 $arr $this->getAddr_a($argument$excl_arr$arr);
  890.             }
  891.         else {
  892.             $addr $this->{$arg};
  893.             if (is_array($addr)) {
  894.                 foreach ($addr as $next_addr{
  895.                     if (is_object($next_addr)) {
  896.                         if (isset($next_addr->host&& ($next_addr->host != '')) {
  897.                             $email $next_addr->mailbox '@' $next_addr->host;
  898.                         else {
  899.                             $email $next_addr->mailbox;
  900.                         }
  901.                         $email strtolower($email);
  902.                         if ($email && !isset($arr[$email]&& !isset($excl_arr[$email])) {
  903.                             $arr[$email$next_addr->personal;
  904.                         }
  905.                     }
  906.                 }
  907.             else {
  908.                 if (is_object($addr)) {
  909.                     $email  $addr->mailbox;
  910.                     $email .= (isset($addr->host'@' $addr->host '');
  911.                     $email  strtolower($email);
  912.                     if ($email && !isset($arr[$email]&& !isset($excl_arr[$email])) {
  913.                         $arr[$email$addr->personal;
  914.                     }
  915.                 }
  916.             }
  917.         }
  918.         return $arr;
  919.     }
  920.  
  921.     /**
  922. //FIXME: This needs some documentation (inside the function too)!  Don't code w/out comments!
  923.      * Looking at the code years after it was written,
  924.      * this is my (Paul) best guess as to what this
  925.      * function does (note that docs previously claimed
  926.      * that this function returns boolean or an array,
  927.      * but it no longer appears to return an array - an
  928.      * integer instead):
  929.      *
  930.      * Inspects the TO and CC (or FROM) headers of the
  931.      * message represented by this object, looking for
  932.      * the address(es) given by $address
  933.      *
  934.      * If $address is a string:
  935.      *    Serves as a test (returns boolean) as to
  936.      *    whether or not the given address is found
  937.      *    anywhere in the TO or CC (or FROM) headers
  938.      *
  939.      * If $address is an array:
  940.      *    Looks through this list of addresses and
  941.      *    returns the array index (an integer even
  942.      *    if the array is given with keys of a
  943.      *    different type) of the *last* matching
  944.      *    $address found in this message's
  945.      *    TO or CC (or FROM) headers, unless there
  946.      *    is an exact match (meaning that the "personal
  947.      *    information" in addition to the email
  948.      *    address also matches), in which case that
  949.      *    index (the first one found) is returned
  950.      *
  951.      * @param mixed $address Address(es) to search for in this
  952.      *                        message's TO and CC (or FROM)
  953.      *                        headers - please see above how the
  954.      *                        format of this argument affects the
  955.      *                        return value of this function
  956.      * @param boolean $use_from When TRUE, this function will ONLY
  957.      *                           search the FROM headers and NOT the
  958.      *                           TO and CC headers, when FALSE, ONLY
  959.      *                           the TO and CC headers are searched
  960.      *                           (OPTIONAL; default is FALSE)
  961.      * @param boolean $recurs FOR INTERNAL USE ONLY
  962.      *
  963.      * @return mixed Boolean when $address is a scalar,
  964.      *                indicating whether or not the address
  965.      *                was found in the TO or CC headers.
  966.      *                An integer when $address is an array,
  967.      *                containing the index of the value in
  968.      *                that array that was found in the TO
  969.      *                or CC headers, or boolean FALSE if
  970.      *                there were no matches at all
  971.      *
  972.      * @since 1.3.2
  973.      */
  974.     function findAddress($address$use_from=FALSE$recurs=FALSE{
  975.         $result false;
  976.         if (is_array($address)) {
  977.             $i=0;
  978.             foreach($address as $argument{
  979.                 $match $this->findAddress($argument$use_fromtrue);
  980.                 if ($match[1]{
  981.                     return $i;
  982.                 else {
  983.                     if (count($match[0]&& !$result{
  984.                         $result $i;
  985.                     }
  986.                 }
  987.                 ++$i;
  988.             }
  989.         else {
  990.             $srch_addr $this->parseAddress($address);
  991.             $results array();
  992.             if ($use_from{
  993.                 if (!is_array($this->from)) $this->from array();
  994.                 foreach ($this->from as $from{
  995.                     if (strtolower($from->host== strtolower($srch_addr->host)) {
  996.                         if (strtolower($from->mailbox== strtolower($srch_addr->mailbox)) {
  997.                             $results[$srch_addr;
  998.                             if (strtolower($from->personal== strtolower($srch_addr->personal)) {
  999.                                 if ($recurs{
  1000.                                     return array($resultstrue);
  1001.                                 else {
  1002.                                     return true;
  1003.                                 }
  1004.                             }
  1005.                         }
  1006.                     }
  1007.                 }
  1008.             else {
  1009.                 if (!is_array($this->cc)) $this->cc array();
  1010.                 if (!is_array($this->to)) $this->to array();
  1011.                 foreach ($this->to as $to{
  1012.                     if (strtolower($to->host== strtolower($srch_addr->host)) {
  1013.                         if (strtolower($to->mailbox== strtolower($srch_addr->mailbox)) {
  1014.                             $results[$srch_addr;
  1015.                             if (strtolower($to->personal== strtolower($srch_addr->personal)) {
  1016.                                 if ($recurs{
  1017.                                     return array($resultstrue);
  1018.                                 else {
  1019.                                     return true;
  1020.                                 }
  1021.                             }
  1022.                         }
  1023.                     }
  1024.                 }
  1025.                 foreach ($this->cc as $cc{
  1026.                     if (strtolower($cc->host== strtolower($srch_addr->host)) {
  1027.                         if (strtolower($cc->mailbox== strtolower($srch_addr->mailbox)) {
  1028.                             $results[$srch_addr;
  1029.                             if (strtolower($cc->personal== strtolower($srch_addr->personal)) {
  1030.                                 if ($recurs{
  1031.                                     return array($resultstrue);
  1032.                                 else {
  1033.                                     return true;
  1034.                                 }
  1035.                             }
  1036.                         }
  1037.                     }
  1038.                 }
  1039.             }
  1040.             if ($recurs{
  1041.                 return array($resultsfalse);
  1042.             elseif (count($results)) {
  1043.                 return true;
  1044.             else {
  1045.                 return false;
  1046.             }
  1047.         }
  1048.         //exit;
  1049.         return $result;
  1050.     }
  1051.  
  1052.     /**
  1053.      * @param string $type0 media type
  1054.      * @param string $type1 media subtype
  1055.      * @return array media properties
  1056.      * @todo check use of media type arguments
  1057.      */
  1058.     function getContentType($type0$type1{
  1059.         $type0 $this->content_type->type0;
  1060.         $type1 $this->content_type->type1;
  1061.         return $this->content_type->properties;
  1062.     }
  1063. }

Documentation generated on Thu, 30 Oct 2014 04:21:45 +0100 by phpDocumentor 1.4.3