Net_NNTP
[ class tree: Net_NNTP ] [ index: Net_NNTP ] [ all elements ]

Source for file Header.php

Documentation is available at Header.php

  1. <?php
  2. // +-----------------------------------------------------------------------+
  3. // |                                                                       |
  4. // |                  http://www.heino.gehlsen.dk/software/license         |
  5. // |                                                                       |
  6. // +-----------------------------------------------------------------------+
  7. // |                                                                       |
  8. // | This work (including software, documents, or other related items) is  |
  9. // | being provided by the copyright holders under the following license.  |
  10. // | By obtaining, using and/or copying this work, you (the licensee)      |
  11. // | agree that you have read, understood, and will comply with the        |
  12. // | following terms and conditions:                                       |
  13. // |                                                                       |
  14. // | Permission to use, copy, modify, and distribute this software and     |
  15. // | its documentation, with or without modification, for any purpose and  |
  16. // | without fee or royalty is hereby granted, provided that you include   |
  17. // | the following on ALL copies of the software and documentation or      |
  18. // | portions thereof, including modifications, that you make:             |
  19. // |                                                                       |
  20. // | 1. The full text of this NOTICE in a location viewable to users of    |
  21. // |    the redistributed or derivative work.                              |
  22. // |                                                                       |
  23. // | 2. Any pre-existing intellectual property disclaimers, notices, or    |
  24. // |    terms and conditions. If none exist, a short notice of the         |
  25. // |    following form (hypertext is preferred, text is permitted) should  |
  26. // |    be used within the body of any redistributed or derivative code:   |
  27. // |     http://www.heino.gehlsen.dk/software/license"                     |
  28. // |                                                                       |
  29. // | 3. Notice of any changes or modifications to the files, including     |
  30. // |    the date changes were made. (We recommend you provide URIs to      |
  31. // |    the location from which the code is derived.)                      |
  32. // |                                                                       |
  33. // | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT    |
  34. // | HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,    |
  35. // | INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR        |
  36. // | FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE    |
  37. // | OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,           |
  38. // | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.                               |
  39. // |                                                                       |
  40. // | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,        |
  41. // | SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE        |
  42. // | SOFTWARE OR DOCUMENTATION.                                            |
  43. // |                                                                       |
  44. // | The name and trademarks of copyright holders may NOT be used in       |
  45. // | advertising or publicity pertaining to the software without specific, |
  46. // | written prior permission. Title to copyright in this software and any |
  47. // | associated documentation will at all times remain with copyright      |
  48. // | holders.                                                              |
  49. // |                                                                       |
  50. // +-----------------------------------------------------------------------+
  51. // |                                                                       |
  52. // | except for the references to the copyright holder, which has either   |
  53. // | been changes or removed.                                              |
  54. // |                                                                       |
  55. // +-----------------------------------------------------------------------+
  56. // $Id: Header.php,v 1.10.2.1 2004/06/25 12:19:01 heino Exp $
  57.  
  58. require_once 'PEAR.php';
  59.  
  60.  
  61. define('NET_NNTP_HEADER_SET_UNFOLD'1);
  62. define('NET_NNTP_HEADER_SET_DECODE'2);
  63. define('NET_NNTP_HEADER_SET_CLEAN'4);
  64. define('NET_NNTP_HEADER_SET_KEEPCASE'8);
  65. define('NET_NNTP_HEADER_SET_DEFAULT'NET_NNTP_HEADER_SET_CLEAN | NET_NNTP_HEADER_SET_UNFOLD | NET_NNTP_HEADER_SET_DECODE);
  66.  
  67. define('NET_NNTP_HEADER_GET_FOLD'1);
  68. define('NET_NNTP_HEADER_GET_ENCODE'2);
  69. define('NET_NNTP_HEADER_GET_DEFAULT'NET_NNTP_HEADER_GET_ENCODE | NET_NNTP_HEADER_GET_FOLD);
  70.  
  71.  
  72. /**
  73.  * The Net_NNTP_Header class
  74.  *
  75.  * @version $Revision: 1.10.2.1 $
  76.  * @package Net_NNTP
  77.  *
  78.  * @author Heino H. Gehlsen <heino@gehlsen.dk>
  79.  */
  80. {
  81.     // {{{ properties
  82.  
  83.     /**
  84.      * Container for the header fields
  85.      *
  86.      * @var    array 
  87.      * @access public
  88.      */
  89.     var $fields;
  90.  
  91.     // }}}
  92.     // {{{ constructor
  93.  
  94.     /**
  95.      * Constructor
  96.      *
  97.      * @access public
  98.      * @since 0.1
  99.      */
  100.     function Net_NNTP_Header()
  101.     {
  102.     // Reset object
  103.     $this->reset();
  104.     }
  105.  
  106.     // }}}
  107.     // {{{ reset()
  108.     
  109.     /**
  110.      * Reset the field container
  111.      * 
  112.      * @access public
  113.      * @since 0.1
  114.      */
  115.     function reset()
  116.     {
  117.     $this->fields = array();
  118.     }
  119.  
  120.     // }}}
  121.     // {{{ create()
  122.     
  123.     /**
  124.      * Create a new instance of Net_NNTP_Header
  125.      *
  126.      * @param optional mixed $input Can be any of the following:
  127.      *                                 (string) RFC2822 style header lines (CRLF included)
  128.      *                               (array)  RFC2822 style header lines (CRLF not included)
  129.      *                               (object) Net_NNTP_Header object
  130.      *                               (object) Net_NNTP_Message object
  131.      * 
  132.      * @return object Net_NNTP_Header object
  133.      * @access public
  134.      * @since 0.1
  135.      */
  136.     function create($input = null)
  137.     {
  138.     switch (true{
  139.  
  140.         // Null
  141.         case is_null($input);
  142.         $Object = new Net_NNTP_Header();
  143.             return $Object;
  144.         break;
  145.  
  146.         // Object
  147.         case is_object($input);
  148.         switch (true{
  149.             
  150.             // Header
  151.             case is_a($input'net_nntp_header'):
  152.                 $return = new Net_NNTP_Header();
  153.                 $return->setFields($input);
  154.                 return $return;
  155.             break;
  156.             
  157.             // Message
  158.             case is_a($input'net_nntp_message'):
  159.                 $return = new Net_NNTP_Header();
  160.                 $return->setFields($input);
  161.                 return $return;
  162.             break;
  163.             
  164.             // Unknown object/class
  165.             default:
  166.             return PEAR::throwError('Unsupported object/class: '.get_class($input)null);
  167.         }
  168.         break;
  169.  
  170.         // String & Array
  171.         case is_string($input);
  172.         case is_array($input);
  173.             $Object = new Net_NNTP_Header();
  174.         $R $Object->setFields($input);
  175.         if (PEAR::isError($R)) {
  176.             return $R;
  177.         }
  178.         return $Object;
  179.         break;
  180.  
  181.         // Unknown type
  182.         default:
  183.         return PEAR::throwError('Unsupported object/class: '.get_class($input)null);
  184.     }
  185.     }
  186.  
  187.     // }}}
  188.     // {{{ add()
  189.     
  190.     /**
  191.      * Add a new field
  192.      * 
  193.      * @param string $tag 
  194.      * @param string $value 
  195.      * @param optional int $index
  196.      * 
  197.      * @access public
  198.      * @since 0.1
  199.      */
  200.     function add($tag$value$index = null)
  201.     {
  202.     // Add header to $return array
  203.         if (isset($this->fields[$tag]&& is_array($this->fields[$tag])) {
  204.         // The header name has already been used at least two times.
  205.             $this->fields[$tag][$value;
  206.         elseif (isset($this->fields[$tag])) {
  207.         // The header name has already been used one time -> change to nedted values.
  208.             $this->fields[$tag= array($this->fields[$tag]$value);
  209.         else {
  210.         // The header name has not used until now.
  211.         $this->fields[$tag$value;
  212.         }
  213.     }
  214.  
  215.     // }}}
  216.     // {{{ replace()
  217.     
  218.     /**
  219.      * Replace a field's value
  220.      * 
  221.      * @param string $tag 
  222.      * @param string $value 
  223.      * @param optional int $index
  224.      * 
  225.      * @access public
  226.      * @since 0.1
  227.      */
  228.     function replace($tag$value$index = null)
  229.     {
  230.     if (isset($this->fields[$tag])) {
  231.         if ($index === null{
  232.         $this->fields[$tag$value;
  233.         else {
  234.         if (is_array($this->fields[$tag])) {
  235.             $this->fields[$tag][$index$value;
  236.         else {
  237. //TODO: Currently ignores $index, and just replaces the value
  238.             $this->fields[$tag$value;
  239.         }
  240.         }
  241.     else {
  242.         $this->fields[$tag$value;
  243.     }
  244.     }
  245.  
  246.     // }}}
  247.     // {{{ delete()
  248.     
  249.     /**
  250.      * Delete a field
  251.      * 
  252.      * @param string $tag 
  253.      * @param optional int $index
  254.      * 
  255.      * @access public
  256.      * @since 0.1
  257.      */
  258.     function delete($tag$index = null)
  259.     {
  260.     if (isset($this->fields[$tag])) {
  261.         if ($index == null{
  262.         unset($this->fields[$tag]);
  263.         else {
  264.         if (is_array($this->fields[$tag])) {
  265.             unset($this->fields[$tag][$index]);
  266.         else {
  267.             unset($this->fields[$tag]);
  268.         }
  269.         }
  270.     else {
  271.         // Do nothing...
  272.     }
  273.     }
  274.  
  275.     // }}}
  276.     // {{{ get()
  277.     
  278.     /**
  279.      * Gets the value of a header field
  280.      * 
  281.      * @param string $tag 
  282.      * @param optional int $index (defaults to 0)
  283.      * 
  284.      * @return string 
  285.      * @access public
  286.      * @since 0.1
  287.      */
  288.     function get($tag$index = 0)
  289.     {
  290.     if (!isset($this->fields[$tag])) {
  291.         return null;
  292.     }
  293.  
  294.     if (is_array($this->fields[$tag])) {
  295.         return $this->fields[$tag][$index];
  296.     else {
  297.         if ($index == 0{
  298.         return $this->fields[$tag];
  299.         else {
  300.             return null;
  301.         }
  302.     }
  303.     }
  304.  
  305.     // }}}
  306.     // {{{ getAll()
  307.     
  308.     /**
  309.      * Gets the values of a all occurences of a field
  310.      * 
  311.      * @param string $tag 
  312.      * @param optional int $index
  313.      * 
  314.      * @return array 
  315.      * @access public
  316.      * @since 0.1
  317.      */
  318.     function getAll($tag)
  319.     {
  320.     if (!isset($this->fields[$tag])) {
  321.         array();
  322.     }
  323.  
  324.     if (is_array($this->fields[$tag])) {
  325.         return $this->fields[$tag];
  326.     else {
  327. // TODO: What to do, when not array but index is set...
  328.         return array($this->fields[$tag]);
  329.     }
  330.     }
  331.  
  332.     // }}}
  333.     // {{{ count()
  334.     
  335.     /**
  336.      * Returns the number of times the given field tag appears in the header.
  337.      * 
  338.      * @param string $tag 
  339.      * 
  340.      * @return int 
  341.      * @access public
  342.      * @since 0.1
  343.      */
  344.     function count($tag)
  345.     {
  346.     if (!isset($this->fields[$tag])) {
  347.         return 0;
  348.     }
  349.  
  350.     if (is_array($this->fields[$tag])) {
  351.         return count($this->fields[$tag]);
  352.     else {
  353.         return 1;
  354.     }
  355.     }
  356.  
  357.     // }}}
  358.     // {{{ tags()
  359.     
  360.     /**
  361.      * Returns an array of all the tags that exist in the header.
  362.      * Each tag will only appear in the list once.
  363.      * 
  364.      * @return array 
  365.      * @access public
  366.      * @since 0.1
  367.      */
  368.     function tags()
  369.     {
  370.     return array_keys($this->fields);
  371.     }
  372.  
  373.     // }}}
  374.     // {{{ clean()
  375.     
  376.     /**
  377.      * Remove any header field that only contains whitespace.
  378.      * 
  379.      * @access public
  380.      * @since 0.1
  381.      */
  382.     function clean()
  383.     {
  384.     foreach (array_keys($this->fieldsas $tag{
  385.         if (is_array($this->fields[$tag])) {
  386.         foreach (array_keys($this->fields[$tag]as $i{
  387.             if (trim($this->fields[$tag][$i]== ''{
  388.             unset($this->fields[$tag][$i]);
  389.             }
  390.         }
  391.         else {
  392.         if (trim($this->fields[$tag]== ''{
  393.             unset($this->fields[$tag]);
  394.         }
  395.         }
  396.     }
  397.     }
  398.  
  399.     // }}}
  400.     // {{{ setFields()
  401.     
  402.     /**
  403.      * Import RFC2822 style header lines given in $input into the object
  404.      * 
  405.      * @param mixed $input Can be any of the following:
  406.      *                      (string) RFC2822 style header lines (CRLF included)
  407.      *                      (array)  RFC2822 style header lines (CRLF not included)
  408.      *                      (object) Net_NNTP_Header object
  409.      *                      (object) Net_NNTP_Message object
  410.      * @param optional $flags 
  411.      * 
  412.      * @access public
  413.      * @since 0.1
  414.      */
  415.     function setFields(&$input$flags = NET_NNTP_HEADER_SET_DEFAULT)
  416.     {
  417.     switch (true{
  418.  
  419.         // Object
  420.         case is_object($input):
  421.         switch (true{
  422.             case is_a($input'net_nntp_header'):
  423.             $this->reset();
  424.             $this->fields = $input->getFields();
  425.             break;
  426.             
  427.             case is_a($input'net_nntp_message'):
  428.             $h $input->getHeader();
  429.             $this->setFields($h);
  430.             break;
  431.             
  432.             // Unknown type
  433.             default:
  434.             return PEAR::throwError('Unsupported object/class: '.get_class($input)null);
  435.         }
  436.         break;
  437.  
  438.         // String
  439.         case is_string($input):
  440.         $this->fields = $this->_parseString($input$flags);
  441.         break;
  442.  
  443.         // Array
  444.         case is_array($input):
  445.         $this->fields = $this->_parseArray($input$flags);
  446.         break;
  447.  
  448.         // Unknown type
  449.         default:
  450.         return PEAR::throwError('Unsupported type: '.gettype($input)null);
  451.     }
  452.     }
  453.  
  454.     // }}}
  455.     // {{{ getFields()
  456.  
  457.     /**
  458.      * Get the array of header fields.
  459.      * 
  460.      * @param optional $flags 
  461.      *
  462.      * @return array 
  463.      * @access public
  464.      * @since 0.1
  465.      */
  466.     function getFields()
  467.     {
  468.     return $this->fields;
  469.     }
  470.  
  471.     // }}}
  472.     // {{{ getFieldsString()
  473.  
  474.     /**
  475.      * Export a string of RFC2822 style header style lines from the object.
  476.      * 
  477.      * @param optional $flags 
  478.      *
  479.      * @return string RFC2822 style header lines (CRLF included)
  480.      * @access public
  481.      * @since 0.1
  482.      */
  483.     function getFieldsString($flags = NET_NNTP_HEADER_GET_DEFAULT)
  484.     {
  485.     return $this->_regenerateString($this->fields$flags);
  486.     }
  487.  
  488.     // }}}
  489.     // {{{ getFieldsArray()
  490.  
  491.     /**
  492.      * Export an array of RFC2822 style header style lines from the object.
  493.      *
  494.      * @param optional $flags 
  495.      *
  496.      * @return array RFC2822 style header lines (CRLF not included)
  497.      * @access public
  498.      * @since 0.1
  499.      */
  500.     function getFieldsArray($flags = NET_NNTP_HEADER_GET_DEFAULT)
  501.     {
  502.     return $this->_regenerateArray($this->fields$flags);
  503.     }
  504.  
  505.     // }}}
  506.     // {{{ _parseString()
  507.     
  508.     /**
  509.      * Parse a string of RFC2822 style header lines into a 'header array' with the header names as keys.
  510.      * 
  511.      * @param string $string RFC2822 style header lines (CRLF included)
  512.      * @param optional $flags 
  513.      * 
  514.      * @return array 'header array' with the header names as keys, values may be nested.
  515.      * @access private
  516.      * @since 0.1
  517.      */
  518.     function _parseString($string$flags)
  519.     {
  520.         // Clean the header lines
  521.     if (($flags NET_NNTP_HEADER_SET_CLEAN== NET_NNTP_HEADER_SET_CLEAN{
  522.         $string $this->cleanString($string);
  523.     }
  524.  
  525.         // Unfold the header lines
  526.     if (($flags NET_NNTP_HEADER_SET_UNFOLD== NET_NNTP_HEADER_SET_UNFOLD{
  527.         $string $this->unfoldString($string);
  528.     }
  529.  
  530.     // Convert to array
  531.     $array explode("\r\n"$string);
  532.  
  533.     // Remove body if present
  534.     $i array_search(''$array);
  535.     if ($i != null{
  536.         array_splice($array$i(count($array))-$i);
  537.     }
  538.  
  539.     // Forward to _parse()
  540.     return $this->_parse($array$flags);
  541.     }
  542.  
  543.     // }}}
  544.     // {{{ _parseArray()
  545.  
  546.     /**
  547.      * Parse an array of RFC2822 style header lines into a 'header array' with the header names as keys.
  548.      * 
  549.      * @param array $array RFC2822 style header lines (CRLF not included)
  550.      * @param optional $flags 
  551.      *
  552.      * @return array 'header array' with the header names as keys, values may be nested.
  553.      * @access private
  554.      * @since 0.1
  555.      */
  556.     function _parseArray($array$flags)
  557.     {
  558.         // Clean the header lines
  559.     if (($flags NET_NNTP_HEADER_SET_CLEAN== NET_NNTP_HEADER_SET_CLEAN{
  560.         $array $this->cleanArray($array);
  561.     }
  562.  
  563.         // Unfold the header lines
  564.     if (($flags NET_NNTP_HEADER_SET_UNFOLD== NET_NNTP_HEADER_SET_UNFOLD{
  565.         $array $this->unfoldArray($array);
  566.     }
  567.  
  568.     // Remove body if present
  569.     $i array_search(''$array);
  570.     if ($i != null{
  571.         array_splice($array$icount($array)-$i);
  572.     }
  573.  
  574.     // Forward to _parse()
  575.     return $this->_parse($array$flags);
  576.     }
  577.  
  578.     // }}}
  579.     // {{{ _parse()
  580.  
  581.     /**
  582.      * Parse a cleaned and unfolded array of RFC2822 style header lines into a 'header array' with the header names as keys.
  583.      * When header names a'pear more the once, the resulting array will have the values nested in the order of a'pear'ence.
  584.      * 
  585.      * @param array $array RFC2822 style header lines (CRLF not included)
  586.      * @param optional $flags 
  587.      *
  588.      * @return array 'header array' with the header names as keys, values may be nested.
  589.      * @access private
  590.      * @since 0.1
  591.      */
  592.     function _parse($array$flags)
  593.     {
  594.     // Init return variable
  595.     $return = array();
  596.  
  597.     // Loop through all headers
  598.         foreach ($array as $field{
  599.         // Separate header name and value
  600.         if (!preg_match('/([\S]+)\:\s*(.*)\s*/'$field$matches)) {
  601.         // Fail...
  602.         }
  603.         $name $matches[1];
  604.         $value $matches[2];
  605.         unset($matches);
  606.  
  607.         // Change header name to lower case
  608.         if (($flags NET_NNTP_HEADER_SET_KEEPCASE!= NET_NNTP_HEADER_SET_KEEPCASE{
  609.          $name strtolower($name);
  610.         }
  611.         
  612.         // Decode header value acording to RFC 2047
  613.         if (($flags NET_NNTP_HEADER_SET_DECODE== NET_NNTP_HEADER_SET_UNFOLD{
  614.         $value $this->decodeString($value);
  615.         }
  616.         
  617.         // Add header to $return array
  618.             if (isset($return[$name]AND is_array($return[$name])) {
  619.         // The header name has already been used at least two times.
  620.                 $return[$name][$value;
  621.             elseif (isset($return[$name])) {
  622.         // The header name has already been used one time -> change to nedted values.
  623.                 $return[$name= array($return[$name]$value);
  624.             else {
  625.         // The header name has not used until now.
  626.             $return[$name$value;
  627.             }
  628.         }
  629.  
  630.         return $return;
  631.     }
  632.  
  633.     // }}}
  634.     // {{{ _regenerateString()
  635.     
  636.     /**
  637.      * Generate a string of RFC2822 style header lines from the 'header array' given in $array.
  638.      * 
  639.      * @param array $array RFC2822 style header lines
  640.      * @param optional $flags 
  641.      *
  642.      * @return string RFC822 style header lines (CRLF included).
  643.      * @access private
  644.      * @since 0.1
  645.      */
  646.     function _regenerateString($array$flags)
  647.     {
  648.     // ( Forward to _regenerateArray() and then convert to string )
  649.     return implode("\r\n"$this->_regenerateArray($array$flags));
  650.     }
  651.  
  652.     // }}}
  653.     // {{{ _regenerateArray()
  654.  
  655.     /**
  656.      * Generate an array of RFC2822 style header lines from the array given in $array.
  657.      *
  658.      * @param array $array 'header field array'
  659.      * @param optional $flags 
  660.      *
  661.      * @return array RFC822 style header lines (CRLF not included).
  662.      * @access private
  663.      * @since 0.1
  664.      */
  665.     function _regenerateArray($array$flags)
  666.     {
  667.     // Init return variable
  668.     $return = array();
  669.  
  670.     // Loop through headers
  671.         foreach ($array as $name => $value{
  672.         // Encode header values acording to RFC 2047
  673.         if (($flags NET_NNTP_HEADER_GET_ENCODE== NET_NNTP_HEADER_GET_ENCODE{
  674.         if (is_array($value)) {
  675.             foreach(array_keys($valueas $key{
  676.             $value[$key$this->encodeString($value[$key]);
  677.             }
  678.         else {
  679.             $value $this->encodeString($value);
  680.         }
  681.         }
  682.  
  683.         if (is_array($value)) {
  684.             foreach ($value as $sub_value{
  685.                 $return[$name.': '.$sub_value;
  686.         }
  687.         else {
  688.             $return[$name.': '.$value;
  689.         }
  690.         }
  691.  
  692.     // Fold headers
  693.     if (($flags NET_NNTP_HEADER_GET_FOLD== NET_NNTP_HEADER_GET_FOLD{
  694.         $return $this->foldArray($return);
  695.     }
  696.  
  697.         return $return;
  698.     }
  699.  
  700.     // }}}
  701.     // {{{ unfoldString()
  702.  
  703.     /**
  704.      * Do the (RFC822 3.1.1) header unfolding to a string of RFC2822 header lines.
  705.      * 
  706.      * @param string $string RFC2822 header lines to unfolded (CRLF included)
  707.      *
  708.      * @return string Unfolded RFC2822 header lines (CRLF included)
  709.      * @access public
  710.      * @since 0.1
  711.      */
  712.     function unfoldString($string)
  713.     {
  714.     // Correct \r to \r\n
  715.         $string preg_replace("/\r?\n/""\r\n"$string);
  716.  
  717.     // Unfold multiline headers
  718.         $string preg_replace("/\r\n(\t| )+/"' '$string);
  719.  
  720.     return $string;
  721.     }
  722.  
  723.     // }}}
  724.     // {{{ unfoldArray()
  725.  
  726.     /**
  727.      * Do the (RFC822 3.1.1) header unfolding to an array of RFC2822 header lines.
  728.      *
  729.      * @param array $array RFC2822 header lines to unfolded (CRLF not included)
  730.      *
  731.      * @return array Unfolded RFC2822 header lines (CRLF not included)
  732.      * @access public
  733.      * @since 0.1
  734.      */
  735.     function unfoldArray($array)
  736.     {
  737.     // Unfold multiline headers
  738.     for ($i count($array)-1; $i>0; $i--{
  739.  
  740.         // Check for leading whitespace
  741.         if (preg_match('/^(\x09|\x20)/'$array[$i])) {
  742.             
  743.             // Remove folding \r\n
  744.                 if (substr($array[$i-1]-2== "\r\n"{
  745.                 $array[$i-1substr($array[$i-1]0-2);
  746.                 }
  747.             
  748.             // Append folded line to prev line
  749.             $array[$i-1$array[$i-1].' '.ltrim($array[$i]" \t");
  750.             
  751.             // Remove folded line
  752.         array_splice($array$i1);
  753.         }
  754.     }
  755.  
  756.     return $array;
  757.     }
  758.  
  759.     // }}}
  760.     // {{{ foldArray()
  761.  
  762.     /**
  763.      * Folds an array of RFC2822 style header lines.
  764.      *
  765.      * @param array $array 
  766.      * @param optional int $maxlen
  767.      *
  768.      * @return array 
  769.      * @access public
  770.      * @since 0.1
  771.      */
  772.     function foldArray($array$maxlen = 78)
  773.     {
  774.     $return = array();
  775.  
  776.     foreach (array_keys($arrayas $key{
  777.         $tmp $this->_foldExplode($array[$key]$maxlen);
  778.         $prepend '';
  779.         foreach (array_keys($tmpas $key2{
  780.         $return[$prepend.$tmp[$key2];
  781.         $prepend "\t";
  782.         }
  783.     }
  784.  
  785.     return $return;    
  786.     }
  787.  
  788.     // }}}
  789.     // {{{ foldString()
  790.  
  791.     /**
  792.      * Folds a string by inserting CRLF's and TAB's where allowed
  793.      *
  794.      * @param string $string 
  795.      * @param optional int $maxlen
  796.      *
  797.      * @return string 
  798.      * @access public
  799.      * @since 0.1
  800.      */
  801.     function foldString($string$maxlen = 78)
  802.     {
  803.     $array $this->_foldExplode($string$maxlen);
  804.     $return implode("\r\n\t"$array);
  805.     return $return;
  806.     }
  807.  
  808.     // }}}
  809.     // {{{ _foldExplode()
  810.  
  811.     /**
  812.      * Unfold $string, and return a 'folded' array
  813.      *
  814.      * The current implementation is still experimental, and is NOT expected to comply with RFC2822 !!!
  815.      *
  816.      * @param string $string 
  817.      * @param optional int $maxlen
  818.      *
  819.      * @return array 
  820.      * @access private
  821.      * @since 0.1
  822.      */
  823.     function _foldExplode($string$maxlen = 78)
  824. //TODO:
  825.     {
  826.     if ($maxlen < 20{
  827.         $maxlen = 20;
  828.     }
  829.  
  830.     if ($maxlen > 998{
  831.         $maxlen = 998;
  832.     }
  833.  
  834.     if (strlen($string<= $maxlen{
  835.         return array($string);
  836.         }
  837.       
  838.     $min = (int) ($maxlen (2/5)) - 4;
  839.         $max $maxlen - 5;                   // 4 for leading spcs + 1 for [\,\;]
  840.  
  841.     // try splitting at ',' or ';'   >2/5 along the line
  842.         // Split the line up
  843.     // next split a whitespace
  844.     // else we are looking at a single word and probably don't want to split
  845.     $exp = array();
  846.         $exp[= "[^\"]\{$min,$max}?[\,\;]\s";
  847.         $exp[= "[^\"]\{1,$max}\s";
  848.         $exp["[^\s\"]*(?:\"[^\"]*\"[^\s\"]*)+\s";
  849.     $exp["[^\s\"]+\s";
  850.  
  851.     $exp ="/^\s*(".implode('|'$exp).")(.*)\$/x";
  852.     $tmp $string;
  853.     $return = array();
  854.  
  855.     while ((strlen($tmp$max&& (preg_match($exp$tmp$match))) {
  856.         $return[$match[1];
  857.         $tmp $match[2];
  858.     }
  859.  
  860.     $return[$tmp;
  861.     
  862.     return $return;
  863.     }
  864.  
  865.     // }}}
  866.     // {{{ decodeString()
  867.  
  868.     /**
  869.      * Given a header/string, this function will decode it according to RFC2047.
  870.      * Probably not *exactly* conformant, but it does pass all the given
  871.      * examples (in RFC2047).
  872.      * 
  873.      * @param string $input Input header value to decode
  874.      *
  875.      * @return string Decoded header value
  876.      * @access public
  877.      * @since 0.1
  878.      */
  879.     function decodeString($input)
  880.     {
  881.         // Remove white space between encoded-words
  882.         $input preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i''\1=?'$input);
  883.  
  884.         // For each encoded-word...
  885.         while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i'$input$matches)) {
  886.  
  887.             $encoded  $matches[1];
  888.             $charset  $matches[2];
  889.             $encoding $matches[3];
  890.             $text     $matches[4];
  891.  
  892.             switch (strtolower($encoding)) {
  893.  
  894.                 case 'b'// RFC2047 4.1
  895.                     $text base64_decode($text);
  896.                     break;
  897.  
  898.                 case 'q'// RFC2047 4.2
  899.                     $text str_replace('_'' '$text);
  900.                     preg_match_all('/=([a-f0-9]{2})/i'$text$matches);
  901.                     foreach($matches[1as $value)
  902.                         $text str_replace('='.$valuechr(hexdec($value))$text);
  903.                     break;
  904.             }
  905.  
  906.             $input str_replace($encoded$text$header);
  907.         }
  908.  
  909.         return $input;
  910.     }
  911.  
  912.     // }}}
  913.     // {{{ encodeString()
  914.  
  915.     /**
  916.      * Encodes the string given in $string as per RFC2047
  917.      * 
  918.      * @param string $string The string to encode
  919.      *
  920.      * @return string Encoded string
  921.      * @access public
  922.      * @since 0.1
  923.      */
  924.     function encodeString($string)
  925.     {
  926.     // TODO: could be better! (Look into CPAN's Encode::MIME::Header)
  927.     
  928.     $charset 'iso-8859-1';
  929.     
  930.         preg_match_all('/(\w*[\x80-\xFF]+\w*)/'$string$matches);
  931.     foreach ($matches[1as $value{
  932.             $replacement preg_replace('/([\x80-\xFF])/e''"=" . strtoupper(dechex(ord("\1")))'$value);
  933.             $string str_replace($value'=?' $charset '?Q?' $replacement '?='$string);
  934.         }
  935.             
  936.         return $string;
  937.     }
  938.  
  939.     // }}}
  940.     // {{{ cleanString()
  941.  
  942.     /**
  943.      * Removes CRLF and misplaced empty lines before and after actual headerlines.
  944.      * 
  945.      * @param string $string. 
  946.      *
  947.      * @return string 
  948.      * @access public
  949.      * @since 0.1
  950.      */
  951.     function cleanString($string)
  952.     {
  953.     // Correct missing CR's before LF's
  954.         $string preg_replace("!\r?\n!""\r\n"$string);
  955.  
  956.     // Remove empty lines from start and end.
  957.     // TODO: This should be done better...
  958.     $string trim($string"\r\n");
  959.  
  960.         return $string;
  961.     }
  962.  
  963.     // }}}
  964.     // {{{ cleanArray()
  965.  
  966.     /**
  967.      * Removes CRLF and misplaced empty lines before and after actual headerlines.
  968.      * 
  969.      * @param array $input 
  970.      *
  971.      * @return array 
  972.      * @access public
  973.      * @since 0.1
  974.      */
  975.     function cleanArray($input)
  976.     {
  977.     // Remove empty lines from the start
  978.     while (reset($input== "\r\n"{
  979.         array_shift($input);
  980.     }
  981.     // Remove empty lines from the end
  982.     while (end($input== "\r\n"{
  983.         array_pop($input);
  984.     }
  985.  
  986.     // Run backwards through all lines
  987.     for ($i count($input)-1; $i > 0; $i--{
  988.  
  989.         // Remove \r\n from the end
  990.         $input preg_replace("/\r?\n$/"''$input);
  991.     }
  992.  
  993.         return $input;
  994.     }
  995.  
  996.     // }}}
  997.  
  998. }
  999.  
  1000. ?>

Documentation generated on Mon, 11 Mar 2019 13:52:39 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.