functions.php
author Dan
Mon, 24 Mar 2008 02:53:42 -0400
changeset 10 d3059e20b0fa
parent 9 63a257541313
child 13 b5db2345c397
permissions -rw-r--r--
SECURITY: Fix ability to crash server in ajax.php; added playback position slider and ability to seek through current song

<?php

/**
 * Utility functions
 * 
 * Greyhound - real web management for Amarok
 * Copyright (C) 2008 Dan Fuhry
 *
 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 *
 * 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 details.
 */

/**
 * Utility/reporting functions
 */
 
/**
 * Report a fatal error and exit
 * @param string Error message
 */

function burnout($msg)
{
  $h = @fopen('php://stderr', 'w');
  fwrite($h, "\x1B[31;1m[Greyhound] fatal: \x1B[37;1m");
  fwrite($h, "$msg\x1B[0m\n");
  fclose($h);
  exit(1);
}

/**
 * Print a stylized status message, compatible with Linux consoles
 * @param string Status message
 */

function status($msg)
{
  $h = @fopen('php://stderr', 'w');
  fwrite($h, "\x1B[32;1m[Greyhound] \x1B[32;0m$msg\x1B[0m\n");
  fclose($h);
}

/**
 * Performs an action with DCOP.
 * @param string DCOP component, e.g. player, playlist, playlistbrowser, ...
 * @param string Action to perform, e.g. stop, play, ...
 * @param string additional parameters... (NOT IMPLEMENTED)
 * @return mixed output of DCOP command
 */

function dcop_action($component, $action)
{
  $tmpfile = tempnam('amaweb', '');
  if ( !$tmpfile )
    burnout('tempnam() failed us');
  system("dcop amarok $component $action > $tmpfile");
  $output = @file_get_contents($tmpfile);
  @unlink($tmpfile);
  $output = trim($output);
  
  // detect type of output
  if ( $output == 'true' )
    return true;
  else if ( $output == 'false' )
    return false;
  else if ( preg_match('/^-?[0-9]+/', $output) )
    return intval($output);
  else
    return $output;
}

/**
 * Rebuilds the copy of the playlist in RAM
 */

$playlist_last_md5 = '';

function rebuild_playlist()
{
  // import what we need
  global $homedir, $playlist;
  // sync and load the playlist file
  $playlist_file = dcop_action('playlist', 'saveCurrentPlaylist');
  // check MD5 - if it's not changed, exit to save CPU cycles
  global $playlist_last_md5;
  $effective_md5 = md5_playlist_file($playlist_file);
  if ( $playlist_last_md5 == $effective_md5 )
  {
    return true;
  }
  $playlist_last_md5 = $effective_md5;
  // start XML parser
  try
  {
    $xml = simplexml_load_file($playlist_file);
  }
  catch ( Exception $e )
  {
    burnout("Caught exception trying to load playlist file:\n$e");
  }
  $attribs = $xml->attributes();
  if ( @$attribs['product'] != 'Amarok' )
  {
    burnout('Playlist is not in Amarok format');
  }
  $playlist = array();
  foreach ( $xml->children() as $child )
  {
    $attribs = $child->attributes();
    $item = array(
        'uri'    => $attribs['uri'],
        'title'  => strval($child->Title),
        'artist' => strval($child->Artist),
        'album'  => strval($child->Album),
        'length' => seconds_to_str(intval($child->Length)),
        'length_int' => intval($child->Length)
      );
    $playlist[] = $item;
  }
}

/**
 * Builds the correct MD5 check for the specified playlist XML file. This is designed to base on the list of actual tracks, disregarding
 * the rest of the text in the XML file.
 * @param string Path to playlist
 * @return string hash
 */

function md5_playlist_file($file)
{
  $contents = @file_get_contents($file);
  if ( empty($contents) )
    return false;
  $count = preg_match_all('/uniqueid="([a-fA-F0-9]+?)"/', $contents, $matches);
  $matches = implode("", $matches[1]);
  return md5($matches);
}

/**
 * Converts a number to minute:second format
 * @param int Seconds
 * @return string format: mm:ss
 */

function seconds_to_str($secs)
{
  $seconds = $secs % 60;
  $minutes = ( $secs - $seconds ) / 60;
  $seconds = strval($seconds);
  $minutes = strval($minutes);
  if ( strlen($seconds) < 2 )
    $seconds = "0$seconds";
  if ( strlen($minutes) < 2 )
    $minutes = "0$minutes";
  return "$minutes:$seconds";
}

/**
 * Loads the specified theme into Smarty
 * @param string Theme ID
 * @return object Smarty object
 */

function load_theme($theme_id)
{
  global $httpd;
  static $smarty = array();
  if ( !isset($smarty[$theme_id]) )
  {
    $smarty[$theme_id] = new Smarty();
    $smarty[$theme_id]->template_dir = GREY_ROOT . "/themes/$theme_id";
    if ( !is_dir("./compiled/$theme_id") )
      @mkdir("./compiled/$theme_id");
    $smarty[$theme_id]->compile_dir  = "./compiled/$theme_id";
    $smarty[$theme_id]->config_dir = "./config";
    $httpd->add_handler("themes/$theme_id", 'dir', GREY_ROOT . "/themes/$theme_id");
  }
  return $smarty[$theme_id];
}