First shot at getting a session management system in place. Login and logout pages are there, and auth seems to be working and sufficiently secure for the moment. Sessions last indefinitely and are cookie-based.
<?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)
{
$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 $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, SIGUSR1);
}
}
/**
* 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 ( !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];
}