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($line0$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($valuearray('  ' => ' '));
  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($valuetrue);
  244.                 break;
  245.             case 'mail-followup-to':
  246.                 $this->mail_followup_to = $this->parseAddress($valuetrue);
  247.                 break;
  248.             case 'to':
  249.                 $this->to = $this->parseAddress($valuetrue);
  250.                 break;
  251.             case 'cc':
  252.                 $this->cc = $this->parseAddress($valuetrue);
  253.                 break;
  254.             case 'bcc':
  255.                 $this->bcc = $this->parseAddress($valuetrue);
  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