SECURITY: Fix ability to crash server in ajax.php; added playback position slider and ability to seek through current song
authorDan
Mon, 24 Mar 2008 02:53:42 -0400
changeset 10 d3059e20b0fa
parent 9 63a257541313
child 11 0faea3a6c881
SECURITY: Fix ability to crash server in ajax.php; added playback position slider and ability to seek through current song
ajax.php
playlist.php
scripts/ajax.js
scripts/dom-drag.js
scripts/position.js
themes/funkymonkey/images/playhead.png
themes/funkymonkey/images/position-empty.png
themes/funkymonkey/images/position-full.png
themes/funkymonkey/images/src/playhead.xcf
themes/funkymonkey/images/src/position-empty.xcf
themes/funkymonkey/images/src/position-full.xcf
themes/funkymonkey/playlist.tpl
themes/funkymonkey/style.css
themes/iphone/images/blank.png
themes/iphone/images/playhead.png
themes/iphone/images/position-empty.png
themes/iphone/images/position-full.png
themes/iphone/playlist.tpl
themes/iphone/style.css
webserver.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() )
--- 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">&nbsp;</div></div>
+        <div id="playhead-button">&nbsp;</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="&laquo; 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 &raquo;" 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">&nbsp;</div></div>
+        <div id="playhead-button">&nbsp;</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>';
   }