Image tools: Use which() instead of reimplementing (should also work on win32 now)
<?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)
{
global $use_colors;
$h = @fopen('php://stderr', 'w');
if ( $use_colors )
{
fwrite($h, "\x1B[31;1m[Greyhound] fatal: \x1B[37;1m");
fwrite($h, "$msg\x1B[0m\n");
}
else
{
fwrite($h, "[Greyhound] fatal: $msg\n");
}
fclose($h);
exit(1);
}
/**
* Print a stylized status message, compatible with Linux consoles. Falls back to no control characters if on unsupported terminal.
* @param string Status message
*/
function status($msg)
{
global $use_colors;
$h = @fopen('php://stderr', 'w');
$label = ( defined('HTTPD_WS_CHILD') ) ? 'Child ' . substr(strval(getmypid()), -3) : 'Greyhound';
if ( $use_colors )
{
fwrite($h, "\x1B[32;1m[$label] \x1B[32;0m$msg\x1B[0m\n");
}
else
{
fwrite($h, "[$label] $msg\n");
}
fclose($h);
}
/**
* Print a stylized warning message, compatible with Linux consoles
* @param string message message
*/
function warning($msg)
{
global $use_colors;
$h = @fopen('php://stderr', 'w');
if ( $use_colors )
{
fwrite($h, "\x1B[33;1m[Greyhound] \x1B[0m\x1B[33mWarning:\x1B[0m $msg\n");
}
else
{
fwrite($h, "[Greyhound] Warning: $msg\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)
{
file_put_contents('php://stderr', "[dcop] start...");
if ( function_exists('proc_open') )
{
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w')
);
$dcop_command = "dcop amarok $component $action";
$dcop_process = proc_open($dcop_command, $descriptorspec, $dcop_pipes);
do
{
$status = proc_get_status($dcop_process);
usleep(2000);
} while ( $status['running'] );
pcntl_waitpid($status['pid'], $status);
$output = fgets($dcop_pipes[1], 1024);
proc_close($dcop_process);
}
else
{
$tmpfile = tempnam('amaweb', '');
if ( !$tmpfile )
burnout('tempnam() failed us');
// This is freezing up for some reason.
system("dcop amarok $component $action > $tmpfile");
$output = @file_get_contents($tmpfile);
@unlink($tmpfile);
}
file_put_contents('php://stderr', "got it...\r");
$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 $playlist, $amarok_home;
// sync and load the playlist file
$playlist_file = dcop_action('playlist', 'saveCurrentPlaylist');
// do we have amarok's home?
if ( !$amarok_home )
$amarok_home = dirname($playlist_file);
// 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;
}
status('Rebuilding playlist cache');
$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;
}
// if we're a child process, signal the parent to update
if ( defined('HTTPD_WS_CHILD') )
{
global $httpd;
posix_kill($httpd->parent_pid, SIGHUP);
}
}
/**
* 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]);
if ( empty($matches) )
{
// sometimes current.xml has blank unique IDs
$count = preg_match_all('/url="([^"]+?)"/', $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 ( $theme_id === '__free__' )
{
$smarty = array();
return false;
}
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];
}
/**
* Implementation of the "which" command in native PHP.
* @param string command
* @return string path to executable, or false on failure
*/
function which($executable)
{
$path = ( isset($_ENV['PATH']) ) ? $_ENV['PATH'] : ( isset($_SERVER['PATH']) ? $_SERVER['PATH'] : false );
if ( !$path )
// couldn't get OS's PATH
return false;
$win32 = ( PHP_OS == 'WINNT' || PHP_OS == 'WIN32' );
$extensions = $win32 ? array('.exe', '.com', '.bat') : array('');
$separator = $win32 ? ';' : ':';
$paths = explode($separator, $path);
foreach ( $paths as $dir )
{
foreach ( $extensions as $ext )
{
$fullpath = "$dir/{$executable}{$ext}";
if ( file_exists($fullpath) && is_executable($fullpath) )
{
return $fullpath;
}
}
}
return false;
}
/**
* Reload the config.
*/
function grey_reload_config()
{
global $httpd;
status('reloading the config');
if ( file_exists('./greyhound-config.php') )
{
require('./greyhound-config.php');
}
else
{
// ignore this, it allows using a different config file when a Mercurial repository
// exists in Greyhound's root directory (to allow the devs to have their own config
// separate from the default)
if ( @is_dir(GREY_ROOT . '/.hg') )
{
require(GREY_ROOT . '/config.dev.php');
}
else
{
require(GREY_ROOT . '/config.php');
}
}
foreach ( array('public', 'enable_ipv4', 'enable_ipv6', 'allowcontrol', 'theme', 'allow_fork', 'use_auth', 'auth_data', 'configpass') as $var )
{
if ( isset($$var) )
{
$GLOBALS[$var] = $$var;
}
}
}