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

Documentation generated on Sat, 07 Oct 2006 16:13:21 +0300 by phpDocumentor 1.3.0RC6