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

Source for file Realtime.php

Documentation is available at Realtime.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: Realtime.php,v 1.1.2.1 2004/06/25 11:46:21 heino Exp $
  57.  
  58. require_once 'Net/NNTP/Protocol.php';
  59. require_once 'Net/NNTP/Header.php';
  60. require_once 'Net/NNTP/Message.php';
  61.  
  62.  
  63. /* NNTP Authentication modes */
  64. define('NET_NNTP_AUTHORIGINAL''original');
  65. define('NET_NNTP_AUTHSIMPLE',   'simple');
  66. define('NET_NNTP_AUTHGENERIC',  'generic');
  67.  
  68. /**
  69.  * The Net_NNTP_Realtime class is a frontend class to the
  70.  * Net_NNTP_Protocol class. It does everything in realtime...
  71.  *
  72.  * @author Heino H. Gehlsen <heino@gehlsen.dk>
  73.  */
  74. class Net_NNTP_Realtime extends Net_NNTP_Protocol
  75. {
  76.     // {{{ properties
  77.  
  78.     /**
  79.      * Used for storing information about the currently selected group
  80.      *
  81.      * @var array 
  82.      * @access private
  83.      * @since 0.3
  84.      */
  85.     var $_currentGroup = null;
  86.  
  87.     // }}}
  88.     // {{{ constructor
  89.  
  90.     /**
  91.      * Constructor
  92.      */
  93.     function Net_NNTP_Realtime()
  94.     {
  95.     parent::Net_NNTP_Protocol();
  96.     }
  97.  
  98.     // }}}
  99.     // {{{ connect()
  100.  
  101.     /**
  102.      * Connect to the NNTP-server.
  103.      *
  104.      * @param optional string $host The adress of the NNTP-server to connect to.
  105.      * @param optional int $port The port to connect to.
  106.      *
  107.      * @return mixed (bool) true on success or (object) pear_error on failure
  108.      * @access public
  109.      * @see Net_NNTP::quit()
  110.      * @see Net_NNTP::authenticate()
  111.      * @see Net_NNTP::connectAuthenticated()
  112.      */
  113.     function connect($host = NET_NNTP_PROTOCOL_DEFAULT_HOST,
  114.                      $port = NET_NNTP_PROTOCOL_DEFAULT_PORT)
  115.     {
  116.     return parent::connect($host$port);
  117.     }
  118.  
  119.     // }}}
  120.     // {{{ connectAuthenticated()
  121.  
  122.     /**
  123.      * Connect to the NNTP-server, and authenticate using given username and password.
  124.      *
  125.      * @param optional string $user The username.
  126.      * @param optional string $pass The password.
  127.      * @param optional string $host The IP-address of the NNTP-server to connect to.
  128.      * @param optional int $port The port to connect to.
  129.      * @param optional string $authmode The authentication mode.
  130.      *
  131.      * @return mixed (bool) true on success or (object) pear_error on failure
  132.      * @access public
  133.      * @since 0.3
  134.      * @see Net_NNTP::connect()
  135.      * @see Net_NNTP::authenticate()
  136.      * @see Net_NNTP::quit()
  137.      */
  138.     function connectAuthenticated($user = null,
  139.                           $pass = null,
  140.                   $host = NET_NNTP_PROTOCOL_DEFAULT_HOST,
  141.                           $port = NET_NNTP_PROTOCOL_DEFAULT_PORT,
  142.                           $authmode = NET_NNTP_AUTHORIGINAL)
  143.     {
  144.     $R $this->connect($host$port);
  145.     if (PEAR::isError($R)) {
  146.         return $R;
  147.     }
  148.  
  149.     // Authenticate if username is given
  150.     if ($user != null{
  151.             $R $this->authenticate($user$pass$authmode);
  152.             if (PEAR::isError($R)) {
  153.             return $R;
  154.             }
  155.     }
  156.  
  157.         return true;
  158.     }
  159.  
  160.     // }}}
  161.     // {{{ quit()
  162.  
  163.     /**
  164.      * Close connection to the newsserver
  165.      *
  166.      * @access public
  167.      * @see Net_NNTP::connect()
  168.      */
  169.     function quit()
  170.     {
  171.         return $this->cmdQuit();
  172.     }
  173.  
  174.     // }}}
  175.     // {{{ authenticate()
  176.  
  177.     /**
  178.      * Authenticate
  179.      * 
  180.      * Auth process (not yet standarized but used any way)
  181.      * http://www.mibsoftware.com/userkt/nntpext/index.html
  182.      *
  183.      * @param string $user The username
  184.      * @param optional string $pass The password
  185.      * @param optional string $mode The authentication mode (original, simple, generic).
  186.      *
  187.      * @return mixed (bool) true on success or (object) pear_error on failure
  188.      * @access public
  189.      * @see Net_NNTP::connect()
  190.      * @see Net_NNTP::connectAuthenticated()
  191.      */
  192.     function authenticate($user$pass$mode = NET_NNTP_AUTHORIGINAL)
  193.     {
  194.         // Username is a must...
  195.         if ($user == null{
  196.             return PEAR::throwError('No username supplied'null);
  197.         }
  198.  
  199.         // Use selected authentication method
  200.         switch ($mode{
  201.             case NET_NNTP_AUTHORIGINAL:
  202.                 return $this->cmdAuthinfo($user$pass);
  203.                 break;
  204.             case NET_NNTP_AUTHSIMPLE:
  205.                 return $this->cmdAuthinfoSimple($user$pass);
  206.                 break;
  207.             case NET_NNTP_AUTHGENERIC:
  208.                 return $this->cmdAuthinfoGeneric($user$pass);
  209.                 break;
  210.             default:
  211.                 return PEAR::throwError("The auth mode: '$mode' is unknown"null);
  212.         }
  213.     }
  214.  
  215.     // }}}
  216.     // {{{ isConnected()
  217.  
  218.     /**
  219.      * Test whether a connection is currently open.
  220.      *
  221.      * @return bool true or false
  222.      * @access public
  223.      * @see Net_NNTP::connect()
  224.      * @see Net_NNTP::quit()
  225.      */
  226.     function isConnected()
  227.     {
  228.         return parent::isConnected();
  229.     }
  230.  
  231.     // }}}
  232.     // {{{ selectGroup()
  233.  
  234.     /**
  235.      * Selects a newsgroup
  236.      *
  237.      * @param string $newsgroup Newsgroup name
  238.      *
  239.      * @return mixed (array) Info about the newsgroup on success or (object) pear_error on failure
  240.      * @access public
  241.      * @see Net_NNTP::group()
  242.      * @see Net_NNTP::first()
  243.      * @see Net_NNTP::last()
  244.      * @see Net_NNTP::count()
  245.      * @see Net_NNTP::getGroups()
  246.      */
  247.     function selectGroup($newsgroup)
  248.     {
  249.         $response_arr $this->cmdGroup($newsgroup);
  250.         if (PEAR::isError($response_arr)) {
  251.         return $response_arr;
  252.     }
  253.  
  254.     // Store group info in the object
  255.     $this->_currentGroup $response_arr;
  256.  
  257.     return $response_arr;
  258.     }
  259.  
  260.     // }}}
  261.     // {{{ getGroups()
  262.  
  263.     /**
  264.      * Fetches a list of all avaible newsgroups
  265.      *
  266.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  267.      * @access public
  268.      * @see Net_NNTP::selectGroup()
  269.      * @see Net_NNTP::getDescriptions()
  270.      */
  271.     function getGroups()
  272.     {
  273.     // Get groups
  274.     $groups $this->cmdList();
  275.     if (PEAR::isError($groups)) {
  276.         return $groups;
  277.     }
  278.  
  279.     return $groups;
  280.     }
  281.  
  282.     // }}}
  283.     // {{{ getDescriptions()
  284.  
  285.     /**
  286.      * Fetches a list of all avaible newsgroup descriptions.
  287.      *
  288.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  289.      * @access public
  290.      * @see Net_NNTP::getGroups()
  291.      */
  292.     function getDescriptions()
  293.     {
  294.  
  295.     // Get group descriptions
  296.     $descriptions $this->cmdListNewsgroups();
  297.     if (PEAR::isError($descriptions)) {
  298.         return $descriptions;
  299.     }
  300.     
  301.     return $descriptions;
  302.     }
  303.  
  304.     // }}}
  305.     // {{{ getOverview()
  306.  
  307.     /**
  308.      * Fetch message header fields from message number $first to $last
  309.      *
  310.      * The format of the returned array is:
  311.      * $messages[message_id][header_name]
  312.      *
  313.      * @param integer $first first article to fetch
  314.      * @param integer $last  last article to fetch
  315.      *
  316.      * @return mixed (array) nested array of message and their headers on success or (object) pear_error on failure
  317.      * @access public
  318.      * @see Net_NNTP::getOverviewFormat()
  319.      * @see Net_NNTP::getReferencesOverview()
  320.      */
  321.     function getOverview($first$last)
  322.     {
  323.     $overview $this->cmdXOver($first$last);
  324.     if (PEAR::isError($overview)) {
  325.         return $overview;
  326.     }
  327.     
  328.     return $overview;
  329.     }
  330.  
  331.     // }}}
  332.     // {{{ getOverviewFmt()
  333.  
  334.     /**
  335.      * Returns a list of avaible headers which are send from NNTP-server to the client for every news message
  336.      *
  337.      * @return mixed (array) header names on success or (object) pear_error on failure
  338.      * @access public
  339.      * @see Net_NNTP::getOverview()
  340.      */
  341.     function getOverviewFormat()
  342.     {
  343.     return $this->cmdListOverviewFmt();
  344.     }
  345.  
  346.     // }}}
  347.     // {{{ getReferencesOverview()
  348.  
  349.     /**
  350.      * Fetch a list of each message's reference header.
  351.      *
  352.      * @param integer $first first article to fetch
  353.      * @param integer $last  last article to fetch
  354.      *
  355.      * @return mixed (array) nested array of references on success or (object) pear_error on failure
  356.      * @access public
  357.      * @see Net_NNTP::getOverview()
  358.      */
  359.     function getReferencesOverview($first$last)
  360.     {
  361.     $overview $this->cmdXROver($first$last);
  362.     if (PEAR::isError($overview)) {
  363.         return $overview;
  364.     }
  365.     
  366.     return $overview;
  367.     }
  368.  
  369.     // }}}
  370.     // {{{ post()
  371.  
  372.     /**
  373.      * Post an article to a number of newsgroups.
  374.      *
  375.      * (Among the aditional headers you might think of adding could be:
  376.      * "NNTP-Posting-Host: <ip-of-author>", which should contain the IP-address
  377.      * of the author of the post, so the message can be traced back to him.
  378.      * Or "Organization: <org>" which contain the name of the organization
  379.      * the post originates from)
  380.      *
  381.      * @param string $newsgroups The newsgroup to post to.
  382.      * @param string $subject The subject of the post.
  383.      * @param string $body The body of the post itself.
  384.      * @param string $from Name + email-adress of sender.
  385.      * @param optional string $aditional Aditional headers to send.
  386.      *
  387.      * @return mixed (string) server response on success or (object) pear_error on failure
  388.      * @access public
  389.      */
  390.     function post($newsgroups$subject$body$from$aditional '')
  391.     {
  392.     return $this->cmdPost($newsgroups$subject$body$from$aditional);
  393.     }
  394.  
  395.     // }}}
  396.     // {{{ getArticle()
  397.  
  398.     /**
  399.      * Get an article
  400.      *
  401.      * The v0.2 version of the this function (which returned the article as a string) has been renamed to getArticleRaw().
  402.      *
  403.      * @param mixed $article Either the message-id or the message-number on the server of the article to fetch.
  404.      *
  405.      * @return mixed (object) message object on success or (object) pear_error on failure
  406.      * @access public
  407.      * @see Net_NNTP::getArticleRaw()
  408.      * @see Net_NNTP::getHeader()
  409.      * @see Net_NNTP::getBody()
  410.      */
  411.     function getArticle($article)
  412.     {
  413.         $message $this->getArticleRaw($articlefalse);
  414.         if (PEAR::isError($message)) {
  415.         return $data;
  416.     }
  417.     
  418.     $M Net_NNTP_Message::create($message);
  419.     
  420.     return $M;
  421.     }
  422.  
  423.     // }}}
  424.     // {{{ getArticleRaw()
  425.  
  426.     /**
  427.      * Get a article (raw data)
  428.      *
  429.      * @param mixed $article Either the message-id or the message-number on the server of the article to fetch.
  430.      * @param optional bool  $implode When true the result array is imploded to a string, defaults to false.
  431.      *
  432.      * @return mixed (array/string) The article on success or (object) pear_error on failure
  433.      * @access public
  434.      * @see Net_NNTP::getArticle()
  435.      * @see Net_NNTP::getHeaderRaw()
  436.      * @see Net_NNTP::getBodyRaw()
  437.      */
  438.     function getArticleRaw($article$implode = false)
  439.     {
  440.         $data $this->cmdArticle($article);
  441.         if (PEAR::isError($data)) {
  442.         return $data;
  443.     }
  444.  
  445.     if ($implode == true{
  446.         $data implode("\r\n"$data);
  447.     }
  448.  
  449.     return $data;
  450.     }
  451.  
  452.     // }}}
  453.     // {{{ getHeader()
  454.  
  455.     /**
  456.      * Get the header of an article
  457.      *
  458.      * @param mixed $article Either the (string) message-id or the (int) message-number on the server of the article to fetch.
  459.      *
  460.      * @return mixed (object) header object on success or (object) pear_error on failure
  461.      * @access public
  462.      * @see Net_NNTP::getHeaderRaw()
  463.      * @see Net_NNTP::getArticle()
  464.      * @see Net_NNTP::getBody()
  465.      */
  466.     function getHeader($article)
  467.     {
  468.         $header $this->getHeaderRaw($articlefalse);
  469.         if (PEAR::isError($header)) {
  470.         return $header
  471. ;
  472.     }
  473.  
  474.     $H Net_NNTP_Header::create($header);
  475.  
  476.     return $H;
  477.     }
  478.  
  479.     // }}}
  480.     // {{{ getHeaderRaw()
  481.  
  482.     /**
  483.      * Get the header of an article (raw data)
  484.      *
  485.      * @param mixed $article Either the (string) message-id or the (int) message-number on the server of the article to fetch.
  486.      * @param optional bool $implode When true the result array is imploded to a string, defaults to false.
  487.      *
  488.      * @return mixed (array/string) header fields on success or (object) pear_error on failure
  489.      * @access public
  490.      * @see Net_NNTP::getHeader()
  491.      * @see Net_NNTP::getArticleRaw()
  492.      * @see Net_NNTP::getBodyRaw()
  493.      */
  494.     function getHeaderRaw($article$implode = false)
  495.     {
  496.         $data $this->cmdHead($article);
  497.         if (PEAR::isError($data)) {
  498.         return $data;
  499.     }
  500.  
  501.     if ($implode == true{
  502.         $data implode("\r\n"$data);
  503.     }
  504.  
  505.     return $data;
  506.     }
  507.  
  508.     // }}}
  509.     // {{{ getBody()
  510.  
  511.     // Not written yet...
  512.  
  513.     // }}}
  514.     // {{{ getBodyRaw()
  515.  
  516.     /**
  517.      * Get the body of an article (raw data)
  518.      *
  519.      * @param mixed $article Either the message-id or the message-number on the server of the article to fetch.
  520.      * @param optional bool $implode When true the result array is imploded to a string, defaults to false.
  521.      *
  522.      * @return mixed (array/string) body on success or (object) pear_error on failure
  523.      * @access public
  524.      * @see Net_NNTP::getBody()
  525.      * @see Net_NNTP::getHeaderRaw()
  526.      * @see Net_NNTP::getArticleRaw()
  527.      */
  528.     function getBodyRaw($article$implode = false)
  529.     {
  530.         $data $this->cmdBody($article);
  531.         if (PEAR::isError($data)) {
  532.         return $data;
  533.     }
  534.     
  535.     if ($implode == true{
  536.         $data implode("\r\n"$data);
  537.     }
  538.     
  539.     return $data;
  540.     }
  541.  
  542.     // }}}
  543.     // {{{ getGroupArticles()
  544.  
  545.     /**
  546.      * Experimental
  547.      *
  548.      * @access public
  549.      * @since 0.3
  550.      */
  551.     function getGroupArticles($newsgroup)
  552.     {
  553.         return $this->cmdListgroup($newsgroup);
  554.     }
  555.  
  556.     // }}}
  557.     // {{{ getNewGroups()
  558.  
  559.     /**
  560.      * Experimental
  561.      *
  562.      * @access public
  563.      * @since 0.3
  564.      */
  565.     function getNewGroups($time)
  566.     {
  567.     switch (gettype($time)) {
  568.         case 'integer':
  569.         break;
  570.         case 'string':
  571.         $time = (int) strtotime($time);
  572.         break;
  573.         default:
  574.             return PEAR::throwError('');
  575.     }
  576.  
  577.     return $this->cmdNewgroups($time);
  578.     }
  579.  
  580.     // }}}
  581.     // {{{ getNewNews()
  582.  
  583.     /**
  584.      * Experimental
  585.      *
  586.      * @access public
  587.      * @since 0.3
  588.      */
  589.     function getNewNews($time$newsgroups '*')
  590.     {
  591.     switch (gettype($time)) {
  592.         case 'integer':
  593.         break;
  594.         case 'string':
  595.         $time = (int) strtotime($time);
  596.         break;
  597.         default:
  598.             return PEAR::throwError('UPS...');
  599.     }
  600.  
  601.     return $this->cmdNewnews($time$newsgroups);
  602.     }
  603.  
  604.     // }}}
  605.     // {{{ getDate()
  606.  
  607.     /**
  608.      * Get the NNTP-server's internal date
  609.      *
  610.      * Get the date from the newsserver format of returned date:
  611.      *
  612.      * @param optional int $format
  613.      *   - 0: $date - timestamp
  614.      *   - 1: $date['y'] - year
  615.      *        $date['m'] - month
  616.      *        $date['d'] - day
  617.      *
  618.      * @return mixed (mixed) date on success or (object) pear_error on failure
  619.      * @access public
  620.      * @since 0.3
  621.      */
  622.     function getDate($format = 1)
  623.     {
  624.         $date $this->cmdDate();
  625.         if (PEAR::isError($date)) {
  626.         return $date;
  627.     }
  628.  
  629.     switch ($format{
  630.         case 1:
  631.             return array('y' => substr($date04)'m' => substr($date42)'d' => substr($date62));
  632.             break;
  633.  
  634.         case 0:
  635.         default:
  636.             return $date;
  637.             break;
  638.     }
  639.     }
  640.  
  641.     // }}}
  642.     // {{{ count()
  643.  
  644.     /**
  645.      * Number of articles in currently selected group
  646.      *
  647.      * @return integer number of article in group
  648.      * @access public
  649.      * @since 0.3
  650.      * @see Net_NNTP::group()
  651.      * @see Net_NNTP::first()
  652.      * @see Net_NNTP::last()
  653.      * @see Net_NNTP::selectGroup()
  654.      */
  655.     function count()
  656.     {
  657.         return $this->_currentGroup['count'];
  658.     }
  659.  
  660.     // }}}
  661.     // {{{ last()
  662.  
  663.     /**
  664.      * Maximum article number in currently selected group
  665.      *
  666.      * @return integer number of last article
  667.      * @access public
  668.      * @since 0.3
  669.      * @see Net_NNTP::first()
  670.      * @see Net_NNTP::group()
  671.      * @see Net_NNTP::count()
  672.      * @see Net_NNTP::selectGroup()
  673.      */
  674.     function last()
  675.     {
  676.     return $this->_currentGroup['last'];
  677.     }
  678.  
  679.     // }}}
  680.     // {{{ first()
  681.  
  682.     /**
  683.      * Minimum article number in currently selected group
  684.      *
  685.      * @return integer number of first article
  686.      * @access public
  687.      * @since 0.3
  688.      * @see Net_NNTP::last()
  689.      * @see Net_NNTP::group()
  690.      * @see Net_NNTP::count()
  691.      * @see Net_NNTP::selectGroup()
  692.      */
  693.     function first()
  694.     {
  695.     return $this->_currentGroup['first'];
  696.     }
  697.  
  698.     // }}}
  699.     // {{{ group()
  700.  
  701.     /**
  702.      * Currently selected group
  703.      *
  704.      * @return string group name
  705.      * @access public
  706.      * @since 0.3
  707.      * @see Net_NNTP::first()
  708.      * @see Net_NNTP::last()
  709.      * @see Net_NNTP::count()
  710.      * @see Net_NNTP::selectGroup()
  711.      */
  712.     function group()
  713.     {
  714.     return $this->_currentGroup['group'];
  715.     }
  716.  
  717.     // }}}
  718.     // {{{ command()
  719.  
  720.     /**
  721.      * Issue a command to the NNTP server
  722.      *
  723.      * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
  724.      *
  725.      * @return mixed (int) response code on success or (object) pear_error on failure
  726.      * @access public
  727.      */
  728.     function command($cmd)
  729.     {
  730.         return $this->_sendCommand($cmd);
  731.     }
  732.  
  733.     // }}}
  734.  
  735. }
  736. ?>

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