<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>