# HG changeset patch # User Dan # Date 1206341622 14400 # Node ID d3059e20b0fa7b5cb844c014c87cfd5075a04d1d # Parent 63a2575413130d8820d8498b6f3de4794ed1f7e1 SECURITY: Fix ability to crash server in ajax.php; added playback position slider and ability to seek through current song diff -r 63a257541313 -r d3059e20b0fa ajax.php --- 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() ) diff -r 63a257541313 -r d3059e20b0fa playlist.php --- 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'); diff -r 63a257541313 -r d3059e20b0fa scripts/ajax.js --- 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); + diff -r 63a257541313 -r d3059e20b0fa scripts/dom-drag.js --- /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 diff -r 63a257541313 -r d3059e20b0fa scripts/position.js --- /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); + diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/images/playhead.png Binary file themes/funkymonkey/images/playhead.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/images/position-empty.png Binary file themes/funkymonkey/images/position-empty.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/images/position-full.png Binary file themes/funkymonkey/images/position-full.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/images/src/playhead.xcf Binary file themes/funkymonkey/images/src/playhead.xcf has changed diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/images/src/position-empty.xcf Binary file themes/funkymonkey/images/src/position-empty.xcf has changed diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/images/src/position-full.xcf Binary file themes/funkymonkey/images/src/position-full.xcf has changed diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/playlist.tpl --- 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 @@
+
 
+
 
AmaroK web control {if $allow_control} diff -r 63a257541313 -r d3059e20b0fa themes/funkymonkey/style.css --- 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; +} diff -r 63a257541313 -r d3059e20b0fa themes/iphone/images/blank.png Binary file themes/iphone/images/blank.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/iphone/images/playhead.png Binary file themes/iphone/images/playhead.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/iphone/images/position-empty.png Binary file themes/iphone/images/position-empty.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/iphone/images/position-full.png Binary file themes/iphone/images/position-full.png has changed diff -r 63a257541313 -r d3059e20b0fa themes/iphone/playlist.tpl --- 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 @@ '; }