diff options
-rwxr-xr-x | docs/CHANGELOG | 4 | ||||
-rw-r--r-- | lib/class/api.class.php | 2 | ||||
-rw-r--r-- | lib/class/stream.class.php | 4 | ||||
-rw-r--r-- | lib/class/vauth.class.php | 352 | ||||
-rw-r--r-- | lib/class/xmlrpcserver.class.php | 28 | ||||
-rw-r--r-- | play/index.php | 32 | ||||
-rw-r--r-- | templates/show_add_user.inc.php | 2 | ||||
-rw-r--r-- | templates/show_edit_user.inc.php | 14 | ||||
-rw-r--r-- | themes/greysme/templates/default.css | 2 |
9 files changed, 418 insertions, 22 deletions
diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 6029e406..623e39fd 100755 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -4,6 +4,10 @@ -------------------------------------------------------------------------- v.3.4-Alpha4 + - Fixed some issues with downsampling + seeking and seeking in + general (Thx Karl Hungus) + - Fixed CSS references to missing files + - Fixed Missing Levels on Edit and incorrect level on edit - Added check to make sure timestamp passed to API is less then four hours old. Set to four hours to allow for some difference in server/client time diff --git a/lib/class/api.class.php b/lib/class/api.class.php index a580bfbe..6a1707ab 100644 --- a/lib/class/api.class.php +++ b/lib/class/api.class.php @@ -72,7 +72,7 @@ class Api { // Run the query and return the passphrases as we'll have to mangle them // to figure out if they match what we've got - $sql = "SELECT * FROM `access_list` WHERE `user`='$user_id' AND `start` <= '$ip' AND `end` >= '$ip'"; + $sql = "SELECT * FROM `access_list` WHERE `type`='rpc' AND `user`='$user_id' AND `start` <= '$ip' AND `end` >= '$ip'"; $db_results = Dba::query($sql); while ($row = Dba::fetch_assoc($db_results)) { diff --git a/lib/class/stream.class.php b/lib/class/stream.class.php index 507cee02..9c1ad1e8 100644 --- a/lib/class/stream.class.php +++ b/lib/class/stream.class.php @@ -521,7 +521,7 @@ class Stream { * opened file handled a reference to the song object is passed so that the changes we make * in here affect the external object, References++ */ - public static function start_downsample(&$song,$now_playing_id=0,$song_name=0) { + public static function start_downsample(&$song,$now_playing_id=0,$song_name=0,$start=0) { /* Check to see if bitrates are set if so let's go ahead and optomize! */ $max_bitrate = Config::get('max_bit_rate'); @@ -591,7 +591,7 @@ class Stream { $song->size = floor($sample_ratio*$song->size); /* Get Offset */ - $offset = ( $start*$song->time )/( $sample_ratio*$song->size ); + $offset = ( $start*$song->time )/( $song->size ); $offsetmm = floor($offset/60); $offsetss = floor($offset-$offsetmm*60); $offset = sprintf("%02d.%02d",$offsetmm,$offsetss); diff --git a/lib/class/vauth.class.php b/lib/class/vauth.class.php new file mode 100644 index 00000000..d0f68e7e --- /dev/null +++ b/lib/class/vauth.class.php @@ -0,0 +1,352 @@ +<?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. + +*/ +/** + * Vauth + * This class handles all of the session related stuff in Ampache + * it takes over for the vauth libs, and takes some stuff out of other + * classes where it didn't belong +*/ +class vauth { + + /* Variables from DB */ + + + /** + * Constructor + * This should never be called + */ + private function __construct() { + + // Rien a faire + + } // __construct + + /** + * open + * This function is for opening a new session so we just verify that we have + * a database connection, nothing more is needed + */ + public static function open($save_path,$session_name) { + + if (!is_resource(Dba::dbh())) { + debug_event('SESSION','Error no database connection session failed','1'); + return false; + } + + return true; + + } // open + + /** + * close + * This is run on the end of a sessoin, nothing to do here for now + */ + public static function close() { + + return true; + + } // close + + /** + * read + * This takes a key and then looks in the database and returns the value + */ + public static function read($key) { + + $results = self::get_session_data($key); + if (strlen($results['value']) < 1) { + debug_event('SESSION','Error unable to read session from key ' . $key . ' no data found','1'); + return ''; + } + + return $results['value']; + + } // read + + /** + * write + * This saves the sessoin information into the database + */ + public static function write($key,$value) { + + + $length = Config::get('session_length'); + $value = Dba::escape($value); + $key = Dba::escape($key); + // Check to see if remember me cookie is set, if so use remember length, otherwise use the session length + $expire = isset($_COOKIE[Config::get('session_name') . '_remember']) ? time() + Config::get('remember_length') : time() + Config::get('session_length'); + + $sql = "UPDATE `session` SET `value`='$value', `expire`='$expire' WHERE `id`='$key'"; + $db_results = Dba::query($sql); + + return $db_results; + + } // write + + /** + * destroy + * This removes the specified sessoin from the database + */ + public static function destroy($key) { + + $key = Dba::escape($key); + + // Remove anything and EVERYTHING + $sql = "DELETE FROM `session` WHERE `id`='$key'"; + $db_results = Dba::query($sql); + + // Destory our cookie! + setcookie(Config::get('session_name'),'',time() - 86400); + + return true; + + } // destroy + + /** + * gc + * This function is randomly called and it cleans up the poo + */ + public static function gc($maxlifetime) { + + $sql = "DELETE FROM `session` WHERE `expire` < '" . time() . "'"; + $db_results = Dba::query($sql); + + return true; + + } // gc + + /** + * logout + * This is called when you want to log out and nuke your session + * //FIXME: move all logout logic here + */ + public static function logout($key) { + + self::destroy($key); + return true; + + } // logout + + /** + * get_session_data + * This takes a key and returns the raw data from the database, nothing to + * see here move along people + */ + public static function get_session_data($key) { + + $key = Dba::escape($key); + + $sql = "SELECT * FROM `session` WHERE `id`='$key' AND `expire` > '" . time() . "'"; + $db_results = Dba::query($sql); + + $results = Dba::fetch_assoc($db_results); + + if (!count($results)) { + return false; + } + + return $results; + + } // get_session_data + + /** + * create_cookie + * This is seperated into it's own function because of some flaws in specific + * webservers *cough* IIS *cough* which prevent us from setting a cookie at the + * same time as a header redirect. As such on view of a login a cookie is set with + * the proper name + */ + public static function create_cookie() { + + /* Setup the cookie prefs before we throw down, this is very important */ + $cookie_life = Config::get('cookie_life'); + $cookie_path = Config::get('cookie_path'); + $cookie_domain = false; + $cookie_secure = Config::get('cookie_secure'); + + session_set_cookie_params($cookie_life,$cookie_path,$cookie_domain,$cookie_secure); + + /* Start the session */ + self::ungimp_ie(); + session_start(); + + } // create_cookie, just watch out for the cookie monster + + /** + * session_create + * This is called when you want to create a new session + * it takes care of setting the initial cookie, and inserting the first chunk of + * data, nifty ain't it! + */ + public static function session_create($data) { + + // Regenerate the session ID to prevent fixation + session_regenerate_id(); + + // Create our cookie! + self::create_cookie(); + + // Before refresh we don't have the cookie so we have to use session ID + $key = session_id(); + + $username = Dba::escape($data['username']); + $ip = Dba::escape(ip2int($_SERVER['REMOTE_ADDR'])); + $type = Dba::escape($data['type']); + $value = Dba::escape($data['value']); + $agent = Dba::escape($_SERVER['HTTP_USER_AGENT']); + $expire = Dba::escape(time() + vauth_conf('session_length')); + + /* We can't have null things here people */ + if (!strlen($value)) { $value = ' '; } + + /* Insert the row */ + $sql = "INSERT INTO `session` (`id`,`username`,`ip`,`type`,`agent`,`value`,`expire`) " . + " VALUES ('$key','$username','$ip','$type','$agent','$value','$expire')"; + $db_results = Dba::query($sql); + + if (!$db_results) { + debug_event('SESSION',"Session Creation Failed with Query: $sql and " . Dba::error(),'1'); + } + + return $db_results; + + } // session_create + + /** + * check_session + * This checks for an existing sessoin and if it's still valid then go ahead and start it and return + * true + */ + public static function check_session() { + + // No cookie n go! + if (!isset($_COOKIE[Config::get('session_name')]) { return false; } + + $key = scrub_in($_COOKIE[Config::get('session_name')]); + $data = self::get_session_data($key); + + if (!is_array($results)) { + return false; + } + + // Check for a remember me + if (isset($_COOKIE[Config::get('session_name') . '_remember'])) { + Config::set('cookie_life',Config::get('remember_length'),'1'); + setcookie(Config::get('session_name') . '_remember',time() + Config::get('remember_length'),'/',Config::get('cookie_domain')); + } + + // Setup the cookie params before we start the session this is vital + session_set_cookie_params( + Config::get('cookie_life'), + Config::get('cookie_path'), + Config::get('cookie_domain'), + Config::get('cookie_secure')); + + // Set name + session_name(Config::get('session_name')); + + // Ungimp IE and go + self::ungimp_io(); + session_start(); + + return true; + + } // check_session + + /** + * session_exists + * This checks to see if the specified session of the specified type + * exists, it also provides an array of key'd data that may be required + * based on the type + */ + public static function session_exists($data,$key,$type) { + + // Switch on the type they pass + switch ($type) { + case 'xml-rpc': + case 'interface': + case 'api': + $key = Dba::escape($key); + $time = time(); + $sql = "SELECT * FROM `session` WHERE `id`='$key' AND `expire` > '$time' AND `type`='$type'"; + $db_results = Dba::query($sql); + + if (Dba::num_rows($db_results)) { + return true; + } + break; + case 'stream': + $key = Dba::escape($key); + $ip = ip2int($data['ip']); + $agent = Dba::escape($data['agent']); + $sql = "SELECT * FROM `session_stream` WHERE `id`='$key' AND `expire` > '$time' AND `ip`='$ip' AND `agent`='$agent'"; + $db_results = Dba::query($sql); + + if (Dba::num_rows($db_results)) { + return true; + } + + break; + default: + return false; + break; + } // type + + // Default to false + return false; + + } // session_exists + + /** + * _auto_init + * This function is called when the object is included, this sets up the session_save_handler + */ + public static function _auto_init() { + + session_set_save_handler('vauth::open','vauth::close','vauth::read','vauth::write','vauth::destroy','vauth::gc'); + + } // auto init + + /** + * ungimp_ie + * This function sets the cache limiting to public if you are running + * some flavor of IE. The detection used here is very conservative so feel free + * to fix it. This only has to be done if we're rolling HTTPS + */ + public static function ungimp_ie() { + + // If no https, no ungimpage required + if ($_SERVER['HTTPS'] != 'on') { return true; } + + // Try to detect IE + $agent = trim($_SERVER['HTTP_USER_AGENT']); + + if ((preg_match('|MSIE ([0-9).]+)|',$agent)) || preg_match('|Internet Explorer/([0-9.]+)|',$agent))) { + session_cache_limiter('public'); + } + + return true; + + } // ungimp_ie + +} // end of vauth class + +?> diff --git a/lib/class/xmlrpcserver.class.php b/lib/class/xmlrpcserver.class.php index 97f0208b..abb8076e 100644 --- a/lib/class/xmlrpcserver.class.php +++ b/lib/class/xmlrpcserver.class.php @@ -136,6 +136,34 @@ class xmlRpcServer { $encoded_key = $xmlrpc_object->params['0']->me['string']; $timestamp = $xmlrpc_object->params['0']->me['int']; + // Check the timestamp make sure it's recent + if ($timestamp < (time() - 14400)) { + debug_event('XMLSERVER','Handshake failure, timestamp too old','1'); + return new xmlrpcresp(php_xmlrpc_encoded("Handshake failure")); + } + + // Log the attempt + debug_event('XMLSERVER','Login Attempt, IP: ' . $_SERVER['REMOTE_ADDR'] . ' Time: ' . $timestamp . ' Hash:' . $encoded_key,'5'); + + // Convert the IP Address to an int + $ip = ip2int($_SERVER['REMOTE_ADDR']); + + // Run the query and return the key's for ACLs of type RPC that would match this IP + $sql = "SELECT * FROM `access_list` WHERE `type`='rpc' AND `start` <= '$ip' AND `end` >= '$ip'"; + $db_results = Dba::query($sql); + + while ($row = Dba::fetch_assoc($db_results)) { + + // Build our encoded passphrase + $md5pass = md5($timestamp . $row['key']); + + if ($md5pass == $encoded_key) { + $token = ''; + } + + } // end while rows + + } // handshake } // xmlRpcServer diff --git a/play/index.php b/play/index.php index 64d83e86..0a378be0 100644 --- a/play/index.php +++ b/play/index.php @@ -228,8 +228,8 @@ if ($_GET['action'] == 'download' AND $GLOBALS['user']->prefs['download']) { } // if they are trying to download and they can -$startArray = sscanf( $_SERVER[ "HTTP_RANGE" ], "bytes=%d-" ); -$start = $startArray[0]; +// Parse byte range request +$n = sscanf($_SERVER['HTTP_RANGE'], "bytes=%d-%d",$start,$end); // Generate browser class for sending headers $browser = new Browser(); @@ -253,7 +253,7 @@ if (Config::get('access_control') AND Config::get('downsample_remote')) { // If they are downsampling, or if the song is not a native stream or it's non-local if (($GLOBALS['user']->prefs['transcode'] == 'always' || !$song->native_stream() || $not_local) && $GLOBALS['user']->prefs['transcode'] != 'never') { debug_event('downsample','Starting Downsample...','5'); - $fp = Stream::start_downsample($song,$lastid,$song_name); + $fp = Stream::start_downsample($song,$lastid,$song_name,$start); $song_name = $song->f_artist_full . " - " . $song->title . "." . $song->type; } // end if downsampling else { @@ -276,7 +276,16 @@ if ($start) { $range = $start ."-". ($song->size-1) . "/" . $song->size; header("HTTP/1.1 206 Partial Content"); header("Content-Range: bytes=$range"); - header("Content-Length: ".($song->size-$start)); + + // Calculate stream size from byte range + if(isset($end)) { + $end = min($end,$song->size-1); + $stream_size = ($end-$start)+1; + } + else { + $stream_size = $song->size - $start; + } + header("Content-Length: ".($stream_size)); } /* Last but not least pump em out */ @@ -284,24 +293,25 @@ else { debug_event('stream','Starting stream of ' . $song->file . ' with size ' . $song->size,'5'); header("Content-Length: $song->size"); $browser->downloadHeaders($song_name, $song->mime, false, $song->size); + $stream_size = $song->size; } /* Let's force them to actually play a portion of the song before * we count it in the statistics */ -$bytesStreamed = 0; -$minBytesStreamed = $song->size / 2; +$bytes_streamed = 0; +$min_bytes_streamed = $song->size / 2; // Actually do the streaming do { $buf = fread($fp, 2048); print($buf); - $bytesStreamed += 2048; -} while (!feof($fp) && (connection_status() == 0)); + $bytes_streamed += 2048; +} while (!feof($fp) && (connection_status() == 0) AND $bytes_streamed < $stream_size); // Make sure that a good chunk of the song has been played -if ($bytesStreamed > $minBytesStreamed) { +if ($bytes_streamed > $min_bytes_streamed) { debug_event('Stats','Registering stats for ' . $song->title,'5'); $user->update_stats($song->id); @@ -319,7 +329,7 @@ if ($bytesStreamed > $minBytesStreamed) { } // if enough bytes are streamed else { - debug_event('stream',$bytesStreamed .' of ' . $song->size . ' streamed, less than ' . $minBytesStreamed . ' not collecting stats','5'); + debug_event('stream',$bytes_streamed .' of ' . $song->size . ' streamed, less than ' . $min_bytes_streamed . ' not collecting stats','5'); } @@ -332,6 +342,6 @@ else { } // Note that the stream has ended -debug_event('stream','Stream Ended at ' . $bytesStreamed . ' bytes out of ' . $song->size,'5'); +debug_event('stream','Stream Ended at ' . $bytes_streamed . ' bytes out of ' . $song->size,'5'); ?> diff --git a/templates/show_add_user.inc.php b/templates/show_add_user.inc.php index af0800cf..0b3adcd3 100644 --- a/templates/show_add_user.inc.php +++ b/templates/show_add_user.inc.php @@ -69,7 +69,7 @@ <?php echo _('User Access Level'); ?>: </td> <td> - <?php $var_name = "on_" . $working_user->access; ${$var_name} = 'selected="selected"'; ?> + <?php $var_name = "on_" . $client->access; ${$var_name} = 'selected="selected"'; ?> <select name="access"> <option value="5" <?php echo $on_5; ?>><?php echo _('Guest'); ?></option> <option value="25" <?php echo $on_25; ?>><?php echo _('User'); ?></option> diff --git a/templates/show_edit_user.inc.php b/templates/show_edit_user.inc.php index 45dcb890..7b119736 100644 --- a/templates/show_edit_user.inc.php +++ b/templates/show_edit_user.inc.php @@ -69,12 +69,14 @@ <?php echo _('User Access Level'); ?>: </td> <td> - <?php $var_name = "on_" . $client->access; ${$var_name} = 'selected="selected"'; ?> - <select name="access"> - <option value="5" <?php echo $on_5; ?>><?php echo _('Guest'); ?></option> - <option value="25" <?php echo $on_25; ?>><?php echo _('User'); ?></option> - <option value="100" <?php echo $on_100; ?>><?php echo _('Admin'); ?></option> - </select> + <?php $var_name = "on_" . $client->access; ${$var_name} = 'selected="selected"'; ?> + <select name="access"> + <option value="5" <?php echo $on_5; ?>><?php echo _('Guest'); ?></option> + <option value="25" <?php echo $on_25; ?>><?php echo _('User'); ?></option> + <option value="50" <?php echo $on_50; ?>><?php echo _('Content Manager'); ?></option> + <option value="75" <?php echo $on_75; ?>><?php echo _('Catalog Manager'); ?></option> + <option value="100" <?php echo $on_100; ?>><?php echo _('Admin'); ?></option> + </select> </td> </tr> </table> diff --git a/themes/greysme/templates/default.css b/themes/greysme/templates/default.css index 3e01a519..e6354e70 100644 --- a/themes/greysme/templates/default.css +++ b/themes/greysme/templates/default.css @@ -468,7 +468,7 @@ input[type=checkbox] { border:0;background:none; } .box a:hover, .info-box a:hover { /*background-color: #8b3e38;*/ color: #e9ad51;}
.box-inside {
- background: url(../images/right.gif) top right repeat-y;
+/* background: url(../images/right.gif) top right repeat-y; */
}
.box-content {
padding:12px 12px;
|