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

Source for file Server.php

Documentation is available at Server.php

  1. <?php
  2.  
  3. /**
  4.  * Object-oriented CDDB Server components
  5.  * 
  6.  * Alpha-quality CDDB server capable of serving CDDB requests over a variety of
  7.  * CDDB protocol options (HTTP only for now, more coming soon) and using a
  8.  * variety of data sources.
  9.  * 
  10.  * @see Net_CDDB_Client
  11.  * 
  12.  * @author Keith Palmer <Keith@UglySlug.com>
  13.  * @category Net
  14.  * @package Net_CDDB
  15.  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  16.  */
  17.  
  18. /**
  19.  * Need the constants from this file...
  20.  */
  21. require_once 'Net/CDDB.php';
  22.  
  23. /**
  24.  * A hook that runs when the server receives the request
  25.  * 
  26.  * The function signature for request hooks should look like this:
  27.  * <code>
  28.  * function your_request_hook(&$arr_a_list_of_cddb_commands_passed_in_the_request)
  29.  * {
  30.  *     ...
  31.  *     // return true; (if you want the CDDB server to not run any other request hooks)
  32.  *     // return false; (if you want the CDDB server to continue to run the other request hooks)
  33.  * }
  34.  * </code>
  35.  * 
  36.  * @var string 
  37.  */
  38. define('NET_CDDB_SERVER_HOOK_REQUEST''request');
  39.  
  40. /**
  41.  * A hook that runs whenever the server responds to a request
  42.  * 
  43.  * The function signature for response hooks should look like this:
  44.  * <code>
  45.  * funtion your_response_hook($cmd, &$int_response_code, &$str_response_message, &$str_any_data_returned_with_request, &$bln_whether_or_not_to_add_a_period_as_a_termination_character)
  46.  * {
  47.  *     ...
  48.  *     // return true; (if you want the CDDB server to not run any other response hooks)
  49.  *     // return false; (if you want the CDDB server to continue to run the other response hooks)
  50.  * }
  51.  * </code>
  52.  * 
  53.  * @var string 
  54.  */
  55. define('NET_CDDB_SERVER_HOOK_RESPONSE''response');
  56.  
  57. /**
  58.  * A hook that runs for each command the CDDB server recieves
  59.  * 
  60.  * The function signature for response hooks should look like this:
  61.  * <code>
  62.  * function your_command_hook(&$str_the_cddb_command)
  63.  * {
  64.  *     ...
  65.  *     // return true; (if you want the CDDB server to not run any other command hooks)
  66.  *     // return false; (if you want the CDDB server to continue to run the other command hooks)
  67.  * }
  68.  * </code>
  69.  * 
  70.  * @var string 
  71.  */
  72. define('NET_CDDB_SERVER_HOOK_COMMAND''command');
  73.  
  74. /**
  75.  * CDDB Server (ALPHA QUALITY, *USE AT YOUR OWN RISK!*)
  76.  * 
  77.  * *** WARNING ***
  78.  * Use at your own risk, it doesn't work very well yet!
  79.  * 
  80.  * Current features:
  81.  *     - CDDB protocol level 5
  82.  *     - Multiple data sources: tell the server to read from a local filesystem, a remote HTTP CDDB server, a remote CDDBP server, a database (coming soon) etc.
  83.  *     - Supports about half of the CDDB commands
  84.  *     - Write and register your own 'hooks' which run when the CDDB server responds to commands
  85.  * 
  86.  * Known issues:
  87.  *     - Extremelly lightly tested (use at your own risk!)
  88.  *     - Command: 'motd' reports and incorrect last modified date
  89.  *     - Lack of any error reporting...
  90.  *     - Lack of any login/hello command support
  91.  *     - Only supports protocol level 5
  92.  *     - Only provides HTTP request support (no CDDBP or SMTP... yet)
  93.  * 
  94.  * You can write your own custom 'hooks' that get run a certain stages of the
  95.  * CDDB request/response process and register them with the ->registerHook()
  96.  * method. You can register multiple hooks at each stage of the process, and
  97.  * they will get run in the order you register them in.
  98.  * 
  99.  * Hooks should receive passed parameters by reference if they intend to modify
  100.  * commands/requests/responses. Hooks should return either a four element array
  101.  * which will be treated as the response (as below) or void if the server should
  102.  * continue to process the response.
  103.  * 
  104.  * <code>
  105.  * $my_custom_response = array(
  106.  *     NET_CDDB_RESPONSE_OK, // This could actually be *any* valid response code
  107.  *     'status message to be returned with the response code',
  108.  *     'any data the response returns (a cddb record, a list of matching records, a list of mirror sites, etc.)',
  109.  *     true, // true if a '.' should be appended to the response as a terminating character, false otherwise
  110.  *     );
  111.  * </code>
  112.  *  
  113.  * @todo Probably have the Net_CDDB_Request_* classes have three methods: getHello(), getProto(), and getCmd() or something
  114.  * 
  115.  * @package Net_CDDB
  116.  */
  117. class Net_CDDB_Server extends Net_CDDB
  118. {
  119.     /**
  120.      * Net_CDDB_Request_* object instance to use for listening for requests
  121.      * 
  122.      * @access protected
  123.      * @var object 
  124.      */
  125.     var $_request;
  126.     
  127.     /**
  128.      * Net_CDDB_Protocol_* object instance to use for data access
  129.      * 
  130.      * @access protected
  131.      * @var object 
  132.      */
  133.     var $_protocol;
  134.     
  135.     /*var $_connect_logging;
  136.     
  137.     var $_use_auth_file;
  138.     var $_auth_file;
  139.     var $_use_auth_mysql;
  140.     var $_auth_method;
  141.     */
  142.     
  143.     /**
  144.      * An array of function names to call as request hooks
  145.      * 
  146.      * @access protected
  147.      * @var array 
  148.      */
  149.     var $_hook_request;
  150.     
  151.     /**
  152.      * An array of function names to call as command hooks
  153.      * 
  154.      * @access protected
  155.      * @var array 
  156.      */
  157.     var $_hook_cmds;
  158.     
  159.     /**
  160.      * An arra of function names to call as response hooks
  161.      * 
  162.      * @access protected
  163.      * @var array 
  164.      */
  165.     var $_hook_resp;
  166.     
  167.     /**
  168.      * Constructor (PHP v4.x)
  169.      * 
  170.      * @access public
  171.      * @see Net_CDDB_Server::__construct()
  172.      */
  173.     function Net_CDDB_Server($request_dsn$protocol_dsn$options = array())
  174.     {
  175.         $this->__construct($request_dsn$protocol_dsn$options);
  176.     }
  177.     
  178.     /**
  179.      * Constructor (PHP v5.x)
  180.      * 
  181.      * @access public
  182.      * 
  183.      * @param string $request_dsn 
  184.      * @param string $protocol_dsn 
  185.      * @param array $options 
  186.      */
  187.     function __construct($request_dsn$protocol_dsn$options = array())
  188.     {
  189.         $this->_protocol = $this->_createProtocol($protocol_dsn$options);
  190.         $this->_request = $this->_createRequest($request_dsn$options);
  191.         
  192.         $this->_hook_request = array();
  193.         $this->_hook_cmds = array();
  194.         $this->_hook_resp = array();
  195.     }
  196.     
  197.     /**
  198.      * Create a Net_CDDB_Request object instance
  199.      * 
  200.      * @access protected
  201.      * 
  202.      * @param string $dsn 
  203.      * @param array $options 
  204.      * @return Net_CDDB_Request Returns an instance of a subclass of Net_CDDB_Request (i.e: Net_CDDB_Request_HTTP instance)
  205.      */
  206.     function _createRequest($dsn$options)
  207.     {
  208.         $parse parse_url($dsn);
  209.         
  210.         $file ucfirst(strtolower($parse['scheme']));
  211.         $class 'Net_CDDB_Request_' $file;
  212.         
  213.         /**
  214.          * Require the file the protocol class is stored in
  215.          */
  216.         include_once 'Net/CDDB/Request/' $file '.php';
  217.         
  218.         if (class_exists($class)) {
  219.             return new $class($dsn$options);
  220.         else {
  221.             return PEAR::raiseError('Could not find request file for: ' $file);
  222.         }
  223.     }
  224.     
  225.     /**
  226.      * Start the CDDB server
  227.      * 
  228.      * @access public
  229.      * @todo Listen forever support (daemon mode)
  230.      * 
  231.      * @return boolean 
  232.      */
  233.     function start()
  234.     {
  235.         if (PEAR::isError($this->_request)) {
  236.             return $this->_request;
  237.         }
  238.         
  239.         if (PEAR::isError($this->_protocol)) {
  240.             return $this->_request;
  241.         }
  242.         
  243.         // Implemented commands
  244.         $impl_cmds = array
  245.             'cddb lscat'     => '_cddbLscat'
  246.             'cddb hello'     => '_cddbHello'
  247.             'cddb query'     => '_cddbQuery',
  248.             'proto'         => '_proto',
  249.             'cddb read'     => '_cddbRead'
  250.             'motd'             => '_motd'
  251.             'ver'             => '_ver',
  252.             'stat'             => '_stat',  
  253.             );
  254.         
  255.         if ($this->_request->mode(== NET_CDDB_REQUEST_ONCE// Listen just once (HTTP for instance)
  256.             
  257.             // Read data from request listener
  258.             $commands $this->_request->listen();
  259.             
  260.             if (!$this->_doRequestHooks($commands)) {
  261.                 return true;
  262.             }
  263.             
  264.             if (!count($commands)) {
  265.                 $this->_request->respond(NET_CDDB_RESPONSE_ERROR_SYNTAX'Command syntax error: incorrect number of arguments.');
  266.                 return true;
  267.             }
  268.             
  269.             foreach ($commands as $cmd)
  270.             {
  271.                 if (!$this->_doCommandHooks($cmd)) {
  272.                     continue;
  273.                 }
  274.                 
  275.                 // Parse command from command parameters
  276.                 $tmp explode(' '$cmd);
  277.                 if (current($tmp== 'cddb' and count($tmp>= 2{
  278.                     $cmd 'cddb ' next($tmp);
  279.                     $query implode(' 'array_slice($tmp2));
  280.                 else {
  281.                     $cmd current($tmp);
  282.                     $query implode(' 'array_slice($tmp1));
  283.                 }
  284.                 
  285.                 if (!strlen($cmd)) {
  286.                     // Empty command...?
  287.                     
  288.                     $status NET_CDDB_RESPONSE_ERROR_EMPTY;
  289.                     $message 'Empty command input.';
  290.                     $data '';
  291.                     $term = false;
  292.                     
  293.                     if ($this->_doResponseHooks($cmd$status$message$data$term)) {
  294.                         $this->_request->respond($status$message);
  295.                     }
  296.                     
  297.                 else if (isset($impl_cmds[$cmd]and method_exists($this$impl_cmds[$cmd])) {
  298.                     // Call the appropriate command handler, and then respond
  299.                     
  300.                     $status NET_CDDB_RESPONSE_SERVER_ERROR;
  301.                     $message '';
  302.                     $data '';
  303.                     $term = false;
  304.                     
  305.                     if ($this->{$impl_cmds[$cmd]}($cmd$query$status$message$data$term)) {
  306.                         if ($this->_doResponseHooks($cmd$status$message$data$term)) {
  307.                             $this->_request->respond($status$message$data$term);
  308.                         }
  309.                     }
  310.                     
  311.                 else {
  312.                     // Respond indicating the command could not be found
  313.                     
  314.                     $status NET_CDDB_RESPONSE_ERROR_UNRECOGNIZED;
  315.                     $message 'Unrecognized command.';
  316.                     $data '';
  317.                     $term = false;
  318.                     
  319.                     if ($this->_doResponseHooks($cmd$status$message$data$term))
  320.                     {
  321.                         $this->_request->respond($status$message)// Unrecognized command...?
  322.                     }
  323.                 }
  324.             }
  325.             
  326.             return true;
  327.             
  328.         else {
  329.             // listen forever, not implemented yet
  330.                 }
  331.     }
  332.     
  333.     /**
  334.      * Register a hook function
  335.      * 
  336.      * See the example file or the class description for more details. Custom
  337.      * hooks can be used to hook into CDDB server requests and implement custom
  338.      * behaviors or actions.
  339.      * 
  340.      * @access public
  341.      * @param char $when 
  342.      * @param function $function 
  343.      * @return boolean 
  344.      */
  345.     function registerHook($when$function)
  346.     {
  347.         if (function_exists($function)) {
  348.             switch ($when{
  349.                 case NET_CDDB_SERVER_HOOK_REQUEST:
  350.                     $this->_hook_request[$function;
  351.                     break;
  352.                 case NET_CDDB_SERVER_HOOK_COMMAND:
  353.                     $this->_hook_cmds[$function;
  354.                     break;
  355.                 case NET_CDDB_SERVER_HOOK_RESPONSE:
  356.                     $this->_hook_resp[$function;
  357.                     break;
  358.                 default:
  359.                     return false;
  360.             }
  361.             
  362.             return true;
  363.         }
  364.         
  365.         return false;
  366.     }
  367.     
  368.     /**
  369.      * Execute the command hooks
  370.      * 
  371.      * @access protected
  372.      * @param string $cmd 
  373.      * @return boolean 
  374.      */
  375.     function _doCommandHooks(&$cmd)
  376.     {
  377.         foreach ($this->_hook_cmds as $hook{
  378.             if ($arr $hook($cmdand is_array($arrand count($arr== 4{
  379.                 // We still need to run the response hooks though...
  380.                 $this->_doResponseHooks($cmd$arr[0]$arr[1]$arr[2]$arr[3]);
  381.                 
  382.                 // Respond...
  383.                 $this->_request->respond($arr[0]$arr[1]$arr[2]$arr[3]);
  384.                 return false;
  385.             }
  386.         }
  387.         
  388.         return true;
  389.     }
  390.     
  391.     /**
  392.      * Execute the response hooks
  393.      * 
  394.      * @access protected
  395.      * @param string $cmd 
  396.      * @param integer $status 
  397.      * @param string $message 
  398.      * @param string $data 
  399.      * @param boolean $term 
  400.      * @return boolean 
  401.      */
  402.     function _doResponseHooks($cmd&$status&$message&$data&$term)
  403.     {
  404.         foreach ($this->_hook_resp as $hook{
  405.             if ($arr $hook($cmd$status$message$data$termand is_array($arrand count($arr== 4{
  406.                 $this->_request->respond($arr[0]$arr[1]$arr[2]$arr[3]);
  407.                 return false;
  408.             }
  409.         }
  410.         
  411.         return true;
  412.     }
  413.     
  414.     /**
  415.      * Execute the request hooks
  416.      * 
  417.      * @access protected
  418.      * @param array $arr_cmds 
  419.      * @return boolean 
  420.      */
  421.     function _doRequestHooks(&$arr_cmds)
  422.     {
  423.         foreach ($this->_hook_request as $hook{
  424.             if ($arr $hook($arr_cmdsand is_array($arrand count($arr== 4{
  425.                 $this->_request->respond($arr[0]$arr[1]$arr[2]$arr[3]);
  426.                 return false;
  427.             }
  428.         }
  429.         
  430.         return true;
  431.     }
  432.     
  433.     /**
  434.      * Dummy placeholder for the 'cddb proto' command
  435.      * 
  436.      * @access protected
  437.      * 
  438.      * @return void 
  439.      */
  440.     function _proto()
  441.     {
  442.         return false;
  443.     }
  444.     
  445.     /**
  446.      * Dummy placeholder for 'cddb hello' command
  447.      * 
  448.      * @access protected
  449.      * 
  450.      * @return void 
  451.      */
  452.     function _cddbHello()
  453.     {
  454.         return false;    
  455.     }
  456.     
  457.     /**
  458.      * Respond to the 'cddb lscat' CDDB command (list categories)
  459.      * 
  460.      * @access protected
  461.      * 
  462.      * @param string $cmd 
  463.      * @param string $query 
  464.      * @return void 
  465.      */
  466.     function _cddbLscat($cmd$query&$status&$message&$data&$term)
  467.     {
  468.         $headers = array(
  469.             NET_CDDB_RESPONSE_OK_FOLLOWS => 'OK, category list follows (until terminating `.\')',
  470.             );
  471.         
  472.         $this->_protocol->send('cddb lscat');
  473.         $resp_body $this->_protocol->recieve();
  474.         $resp_stat $this->_protocol->status();
  475.         
  476.         switch ($resp_stat{
  477.             case NET_CDDB_RESPONSE_OK_FOLLOWS:
  478.                 $status $resp_stat;
  479.                 $message $headers[$status];
  480.                 $data $resp_body;
  481.                 $term = true;
  482.                 break;
  483.             default:
  484.                 $status NET_CDDB_RESPONSE_SERVER_ERROR;
  485.                 $message 'Internal server error.';
  486.                 break;
  487.         }
  488.         
  489.         return true;
  490.     }
  491.     
  492.     /**
  493.      * Respond to the 'cddb read ...' CDDB command
  494.      * 
  495.      * @access protected
  496.      * 
  497.      * @param string $cmd 
  498.      * @param string $query 
  499.      * @return void 
  500.      */
  501.     function _cddbRead($cmd$query&$status&$message&$data&$term)
  502.     {
  503.         $headers = array(
  504.             NET_CDDB_RESPONSE_OK_FOLLOWS => $query ' CD database entry follows (until terminating `.\')'
  505.             );
  506.         
  507.         $this->_protocol->send($cmd ' ' $query);
  508.         $resp_body $this->_protocol->recieve();
  509.         $resp_stat $this->_protocol->status();
  510.         
  511.         switch ($resp_stat{
  512.             case NET_CDDB_RESPONSE_OK_FOLLOWS:
  513.                 $status $resp_stat;
  514.                 $message $headers[$status];
  515.                 $data $resp_body;
  516.                 $term = true;
  517.                 break;
  518.             default:
  519.                 $status NET_CDDB_RESPONSE_SERVER_ERROR;
  520.                 $message 'Internal server error.';
  521.                 break;
  522.         }
  523.         
  524.         return true;
  525.     }
  526.     
  527.     /**
  528.      * Respond to the 'cddb query ...' CDDB command
  529.      * 
  530.      * @access protected
  531.      * 
  532.      * @param string $cmd 
  533.      * @param string $query 
  534.      * @return void 
  535.      */
  536.     function _cddbQuery($cmd$query&$status&$message&$data&$term)
  537.     {
  538.         $this->_protocol->send($cmd ' ' $query);
  539.         $resp_body $this->_protocol->recieve();
  540.         $resp_stat $this->_protocol->status();
  541.         
  542.         switch ($resp_stat{
  543.             case NET_CDDB_RESPONSE_OK:
  544.                 $status $resp_stat;
  545.                 $message $resp_body;
  546.                 break;
  547.             default:
  548.                 $status NET_CDDB_RESPONSE_SERVER_ERROR;
  549.                 $message 'Internal server error.';
  550.                 break;
  551.         }
  552.         
  553.         return true;
  554.     }
  555.     
  556.     /**
  557.      * Respond to the CDDB 'motd' command
  558.      * 
  559.      * @access protected
  560.      * @todo Find out the motd modified time somehow...?
  561.      * 
  562.      * @param string $cmd 
  563.      * @param string $query 
  564.      * @return void 
  565.      */
  566.     function _motd($cmd$query&$status&$message&$data&$term)
  567.     {
  568.         $headers = array(
  569.             NET_CDDB_RESPONSE_OK_FOLLOWS => 'Last modified: ' date('m/d/Y H:i:s'' MOTD follows (until terminating `.\')'
  570.             );
  571.         
  572.         $this->_protocol->send('motd');
  573.         $resp_body $this->_protocol->recieve();
  574.         $resp_stat $this->_protocol->status();
  575.         
  576.         switch ($resp_stat{
  577.             case NET_CDDB_RESPONSE_OK_FOLLOWS:
  578.                 $status $resp_stat;
  579.                 $message $headers[$status];
  580.                 $data $resp_body;
  581.                 $term = true;
  582.                 break;
  583.             default:
  584.                 $status NET_CDDB_RESPONSE_SERVER_ERROR;
  585.                 $message 'Internal server error.';
  586.                 break;
  587.         }
  588.         
  589.         return true;
  590.     }
  591.     
  592.     /**
  593.      * Respond to the CDDB 'ver' command
  594.      * 
  595.      * @access protected
  596.      * 
  597.      * @param string $cmd 
  598.      * @param string $query 
  599.      * @return void 
  600.      */
  601.     function _ver($cmd$query&$status&$message&$data&$term)
  602.     {
  603.         $this->_protocol->send('ver');
  604.         $resp_body $this->_protocol->recieve();
  605.         $resp_stat $this->_protocol->status();
  606.         
  607.         /*
  608.          * If the protocol we're using to get the data is a remote protocol 
  609.          * (i.e.: We're reading this data from a remote CDDB server via 
  610.          * HTTP/CDDBP instead of using a local copy of the CDDB db) then we'll 
  611.          * modify the version information to reflect the tunnelling of the 
  612.          * response through the Net_CDDB/PHP server. 
  613.          */ 
  614.         if ($this->_protocol->remote()) {
  615.             $resp_body .= ' (Tunnelled through PHP/PEAR/' get_class($this' v' NET_CDDB_VERSION . ')';
  616.         }
  617.         
  618.         switch ($resp_stat{
  619.             case NET_CDDB_RESPONSE_OK:
  620.                 $status $resp_stat;
  621.                 $message $resp_body;
  622.                 break;
  623.             default:
  624.                 $status NET_CDDB_RESPONSE_SERVER_ERROR;
  625.                 $messsage 'Internal server error.';
  626.                 break;
  627.         }
  628.         
  629.         return true;
  630.     }
  631.     
  632.     function _stat($cmd$query&$status&$message&$data&$term)
  633.     {
  634.         $this->_protocol->send('stat');
  635.         $resp_body $this->_protocol->recieve();
  636.         $resp_stat $this->_protocol->status();
  637.         
  638.         // Correct the interface stat header
  639.         $explode explode("\n"$resp_body);
  640.         foreach ($explode as $num => $line{
  641.             if (trim(current(explode(':'$line))) == 'interface'{
  642.                 $explode[$num'    interface: ' $this->_request->face();
  643.             }
  644.         }
  645.         $resp_body implode("\n"$explode);
  646.         
  647.         switch ($resp_stat)
  648.         {
  649.             case NET_CDDB_RESPONSE_OK_FOLLOWS:
  650.                 $status $resp_stat;
  651.                 $message 'OK, status information follows (until terminating `.\')';
  652.                 $data trim($resp_body);
  653.                 $term = true;
  654.                 break;
  655.             default:
  656.                 $status NET_CDDB_RESPONSE_SERVER_ERROR;
  657.                 $message 'Internal server error.';
  658.                 break;
  659.         }
  660.         
  661.         return true;
  662.     }
  663. }
  664.  
  665. ?>

Documentation generated on Sun, 25 Feb 2007 14:00:20 -0500 by phpDocumentor 1.3.0. PEAR Logo Copyright © PHP Group 2004.