diff options
-rwxr-xr-x | docs/CHANGELOG | 2 | ||||
-rw-r--r-- | lib/class/catalog.class.php | 124 | ||||
-rw-r--r-- | lib/class/song.class.php | 2 | ||||
-rw-r--r-- | lib/class/xmlrpcserver.class.php | 142 | ||||
-rw-r--r-- | lib/log.lib.php | 3 | ||||
-rw-r--r-- | lib/ui.lib.php | 2 | ||||
-rw-r--r-- | modules/xmlrpc/xmlrpc.inc | 9 | ||||
-rw-r--r-- | modules/xmlrpc/xmlrpcs.inc | 4 | ||||
-rw-r--r-- | server/xmlrpc.server.php | 50 | ||||
-rw-r--r-- | templates/show_edit_access.inc.php | 4 |
10 files changed, 241 insertions, 101 deletions
diff --git a/docs/CHANGELOG b/docs/CHANGELOG index ba3f4b8a..08e47e3a 100755 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -4,6 +4,8 @@ -------------------------------------------------------------------------- v.3.4-Alpha4 + - Fixed basic XML-RPC functionality, using insecure / old + authentication method needs more work - Fixed it so that all errors should return an XML document when using the XML API. - Added Basic ShoutBox functionality, needs formating fixes diff --git a/lib/class/catalog.class.php b/lib/class/catalog.class.php index ce5c3e0d..d0388477 100644 --- a/lib/class/catalog.class.php +++ b/lib/class/catalog.class.php @@ -223,7 +223,7 @@ class Catalog { $path = Dba::escape($data['path']); // Make sure the path is readable/exists - if (!is_readable($data['path'])) { + if (!is_readable($data['path']) AND $data['type'] == 'local') { Error::add('general','Error: ' . scrub_out($data['path']) . ' is not readable or does not exist'); return false; } @@ -271,6 +271,13 @@ class Catalog { */ public function run_add($options) { + if ($this->catalog_type == 'remote') { + show_box_top(_('Running Remote Sync') . '. . .'); + $this->get_remote_catalog($type=0); + show_box_bottom(); + return true; + } + // Catalog Add start $start_time = time(); @@ -1178,8 +1185,9 @@ class Catalog { public function add_to_catalog() { if ($this->catalog_type == 'remote') { - echo _('Running Remote Update') . ". . .<br />"; + show_box_top(_('Running Remote Update') . '. . .'); $this->get_remote_catalog($type=0); + show_box_bottom(); return true; } @@ -1242,14 +1250,10 @@ class Catalog { /** * get_remote_catalog - * get a remote catalog and runs update if needed - * @package XMLRPC - * @catagory Client - * @author Karl Vollmer - * @todo Add support for something besides port 80 - * @todo Add a Pub/Private Key swap in here for extra security + * get a remote catalog and runs update if needed this requires + * the XML RPC stuff and a key to be passed */ - function get_remote_catalog($type=0) { + public function get_remote_catalog($type=0) { /* Make sure the xmlrpc lib is loaded */ if (!class_exists('xmlrpc_client')) { @@ -1273,13 +1277,15 @@ class Catalog { /* encode the variables we need to send over */ $encoded_key = new xmlrpcval($this->key,"string"); - $encoded_path = new xmlrpcval(conf('web_path'),"string"); + $encoded_path = new xmlrpcval(Config::get('web_path'),"string"); - $f = new xmlrpcmsg('remote_catalog_query', array($encoded_key,$encoded_path)); + $xmlrpc_message = new xmlrpcmsg('xmlrpcserver.get_catalogs', array($encoded_key,$encoded_path)); - if (conf('debug')) { $client->setDebug(1); } + // 6 that's right, the secret level because if you do have debug on most likely you're + // going to just crash your browser... sorry folks + if (Config::get('debug') AND Config::get('debug_level') == '6') { $client->setDebug(1); } - $response = $client->send($f,30); + $response = $client->send($xmlrpc_message,30); $value = $response->value(); if ( !$response->faultCode() ) { @@ -1287,8 +1293,8 @@ class Catalog { // Print out the catalogs we are going to sync foreach ($data as $vars) { - $catalog_name = $vars[0]; - $count = $vars[1]; + $catalog_name = $vars['name']; + $count = $vars['count']; print("<b>Reading Remote Catalog: $catalog_name ($count Songs)</b> [$this->path]<br />\n"); $total += $count; } @@ -1298,7 +1304,7 @@ class Catalog { } // if we didn't get an error else { $error_msg = _("Error connecting to") . " " . $server . " " . _("Code") . ": " . $response->faultCode() . " " . _("Reason") . ": " . $response->faultString(); - debug_event('xmlrpc-client',$error_msg,'1','ampache-catalog'); + debug_event('XMLCLIENT',$error_msg,'1'); echo "<p class=\"error\">$error_msg</p>"; return; } @@ -1324,11 +1330,8 @@ class Catalog { * get_remote_song * This functions takes a start and end point for gathering songs from a remote server. It is broken up * in attempt to get around the problem of very large target catalogs - * @package XMLRPC - * @catagory Client - * @todo Allow specificion of single catalog */ - function get_remote_song($client,$start,$end) { + public function get_remote_song($client,$start,$end) { $encoded_start = new xmlrpcval($start,"int"); $encoded_end = new xmlrpcval($end,"int"); @@ -1336,12 +1339,12 @@ class Catalog { $query_array = array($encoded_key,$encoded_start,$encoded_end); - $f = new xmlrpcmsg('remote_song_query',$query_array); + $xmlrpc_message = new xmlrpcmsg('xmlrpcserver.get_songs',$query_array); /* Depending upon the size of the target catalog this can be a very slow/long process */ set_time_limit(0); // Sixty Second time out per chunk - $response = $client->send($f,60); + $response = $client->send($xmlrpc_message,60); $value = $response->value(); if ( !$response->faultCode() ) { @@ -1353,7 +1356,7 @@ class Catalog { } else { $error_msg = _('Error connecting to') . " " . $server . " " . _("Code") . ": " . $response->faultCode() . " " . _("Reason") . ": " . $response->faultString(); - debug_event('xmlrpc-client',$error_msg,'1','ampache-catalog'); + debug_event('XMLCLIENT',$error_msg,'1'); echo "<p class=\"error\">$error_msg</p>"; } @@ -1370,37 +1373,26 @@ class Catalog { * @todo This should be based off of seralize * @todo some kind of cleanup of dead songs? */ - function update_remote_catalog($songs,$root_path) { + function update_remote_catalog($data,$root_path) { /* We need to check the incomming songs to see which ones need to be added */ - foreach ($songs as $song) { + foreach ($data as $serialized_song) { // Prevent a timeout set_time_limit(0); - $song = base64_decode($song); - - $data = explode("::", $song); - - $new_song->artist = self::check_artist($data[0]); - $new_song->album = self::check_album($data[1],$data[4]); - $new_song->title = $data[2]; - $new_song->year = $data[4]; - $new_song->bitrate = $data[5]; - $new_song->rate = $data[6]; - $new_song->mode = $data[7]; - $new_song->size = $data[8]; - $new_song->time = $data[9]; - $new_song->track = $data[10]; - $new_song->genre = self::check_genre($data[11]); - $new_song->file = $root_path . "/play/index.php?song=" . $data[12]; - $new_song->catalog = $this->id; + $song = unserialize($serialized_song); + $song->artist = self::check_artist($song->artist); + $song->album = self::check_album($song->album,$song->year); + $song->genre = self::check_genre($song->genre); + $song->file = $root_path . "/play/index.php?song=" . $data[12]; + $song->catalog = $this->id; - if (!$this->check_remote_song($new_song->file)) { - $this->insert_remote_song($new_song); + if (!$this->check_remote_song($song->file)) { + $this->insert_remote_song($song); } } // foreach new Songs @@ -2113,22 +2105,22 @@ class Catalog { } // insert_local_song - /*! - @function insert_remote_song - @discussion takes the information gotten from XML-RPC and - inserts it into the local database. The filename - ends up being the url. - */ - function insert_remote_song($song) { + /** + * insert_remote_song + * takes the information gotten from XML-RPC and + * inserts it into the local database. The filename + * ends up being the url. + */ + public function insert_remote_song($song) { - $url = sql_escape($song->file); + $url = Dba::escape($song->file); $title = self::check_title($song->title); - $title = sql_escape($title); + $title = Dba::escape($title); $current_time = time(); $sql = "INSERT INTO song (file,catalog,album,artist,title,bitrate,rate,mode,size,time,track,genre,addition_time,year)" . " VALUES ('$url','$song->catalog','$song->album','$song->artist','$title','$song->bitrate','$song->rate','$song->mode','$song->size','$song->time','$song->track','$song->genre','$current_time','$song->year')"; - $db_results = mysql_query($sql, dbh()); + $db_results = Dba::query($sql); if (!$db_results) { debug_event('insert',"Unable to Add Remote $url -- $sql",'5','ampache-catalog'); @@ -2138,20 +2130,19 @@ class Catalog { } // insert_remote_song - /*! - @function check_remote_song - @discussion checks to see if a remote song exists in the database or not - if it find a song it returns the UID - */ - function check_remote_song($url) { + /** + * check_remote_song + * checks to see if a remote song exists in the database or not + * if it find a song it returns the UID + */ + public function check_remote_song($url) { - $url = sql_escape($url); + $url = Dba::escape($url); - $sql = "SELECT id FROM song WHERE file='$url'"; - - $db_results = mysql_query($sql, dbh()); + $sql = "SELECT `id` FROM `song` WHERE `file`='$url'"; + $db_results = Dba::query($sql); - if (mysql_num_rows($db_results)) { + if (Dba::num_rows($db_results)) { return true; } @@ -2159,12 +2150,11 @@ class Catalog { } // check_remote_song - /** * check_local_mp3 * Checks the song to see if it's there already returns true if found, false if not */ - function check_local_mp3($full_file, $gather_type='') { + public function check_local_mp3($full_file, $gather_type='') { if ($gather_type == 'fast_add') { $file_date = filemtime($full_file); diff --git a/lib/class/song.class.php b/lib/class/song.class.php index 4aa773fd..61ee2b93 100644 --- a/lib/class/song.class.php +++ b/lib/class/song.class.php @@ -659,7 +659,7 @@ class Song { * and does a ton of formating on it creating f_??? variables on the current * object */ - function format() { + public function format() { $this->fill_ext_info(); diff --git a/lib/class/xmlrpcserver.class.php b/lib/class/xmlrpcserver.class.php new file mode 100644 index 00000000..97f0208b --- /dev/null +++ b/lib/class/xmlrpcserver.class.php @@ -0,0 +1,142 @@ +<?php +/* + + Copyright (c) 2001 - 2007 Ampache.org + All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License v2 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +/** + * xmlRpcServer + * This class contains all the methods that the /server/xmlrpc.server.php will respond to + * to add a new method, just define a new public static function in here and it will be automagicaly + * populated to xmlrpcserver.<FUNCTION> in /server/xmlrpc.server.php + */ + +class xmlRpcServer { + + /** + * get_catalogs + * This returns a list of the current non-remote catalogs hosted on this Ampache instance + * It requires a key be passed as the first element + * //FIXME: USE TOKEN! + */ + public static function get_catalogs($xmlrpc_object) { + + // Pull out the key + $variable = $xmlrpc_object->getParam(0); + $key = $variable->scalarval(); + + // Check it and make sure we're super green + if (!Access::check_network('rpc',$_SERVER['REMOTE_ADDR'],'','5',$key)) { + debug_event('XMLSERVER','Error ' . $_SERVER['REMOTE_ADDR'] . ' with key ' . $key . ' does not match any ACLs','1'); + return new xmlrpcresp(0,'503','Key/IP Mis-match Access Denied'); + } + + // Go ahead and gather up the information they are legit + $results = array(); + + $sql = "SELECT `catalog`.`name`,COUNT(`song`.`id`) AS `count`,`catalog`.`id` AS `catalog_id` FROM `catalog` ". + "LEFT JOIN `song` ON `catalog`.`id`=`song`.`catalog` WHERE `catalog`.`catalog_type`='local' " . + "GROUP BY `catalog`.`id`"; + $db_results = Dba::query($sql); + + while ($row = Dba::fetch_assoc($db_results)) { + $results[] = $row; + } + + // We need to set time limit at this point as who know how long this data is going to take + // to return to the client + set_time_limit(0); + + $encoded_array = php_xmlrpc_encode($results); + debug_event('XMLSERVER','Returning data about ' . count($results) . ' catalogs to ' . $_SERVER['REMOTE_ADDR'],'5'); + + return new xmlrpcresp($encoded_array); + + } // get_catalogs + + /** + * get_songs + * This is a basic function to return all of the song data in a serialized format. It takes a start and end point + * as well as the TOKEN for auth mojo + * //FIXME: USE TOKEN! + */ + public static function get_songs($xmlrpc_object) { + + // We're going to be here a while + set_time_limit(0); + + // Pull out the key + $variable = $xmlrpc_object->getParam(0); + $key = $variable->scalarval(); + + // Check it and make sure we're super green + if (!Access::check_network('rpc',$_SERVER['REMOTE_ADDR'],'','5',$key)) { + debug_event('XMLSERVER','Error ' . $_SERVER['REMOTE_ADDR'] . ' with key ' . $key . ' does not match any ACLs','1'); + return new xmlrpcresp(0,'503','Key/IP Mis-match Access Denied'); + } + + // Now pull out the start and end + $start = intval($xmlrpc_object->params['1']->me['int']); + $end = intval($xmlrpc_object->params['2']->me['int']); + + // Get Catalogs first + $sql = "SELECT `catalog`.`id` FROM `catalog` WHERE `catalog`.`catalog_type`='local'"; + $db_results = Dba::query($sql); + + while ($row = Dba::fetch_assoc($db_results)) { + $where_sql .= "`song`.`catalog`='" . $row['id'] . "' OR"; + } + + $where_sql = rtrim($where_sql,'OR'); + + $sql = "SELECT `song`.`id` FROM `song` WHERE `song`.`enabled`='1' AND ($where_sql) LIMIT $start,$end"; + $db_results = Dba::query($sql); + + while ($row = Dba::fetch_assoc($db_results)) { + $song = new Song($row['id']); + $song->fill_ext_info(); + $song->album = $song->get_album_name(); + $song->artist = $song->get_artist_name(); + $song->genre = $song->get_genre_name(); + + $output = serialize($song); + $results[] = $output; + } // end while + + $encoded_array = php_xmlrpc_encode($results); + debug_event('XMLSERVER','Encoded ' . count($results) . ' songs (' . $start . ',' . $end . ')','5'); + + return new xmlrpcresp($encoded_array); + + } // get_songs + + /** + * handshake + * This should be run before any other XMLRPC actions, it checks the KEY encoded with a timestamp then returns a valid TOKEN to be + * used in all further communication + */ + public static function handshake($xmlrpc_object) { + + // Pull out the params + $encoded_key = $xmlrpc_object->params['0']->me['string']; + $timestamp = $xmlrpc_object->params['0']->me['int']; + + } // handshake + +} // xmlRpcServer +?> diff --git a/lib/log.lib.php b/lib/log.lib.php index 63c2ba4a..83786c67 100644 --- a/lib/log.lib.php +++ b/lib/log.lib.php @@ -84,7 +84,8 @@ function ampache_error_handler($errno, $errstr, $errfile, $errline) { * Yea now getid3() spews errors I love it :( */ if (strstr($errstr,"var: Deprecated. Please use the public/private/protected modifiers") OR - strstr($errstr,"getimagesize() [") OR strstr($errstr,"Non-static method getid3")) { + strstr($errstr,"getimagesize() [") OR strstr($errstr,"Non-static method getid3") OR + strstr($errstr,"Assigning the return value of new by reference is deprecated")) { return false; } diff --git a/lib/ui.lib.php b/lib/ui.lib.php index 72203091..ad51b148 100644 --- a/lib/ui.lib.php +++ b/lib/ui.lib.php @@ -725,7 +725,7 @@ function show_catalog_select($name='catalog',$catalog_id=0,$style='') { function show_user_select($name,$selected='',$style='') { echo "<select name=\"$name\" style=\"$style\">\n"; - echo "\t<option value=\"\">" . _('None') . "</option>\n"; + echo "\t<option value=\"\">" . _('All') . "</option>\n"; $sql = "SELECT `id`,`username`,`fullname` FROM `user` ORDER BY `fullname`"; $db_results = Dba::query($sql); diff --git a/modules/xmlrpc/xmlrpc.inc b/modules/xmlrpc/xmlrpc.inc index 0c3702e9..da096c1b 100644 --- a/modules/xmlrpc/xmlrpc.inc +++ b/modules/xmlrpc/xmlrpc.inc @@ -1904,7 +1904,7 @@ $cp1252_to_xmlent = if ($valtyp == '') { // user did not declare type of response value: try to guess it - if (is_object($this->val) && is_a($this->val, 'xmlrpcval')) + if (is_object($this->val) && $this->val instanceof xmlrpcval) { $this->valtyp = 'xmlrpcvals'; } @@ -1997,7 +1997,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } else { - if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval')) + if(!is_object($this->val) || !$this->val instanceof xmlrpcval) { if (is_string($this->val) && $this->valtyp == 'xml') { @@ -2136,7 +2136,8 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha function addParam($par) { // add check: do not add to self params which are not xmlrpcvals - if(is_object($par) && is_a($par, 'xmlrpcval')) + $is_instance = $par instanceof xmlrpcval; + if(is_object($par) && $is_instance) { $this->params[]=$par; return true; @@ -3637,4 +3638,4 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } } -?>
\ No newline at end of file +?> diff --git a/modules/xmlrpc/xmlrpcs.inc b/modules/xmlrpc/xmlrpcs.inc index 661a1b4b..d363bf7d 100644 --- a/modules/xmlrpc/xmlrpcs.inc +++ b/modules/xmlrpc/xmlrpcs.inc @@ -1061,7 +1061,7 @@ { $r = call_user_func($func, $m); } - if (!is_a($r, 'xmlrpcresp')) + if (!$r instanceof xmlrpcresp) { error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object"); if (is_a($r, 'xmlrpcval')) @@ -1169,4 +1169,4 @@ print $r->serialize(); } } -?>
\ No newline at end of file +?> diff --git a/server/xmlrpc.server.php b/server/xmlrpc.server.php index 5487ddec..ab6d9e3c 100644 --- a/server/xmlrpc.server.php +++ b/server/xmlrpc.server.php @@ -1,7 +1,7 @@ <?php /* - Copyright 2001 - 2006 Ampache.org + Copyright 2001 - 2007 Ampache.org All Rights Reserved This program is free software; you can redistribute it and/or @@ -22,32 +22,36 @@ define('NO_SESSION','1'); require_once('../lib/init.php'); -if (conf('xml_rpc')) { - require_once(conf('prefix') . "/modules/xmlrpc/xmlrpcs.inc"); - require_once(conf('prefix') . "/modules/xmlrpc/xmlrpc.inc"); -} -else { exit(); } +/* Set the correct headers */ +header("Content-type: text/xml; charset=" . Config::get('site_charset')); +header("Content-Disposition: attachment; filename=xmlrpc-server.xml"); -/* Setup the vars we are going to need */ -$access = new Access(); +if (Config::get('xml_rpc')) { + require_once Config::get('prefix') . "/modules/xmlrpc/xmlrpcs.inc"; + require_once Config::get('prefix') . "/modules/xmlrpc/xmlrpc.inc"; +} +else { + debug_event('DENIED','Attempted to Access XMLRPC server with xml_rpc disabled','1'); + exit(); +} // ** check that the remote server has access to this catalog -if ($access->check('init-xml-rpc',$_SERVER['REMOTE_ADDR'],'','5','')) { +if (Access::check_network('init-rpc',$_SERVER['REMOTE_ADDR'],'','5','')) { - /* Setup Possible Actions */ - $methods['remote_catalog_query'] = array('function' => 'remote_catalog_query'); - $methods['remote_song_query'] = array('function' => 'remote_song_query'); - $methods['remote_session_verify'] = array('function' => 'remote_session_verify'); + // Define an array of classes we need to pull from for the + $classes = array('xmlRpcServer'); - $s = new xmlrpc_server($methods); -} -else { - // Access Denied... Sucka!! - $methods['remote_catalog_query'] = array('function' => 'remote_server_denied'); - $methods['remote_song_query'] = array('function' => 'remote_server_denied'); - $methods['remote_session_verify'] = array('function' => 'remote_server_denied'); - - $s = new xmlrpc_server($methods); -} + foreach ($classes as $class) { + $methods = get_class_methods($class); + + foreach ($methods as $method) { + $name = strtolower($class) . '.' . strtolower($method); + $functions[$name] = array('function'=>$class . '::' . $method); + } + + } // end foreach of classes + + $server = new xmlrpc_server($functions); +} // test for ACL ?> diff --git a/templates/show_edit_access.inc.php b/templates/show_edit_access.inc.php index 676a8570..c4825d2f 100644 --- a/templates/show_edit_access.inc.php +++ b/templates/show_edit_access.inc.php @@ -43,12 +43,14 @@ <td><?php echo _('Start IP Address'); ?>:</td> <td> <input type="text" name="start" value="<?php echo int2ip($access->start); ?>" size="20" maxlength="15" /> + <span class="information">(0.0.0.0)</span> </td> </tr> <tr> <td><?php echo _('End IP Address'); ?>:</td> <td> <input type="text" name="end" value="<?php echo int2ip($access->end); ?>" size="20" maxlength="15" /> + <span class="information">(0.0.0.0)</span> </td> </tr> <tr> @@ -57,14 +59,12 @@ <?php show_user_select('user',$access->user); ?> </td> </tr> -<?php if ($access->type == 'rpc') { ?> <tr> <td><?php echo _('Remote Key'); ?></td> <td> <input type="text" name="key" value="<?php echo scrub_out($access->key); ?>" size="32" maxlength="32" /> </td> </tr> -<?php } ?> </tr> <tr> <td><?php echo _('Level'); ?>:</td> |