SECURITY: Fix ability to crash server in ajax.php; added playback position slider and ability to seek through current song
--- a/ajax.php Mon Mar 24 00:12:21 2008 -0400
+++ b/ajax.php Mon Mar 24 02:53:42 2008 -0400
@@ -70,7 +70,7 @@
$tid =& $params[1];
if ( !preg_match('/^[0-9]+$/', $tid) )
{
- return die_json('Invalid track ID');
+ return json_die('Invalid track ID');
}
$tid = intval($tid);
dcop_action('playlist', "playByIndex $tid");
@@ -86,7 +86,7 @@
$volume =& $params[1];
if ( !preg_match('/^[0-9]+$/', $volume) )
{
- return die_json('Invalid track ID');
+ return json_die('Invalid track ID');
}
$volume = intval($volume);
dcop_action('player', "setVolume $volume");
@@ -95,6 +95,18 @@
);
echo $json->encode($return);
break;
+ case 'seek':
+ if ( !$allowcontrol )
+ return false;
+ $pos =& $params[1];
+ if ( !preg_match('/^[0-9]+$/', $pos) )
+ {
+ return json_die('Invalid track ID');
+ }
+ $pos = intval($pos);
+ dcop_action('player', "seek $pos");
+
+ break;
case 'refresh':
global $playlist_last_refresh, $playlist, $playlist_last_md5;
if ( $playlist_last_refresh + 60 < time() )
--- a/playlist.php Mon Mar 24 00:12:21 2008 -0400
+++ b/playlist.php Mon Mar 24 02:53:42 2008 -0400
@@ -17,10 +17,11 @@
{
global $theme, $playlist, $allowcontrol;
- $iphone = ( strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') ||
+ $iphone = ( ( strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') ||
strpos($_SERVER['HTTP_USER_AGENT'], 'iPod') ||
strpos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') ||
- isset($_GET['m'])
+ isset($_GET['m']) )
+ && !isset($_GET['f'])
);
$theme_id = ( $iphone ) ? 'iphone' : $theme;
$smarty = load_theme($theme_id);
@@ -32,7 +33,9 @@
$smarty->assign('scripts', array(
'ajax.js',
'domutils.js',
- 'volume.js'
+ 'volume.js',
+ 'dom-drag.js',
+ 'position.js'
));
$smarty->assign('allow_control', $allowcontrol);
$smarty->display('playlist.tpl');
--- a/scripts/ajax.js Mon Mar 24 00:12:21 2008 -0400
+++ b/scripts/ajax.js Mon Mar 24 02:53:42 2008 -0400
@@ -14,6 +14,39 @@
var ajax;
var is_playing = false, current_track = -1, current_track_length, current_track_pos, ct_advance_timeout = false, ct_counter = false, playlist_md5 = false, first_load = true;
+var onload_hooks = new Array();
+
+function addOnloadHook(func)
+{
+ if ( typeof ( func ) == 'function' )
+ {
+ if ( typeof(onload_hooks.push) == 'function' )
+ {
+ onload_hooks.push(func);
+ }
+ else
+ {
+ onload_hooks[onload_hooks.length] = func;
+ }
+ }
+}
+
+function runOnloadHooks(e)
+{
+ var _errorTrapper = 0;
+ for ( var _oLc = 0; _oLc < onload_hooks.length; _oLc++ )
+ {
+ _errorTrapper++;
+ if ( _errorTrapper >= 1000 )
+ break;
+ var _f = onload_hooks[_oLc];
+ if ( typeof(_f) == 'function' )
+ {
+ _f(e);
+ }
+ }
+}
+
function ajaxGet(uri, f)
{
if (window.XMLHttpRequest)
@@ -147,7 +180,7 @@
if ( ct_counter )
clearInterval(ct_counter);
update_clock();
- if ( is_playing )
+ if ( is_playing && time_remaining > 0 )
{
ct_advance_timeout = setTimeout(refresh_playlist, ( 1000 * time_remaining ));
ct_counter = setInterval(update_clock, 1000);
@@ -228,8 +261,26 @@
});
}
+function set_playback_position(pos)
+{
+ pos = Math.round(( pos / 100 ) * current_track_length);
+ setAjaxLoading();
+ if ( !allow_control )
+ return false;
+ ajaxGet('/action.json/seek/' + pos, function()
+ {
+ if ( ajax.readyState == 4 && ajax.status == 200 )
+ {
+ unsetAjaxLoading();
+ current_track_pos = pos;
+ update_clock();
+ }
+ });
+}
+
function update_clock()
{
+ posslide_set_position((100 * (current_track_pos / current_track_length)));
var str = secs_to_string(current_track_pos) + '/' + secs_to_string(current_track_length);
$('playmeter').object.innerHTML = str;
current_track_pos++;
@@ -256,6 +307,12 @@
return str;
}
-window.onload = refresh_playlist;
setInterval(refresh_playlist, 10000);
+window.onload = function(e)
+{
+ runOnloadHooks(e);
+}
+
+addOnloadHook(refresh_playlist);
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/dom-drag.js Mon Mar 24 02:53:42 2008 -0400
@@ -0,0 +1,121 @@
+/**************************************************
+ * dom-drag.js
+ * 09.25.2001
+ * www.youngpup.net
+ **************************************************
+ * 10.28.2001 - fixed minor bug where events
+ * sometimes fired off the handle, not the root.
+ **************************************************/
+
+var Drag = {
+
+ obj : null,
+
+ init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
+ {
+ o.onmousedown = Drag.start;
+
+ o.hmode = bSwapHorzRef ? false : true ;
+ o.vmode = bSwapVertRef ? false : true ;
+
+ o.root = oRoot && oRoot != null ? oRoot : o ;
+
+ if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left = "0px";
+ if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top = "0px";
+ if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right = "0px";
+ if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
+
+ o.minX = typeof minX != 'undefined' ? minX : null;
+ o.minY = typeof minY != 'undefined' ? minY : null;
+ o.maxX = typeof maxX != 'undefined' ? maxX : null;
+ o.maxY = typeof maxY != 'undefined' ? maxY : null;
+
+ o.xMapper = fXMapper ? fXMapper : null;
+ o.yMapper = fYMapper ? fYMapper : null;
+
+ o.root.onDragStart = new Function();
+ o.root.onDragEnd = new Function();
+ o.root.onDrag = new Function();
+ },
+
+ start : function(e)
+ {
+ var o = Drag.obj = this;
+ e = Drag.fixE(e);
+ var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
+ var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+ o.root.onDragStart(x, y);
+
+ o.lastMouseX = e.clientX;
+ o.lastMouseY = e.clientY;
+
+ if (o.hmode) {
+ if (o.minX != null) o.minMouseX = e.clientX - x + o.minX;
+ if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
+ } else {
+ if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
+ if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
+ }
+
+ if (o.vmode) {
+ if (o.minY != null) o.minMouseY = e.clientY - y + o.minY;
+ if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
+ } else {
+ if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
+ if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
+ }
+
+ document.onmousemove = Drag.drag;
+ document.onmouseup = Drag.end;
+
+ return false;
+ },
+
+ drag : function(e)
+ {
+ e = Drag.fixE(e);
+ var o = Drag.obj;
+
+ var ey = e.clientY;
+ var ex = e.clientX;
+ var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
+ var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+ var nx, ny;
+
+ if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
+ if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
+ if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
+ if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
+
+ nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
+ ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
+
+ if (o.xMapper) nx = o.xMapper(y)
+ else if (o.yMapper) ny = o.yMapper(x)
+
+ Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
+ Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
+ Drag.obj.lastMouseX = ex;
+ Drag.obj.lastMouseY = ey;
+
+ Drag.obj.root.onDrag(nx, ny);
+ return false;
+ },
+
+ end : function()
+ {
+ document.onmousemove = null;
+ document.onmouseup = null;
+ Drag.obj.root.onDragEnd( parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]),
+ parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
+ Drag.obj = null;
+ },
+
+ fixE : function(e)
+ {
+ if (typeof e == 'undefined') e = window.event;
+ if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
+ if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
+ return e;
+ }
+};
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/position.js Mon Mar 24 02:53:42 2008 -0400
@@ -0,0 +1,76 @@
+var pos_supported = false;
+var pos_in_drag = false;
+
+var posslide_init = function()
+{
+ // make sure the theme supports the playhead
+ var base = document.getElementById('playhead');
+ var inner = document.getElementById('playhead-filler');
+ var slider = document.getElementById('playhead-button');
+ if ( !slider || !inner || !base )
+ return false;
+
+ pos_supported = true;
+
+ var minX = $(base).Left() - 3;
+ var minY = $(base).Top() + 3;
+ var maxY = minY;
+ var maxX = minX + $(base).Width() - 6;
+ Drag.init(slider, inner, minX, maxX, minY, maxY);
+
+ inner.onDrag = posslide_handle_drag;
+ inner.onDragEnd = posslide_handle_dragend;
+ inner.onDragStart = function(x, y) { pos_in_drag = true; };
+ base.onclick = posslide_handle_click;
+
+ posslide_set_position(0);
+ slider.style.top = minY + 'px';
+}
+
+var posslide_handle_drag = function(x, y, do_inner)
+{
+ var inner = document.getElementById('playhead-filler');
+ var slider = document.getElementById('playhead-button');
+ var size = x - $(inner).Left() + 8;
+ if ( do_inner )
+ inner.style.width = size + 'px';
+ if ( ( pos_in_drag && !do_inner ) || ( !pos_in_drag && do_inner ) )
+ slider.style.left = x + 'px';
+}
+
+var posslide_handle_dragend = function(x, y)
+{
+ pos_in_drag = false;
+ var inner = document.getElementById('playhead-filler');
+ var base = document.getElementById('playhead');
+ var multiplier = $(base).Width();
+ var pos = x - $(inner).Left() + 8;
+ pos = 100 * ( pos / multiplier );
+ set_playback_position(pos);
+}
+
+var posslide_handle_click = function(e)
+{
+ e = Drag.fixE(e);
+ var base = document.getElementById('playhead');
+ var val = e.clientX - $(base).Left();
+ val = 100 * ( val / $(base).Width() );
+ posslide_set_position(val);
+ set_playback_position(val);
+}
+
+function posslide_set_position(pos)
+{
+ if ( !pos_supported )
+ return false;
+
+ // pos needs to be 1-100
+ var base = document.getElementById('playhead');
+ var multiplier = $(base).Width();
+ pos = ( pos / 100 ) * multiplier;
+ var left = pos + $(base).Left();
+ posslide_handle_drag(left, 0, true);
+}
+
+addOnloadHook(posslide_init);
+
Binary file themes/funkymonkey/images/playhead.png has changed
Binary file themes/funkymonkey/images/position-empty.png has changed
Binary file themes/funkymonkey/images/position-full.png has changed
Binary file themes/funkymonkey/images/src/playhead.xcf has changed
Binary file themes/funkymonkey/images/src/position-empty.xcf has changed
Binary file themes/funkymonkey/images/src/position-full.xcf has changed
--- a/themes/funkymonkey/playlist.tpl Mon Mar 24 00:12:21 2008 -0400
+++ b/themes/funkymonkey/playlist.tpl Mon Mar 24 02:53:42 2008 -0400
@@ -27,6 +27,8 @@
<body>
<div id="playbar">
<div class="playbar-inner">
+ <div id="playhead"><div id="playhead-filler"> </div></div>
+ <div id="playhead-button"> </div>
<img alt=" " id="ajax_status" style="display: none; float: right; margin: 3px 0;" src="about:blank" />
<img alt="AmaroK web control" src="/themes/{$theme|escape}/images/amarok.gif" style="margin-right: 20px;" />
{if $allow_control}
--- a/themes/funkymonkey/style.css Mon Mar 24 00:12:21 2008 -0400
+++ b/themes/funkymonkey/style.css Mon Mar 24 02:53:42 2008 -0400
@@ -95,3 +95,30 @@
border-color: #d0d0d0;
}
+/* Position slider (playhead) */
+
+div#playhead {
+ background-image: url(images/position-empty.png);
+ width: 250px;
+ background-repeat: no-repeat;
+ background-position: center center;
+ float: right;
+ margin-left: 10px;
+}
+
+div#playhead-filler {
+ background-image: url(images/position-full.png);
+ width: 150px;
+ background-repeat: no-repeat;
+ background-position: left center;
+}
+
+div#playhead-button {
+ background-image: url(images/playhead.png);
+ width: 16px;
+ height: 16px;
+ font-size: 1px;
+ position: absolute;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
Binary file themes/iphone/images/blank.png has changed
Binary file themes/iphone/images/playhead.png has changed
Binary file themes/iphone/images/position-empty.png has changed
Binary file themes/iphone/images/position-full.png has changed
--- a/themes/iphone/playlist.tpl Mon Mar 24 00:12:21 2008 -0400
+++ b/themes/iphone/playlist.tpl Mon Mar 24 02:53:42 2008 -0400
@@ -33,12 +33,16 @@
<div class="playbar-inner">
<img alt=" " id="ajax_status" style="display: none; position: absolute; top: 5px; right: 5px;" src="about:blank" />
{if $allow_control}
+ <a href="#action:_nil" onclick=" return false;"><img alt=" " src="/themes/{$theme|escape}/images/blank.png" /></a>
<a href="#action:prev" onclick="player_action('prev'); return false;"><img alt="« PrevTrk" src="/themes/{$theme|escape}/images/prev.png" style="position: relative; top: -8px;" /></a>
<a href="#action:play" onclick="player_action('play'); return false;" id="btn_playpause"><img alt="Play" src="/themes/{$theme|escape}/images/play.png" /></a>
<a href="#action:next" onclick="player_action('next'); return false;"><img alt="NextTrk »" src="/themes/{$theme|escape}/images/next.png" style="position: relative; top: -8px;" /></a>
+ <a href="#action:stop" onclick="player_action('stop'); return false;"><img alt="Stop" src="/themes/{$theme|escape}/images/stop.png" style="position: relative; top: -8px; border-left: 1px solid #a0a0a0;" /></a>
<br />
{/if}
<span id="playmeter">--:--/--:--</span><br />
+ <div id="playhead"><div id="playhead-filler"> </div></div>
+ <div id="playhead-button"> </div>
{if $allow_control}
<img hspace="4" alt="Volume: " src="/themes/{$theme|escape}/images/volume.png" />
<span id="volume_wrap"><a
--- a/themes/iphone/style.css Mon Mar 24 00:12:21 2008 -0400
+++ b/themes/iphone/style.css Mon Mar 24 02:53:42 2008 -0400
@@ -65,7 +65,7 @@
}
div#playlist {
- margin: 138px 8px 0 8px;
+ margin: 174px 8px 0 8px;
}
a.tracklink {
@@ -106,3 +106,31 @@
margin-top: 10px;
padding: 5px;
}
+
+/* Position slider (playhead) */
+
+div#playhead {
+ background-image: url(images/position-empty.png);
+ width: 250px;
+ background-repeat: no-repeat;
+ background-position: center center;
+ margin: 0 auto 10px auto;
+}
+
+div#playhead-filler {
+ background-image: url(images/position-full.png);
+ width: 150px;
+ background-repeat: no-repeat;
+ background-position: left center;
+}
+
+div#playhead-button {
+ background-image: url(images/playhead.png);
+ width: 16px;
+ height: 16px;
+ font-size: 1px;
+ position: absolute;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
+
--- a/webserver.php Mon Mar 24 00:12:21 2008 -0400
+++ b/webserver.php Mon Mar 24 02:53:42 2008 -0400
@@ -599,7 +599,7 @@
{
echo '<div style="border: 1px solid #AA0000; background-color: #FFF0F0; padding: 10px;">';
echo "<b>PHP warning/error:</b> type $errno ($errstr) caught in <b>$errfile</b> on <b>$errline</b><br />";
- echo "Error context:<pre>" . htmlspecialchars(print_r($errcontext, true)) . "</pre>";
+ // echo "Error context:<pre>" . htmlspecialchars(print_r($errcontext, true)) . "</pre>";
echo '</div>';
}