<html>
<head>
<title>Pandora App Store</title>
</head>
<body>
<?php
// Setup various directory locations.
define( "kRootDir", "C:/xampp/xampp/htdocs/pandora" );
define( "kUploadDir", kRootDir . "/upload" );
define( "kDownloadDir", kRootDir . "/download" );
define( "kIconDir", kRootDir . "/icon" );
// Read buffer size.
define( "kReadBufferSize", 4096 );
// Tags to look for when identifying pxml data.
define( "kPxmlBegin", "<PXML" );
define( "kPxmlEnd", "</PXML>" );
// Amount of overlap to read when identifying pxml tags.
define( "kPxmlOverlap", max( strlen( kPxmlBegin ), strlen( kPxmlEnd ) ) );
// Png magic.
define( "kPngMagic", chr( 0x89 ) . chr( 0x50 ) . chr( 0x4e ) . chr( 0x47 ) );
// Utility function to join two paths.
function JoinPaths( )
{
$args = func_get_args( );
$paths = array( );
foreach ( $args as $arg )
{
$paths = array_merge( $paths, ( array )$arg );
}
foreach ( $paths as &$path )
{
$path = trim( $path, '/' );
}
return join( '/', $paths );
}
// Utility function to get a directory listing.
function GetDirectoryList($directory)
{
// create an array to hold directory list
$results = array();
// create a handler for the directory
$handler = opendir($directory);
if ( $handler === false )
{
return $results;
}
// open directory and walk through the filenames
while ($file = readdir($handler))
{
// if file isn't this directory or its parent, add it to the results
if ( $file != "." && $file != ".." )
{
$results[] = $file;
}
}
// tidy up: close the handler
closedir($handler);
// done!
return $results;
}
function RetreivePxmlDataAndIcon( $filename )
{
// Make sure file exists.
if ( ! file_exists( $filename ) )
{
return array( false, false );
}
// Gets the file size.
$fileSize = filesize( $filename );
// If it is too short to even contain the begin/end PXML tags there is no hope.
if ( $fileSize < strlen( kPxmlBegin ) + strlen( kPxmlEnd ) )
{
return false;
}
$startOfPxmlData = false;
$endOfPxmlData = false;
// Open the file for binary read.
$handle = fopen($filename, "rb");
// Set the last read position to essentially be the end of the file.
$lastReadPosition = $fileSize - kPxmlOverlap;
// Until we have read all the way back to the start of the file.
while ( $lastReadPosition != 0 )
{
// Go back one buffer size, but nudge forward by the overlap amount.
$readPosition = max( $lastReadPosition - kReadBufferSize + kPxmlOverlap, 0 );
// Seek.
fseek( $handle, $readPosition );
// Read.
$data = fread( $handle, kReadBufferSize );
// If we haven't found the end tag yet.
if ( $endOfPxmlData === false )
{
// Look for end tag.
$ix = stripos( $data, kPxmlEnd );
// If found...
if ( $ix !== false )
{
// Store position.
$endOfPxmlData = $readPosition + $ix + strlen( kPxmlEnd );
}
}
// If we have foudn end tag, look for start tag.
if ( $endOfPxmlData !== false )
{
$ix = stripos( $data, kPxmlBegin );
if ( $ix !== false )
{
$startOfPxmlData = $readPosition + $ix;
// Once we have found start and end tags we can break out of while loop.
break;
}
}
// Save the last read position for next loop.
$lastReadPosition = $readPosition;
}
// Default our pxmlData to false.
$pxmlData = false;
$iconData = false;
// If we found begin/end PXML tags then pull out PXML data.
if ( $startOfPxmlData !== false && $endOfPxmlData !== false )
{
// Read pxml data.
fseek( $handle, $startOfPxmlData );
$pxmlData = fread( $handle, $endOfPxmlData - $startOfPxmlData );
// See if there is an icon after the data.
fseek( $handle, $endOfPxmlData );
// Read rest of file.
$data = fread( $handle, $fileSize - $endOfPxmlData );
// Search for PNG magic.
$ix = stripos( $data, kPngMagic );
if ( $ix !== false )
{
$iconData = substr( $data, $ix );
}
}
// Close file handle.
fclose( $handle );
// Return our results.
return array( $pxmlData, $iconData );
}
// ---------------------------------------------------------------------------------------------------------------------
class Translation
{
public $Title;
public $Description;
public function __construct( $title, $description )
{
$this->Title = $title;
$this->Description = $description;
}
}
// ---------------------------------------------------------------------------------------------------------------------
class PndInfo
{
public $AppId;
public $Version;
public $DownloadName;
public $Translations;
public function __construct( $appId, $version, $downloadName, $translations )
{
$this->AppId = $appId;
$this->Version = $version;
$this->DownloadName = $downloadName;
$this->Translations = $translations;
}
private function GetTranslation( $array, $region = none )
{
// If there are no items, return nothing.
if ( sizeof( $array ) === 0 )
{
return none;
}
// If no region is specified...
if ( $region === none )
{
if ( array_key_exists( "en_US", $array ) )
{
// Try en_US first.
return $array[ "en_US" ];
}
else if ( array_key_exists( "en_GB", $array ) )
{
// Next try en_GB.
return $array[ "en_GB" ];
}
else
{
// Return whatever we find first.
foreach ( $array as $item )
{
return $item;
}
}
}
// A region has been specified, so find exact match.
foreach ( $array as $key => $value )
{
if ( $key === $region )
{
return $value;
}
}
// Didn't find specified region.
return none;
}
public function GetTitle( $region = none )
{
return $this->GetTranslation( $this->Translations, $region )->Title;
}
public function GetDescription( $region = none )
{
return $this->GetTranslation( $this->Translations, $region )->Description;
}
}
// ---------------------------------------------------------------------------------------------------------------------
class Database
{
private $m_Host;
private $m_Username;
private $m_Password;
private $m_Database;
private $m_MainTable;
private $m_TranslationTable;
// Connect to database on construction.
public function __construct( )
{
$this->m_Host = localhost;
$this->m_Username = "root";
$this->m_Password = "";
$this->m_Database = "pandora-pnd";
$this->m_MainTable = "pnd-info";
$this->m_TranslationTable = "pnd-translation";
mysql_connect( $this->m_Host, $this->m_Username, $this->m_Password ) or die ( "Failed to connect to database" );
}
// Close connection.
function __destruct( )
{
mysql_close( );
}
private function Sql[quote=" $text "]
{
return '`' . $text . '`';
}[/quote] // Query current entries in database.
function Query( $appId = none )
{
$returnValues = array( );
mysql_select_db( $this->m_Database ) or die( "Unable to select database" );
$query = 'SELECT * ';
$query .= 'FROM ' . $this->Sql[quote=" $this->m_MainTable "] . ' ';
$query .= 'JOIN ' . $this->Sql[quote=" $this->m_TranslationTable "] . ' ON ' . $this->Sql[quote=" $this->m_MainTable "] . '.appid = ' . $this->Sql[quote=" $this->m_TranslationTable "] . '.appid';[/quote] // If we have been given an AppId to search for, then using this as the WHERE clause.
if ( $appId !== none )
{
$query .= " WHERE `" . $this->m_MainTable . "`.appid = '" . $appId . "'";
}
$result = mysql_query( $query );
$numRows = mysql_numrows( $result );
// Loop through results.
for ( $i = 0; $i < $numRows; $i += 1 )
{
// Pull out result data.
$currentAppId = mysql_result( $result, $i, "appid" );
$currentVersion = mysql_result( $result, $i, "version" );
$currentDownloadName = mysql_result( $result, $i, "download_name" );
$currentLocale = mysql_result( $result, $i, "locale" );
$currentTitle = mysql_result( $result, $i, "title" );
$currentDescription = mysql_result( $result, $i, "description" );
// Look for existing result (in our return array) for this application ID.
$foundExisingResult = false;
foreach ( $returnValues as $returnValue )
{
if ( $returnValue->AppId == $currentAppId )
{
// If we found one, then just add the current locale.
$returnValue->Translations[ $currentLocale ] = ( new Translation( $currentTitle, $currentDescription ) );
// Set found flag.
$foundExisingResult = true;
// Break out of loop.
break;
}
}
// If we did not find existing result, then create one now.
if ( $foundExisingResult === false )
{
$returnValues[] = new PndInfo(
$appId = $currentAppId,
$version = $currentVersion,
$downloadName = $currentDownloadName,
$translations = array(
$currentLocale => new Translation( $currentTitle, $currentDescription )
)
);
}
}
return $returnValues;
}
// Add a new entry if primary key, AppId, doesn't already exist in database.
function Add( $pndInfo )
{
// Make sure this entry doesn't already exist.
if ( sizeof( $this->Query( $pndInfo->AppId ) ) )
{
return false;
}
// Select main database.
mysql_select_db( $this->m_Database ) or die( "Unable to select database" );
// Add main info.
$query = 'INSERT INTO ' . $this->Sql[quote=" $this->m_Database "] . '.' . $this->Sql[quote=" $this->m_MainTable "] .
" (`appid`, `version`, `download_name`) VALUES (" .
"'" . $pndInfo->AppId . "', " .
"'" . $pndInfo->Version . "', " .
"'" . $pndInfo->DownloadName . "'" .
")";[/quote] mysql_query( $query ) or die ( "Failed to insert into database" );
// Add translations.
foreach ( $pndInfo->Translations as $region => $translation )
{
$query = 'INSERT INTO ' . $this->Sql[quote=" $this->m_Database "] . '.' . $this->Sql[quote=" $this->m_TranslationTable "] .
" (`appid`, `locale`, `title`, `description`) VALUES (" .
"'" . $pndInfo->AppId . "', " .
"'" . $region . "', " .
"'" . $translation->Title . "', " .
"'" . $translation->Description . "'" .
")";[/quote] mysql_query( $query ) or die ( "Failed to insert into database" );
}
}
}
// ---------------------------------------------------------------------------------------------------------------------
// Create connection to database.
$db = new Database( );
// Get list of all files to be processed.
$dirList = GetDirectoryList( kUploadDir );
// Go through each file.
foreach ( $dirList as $filename )
{
// If it is a .pnd then process.
if ( stripos( pathinfo( $filename, PATHINFO_EXTENSION ), "pnd" ) !== false )
{
// Get pxml data and icon.
list( $pxmlData, $iconData ) = RetreivePxmlDataAndIcon( kUploadDir . $filename );
// If data is valid...
if ( $pxmlData !== false )
{
// Process xml.
$xmlDoc = new DOMDocument( );
$xmlDoc->loadXML( $pxmlData );
$pxml = $xmlDoc->getElementsByTagName("PXML");
foreach ($pxml as $item)
{
$applications = $item->getElementsByTagName("application");
foreach ($applications as $application)
{
// AppId.
$AppId = $application->getAttribute( "id" );
// Version.
$Version = none;
$versions = $item->getElementsByTagName( "version" );
foreach ( $versions as $version )
{
$major = $version->getAttribute( "major" );
$minor = $version->getAttribute( "minor" );
$release = $version->getAttribute( "release" );
$build = $version->getAttribute( "build" );
$Version = "$major.$minor.$release.$build";
}
// Translation(s).
$Translations = array( );
$titles = $item->getElementsByTagName("title");
foreach ( $titles as $title )
{
$locale = $title->getAttribute( "lang" );
$Translations[ $locale ] = new Translation( $title->nodeValue, none );
}
$descriptions = $item->getElementsByTagName("description");
foreach ( $descriptions as $description )
{
$locale = $description->getAttribute( "lang" );
$title = none;
if ( array_key_exists( $description->getAttribute( "lang" ), $Translations ) )
{
$title = $Translations[ $locale ]->Title;
}
$Translations[ $locale ] = new Translation( $title, $description->nodeValue );
}
// Add to database.
$db->Add(
new PndInfo(
$AppId,
$Version,
basename( $filename ),
$Translations
)
);
}
// Create download folder of AppId.
$appDownloadDir = kDownloadDir . "/" . $AppId;
if ( ! file_exists( $appDownloadDir ) )
{
mkdir( $appDownloadDir );
}
rename( kUploadDir . "/" . $filename, $appDownloadDir . "/" . $filename );
}
if ( $iconData !== false )
{
$fh = fopen( kIconDir . "/" . $AppId . ".png", "wb" );
fwrite( $fh, $iconData );
fclose( $fh );
}
}
}
}
// ---------------------------------------------------------------------------------------------------------------------
// Dump out contents of database in default translation just to test!
$results = $db->Query( );
foreach ( $results as $result )
{
echo '<p>AppId : ' . $result->AppId . ' - Version : ' . $result->Version . ' - Title : ' . $result->GetTitle( ) . ' - Description : ' . $result->GetDescription( ) . '</p>';
$pngFilename = kIconDir . "/" . $result->AppId . ".png";
if ( file_exists( $pngFilename ) )
{
echo '[img]' . $pngFilename . '[/img]';
}
echo '<p><a href="' . kDownloadDir . '/' . $result->AppId . '/' . $result->DownloadName . '">download</a></p>';
}
?>
</body>
</html>