#native_company# #native_desc#
#native_cta#

class.cddb.php

By Private Ace
on June 8, 2003

Version: 1.0

Type: Class

Category: Other

License: GNU General Public License

Description: A small and quick class to get CDDB information using the Linux program cd-discid.

<?php

#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
###################                                               ###################
###################       Aces XMMS-PHP MP3 Jukebox v0.1.0        ###################
###################       --------------------------------        ###################
###################                                               ###################
###################     Based on XMMS-Control by Joe Thielen      ###################
###################                                               ###################
###################  Designed by Ace ([email protected])   ###################
###################                                               ###################
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
###        You may use or modify this program for any non-profit use              ###
###        as long as there is a the copyright notice displayed in a              ###
###                     comment inside the output html                            ###
###          (i.e. <!-- Generated by XMMS-PHP.                                    ###
###                    http://bassetts.port5.com (C) Ace 2003 -->)                ###
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
###                                                                               ###
###	PHPlibcddb 0.01 - PHP-based implementation of the CDDB protocol             ###
###	Copyright (C) 2003  David Jonathan Grant                                    ###
###                                                                               ###
###	This program is free software; you can redistribute it 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 more details.                                ###
###                                                                               ###
###	You should have received a copy of the GNU General Public License           ###
###	along with this program; if not, write to the Free Software                 ###
###	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. ###
###                                                                               ###
###	David Jonathan Grant <[email protected]>                   ###
###                                                                               ###
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
###############                                                       ###############
###############               CDDB Connectivity Class                 ###############
###############                                                       ###############
###############                   Designed by: Ace.                   ###############
###############              <[email protected]>               ###############
###############                                                       ###############
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
####                                                                             ####
####  Usage Example:                                                             ####
####  --------------                                                             ####
####                                                                             ####
####        echo "<PRE>";                                                        ####
####                                                                             ####
####   	include("classes/class.cddb.php");                                   ####
####                                                                             ####
####        $cddb = new cddb();                                                  ####
####        $cddb->connect();                                                    ####
####        $cddb->protocol(5);                                                  ####
####                                                                             ####
####        print_r($cddb->genres());                                            ####
####        print_r($cddb->help("discid"));                                      ####
####        print_r($cddb->log());                                               ####
####        print_r($cddb->status());                                            ####
####        $info = $cddb->import();                                             ####
####        $data = $cddb->query($info['discid'], $info['tracks'],               ####
####                              $info['timing'], $info['length']);             ####
####        print_r($data[0]);                                                   ####
####        print_r($cddb->read($data[0]['category'], $info['discid']));         ####
####        print_r($cddb->motd());                                              ####
####        print_r($cddb->version());                                           ####
####        $cddb->disconnect();                                                 ####
####        print_r($cddb->messages);                                            ####
####                                                                             ####
####        echo "</PRE>";                                                       ####
####                                                                             ####
#####################################################################################
#####################################################################################

class cddb
{
	var $connection = NULL;
	var $messages = NULL;

	function code($response)
	{
		return (substr($response, 0, 3));
	}

	function connect($servernumber = 0)
	{
		$serverport = 8880;
		$server[0] = "freedb.freedb.org";	// Random freedb server
		$server[1] = "freedb.freedb.de";	// Dortmund, Germany
		$server[2] = "at.freedb.org";		// Vienna, Austria
		$server[3] = "au.freedb.org";		// Sydney, Australia
		$server[4] = "bg.freedb.org";		// Sofia, Bulgaria
		$server[5] = "ca.freedb.org";		// Winnipeg, MB Canada
		$server[6] = "de.freedb.org";		// Berlin, Germany
		$server[7] = "es.freedb.org";		// Madrid, Spain
		$server[8] = "lu.freedb.org";		// Betzdorf, Luxemburg
		$server[9] = "uk.freedb.org";		// London, UK
		$server[10] = "us.freedb.org";		// San Jose, CA USA

		if (!isset($this->serverport))
		{
			$this->serverport = 8880;
		}
		if (!isset($servernumber))
		{
			$servernumber = 0;
		}
		// Connect to the central FreeDB server.
		$this->connection = fsockopen($this->server[$this->servernumber], $this->serverport);

		// Checck to see if the socket connected properly.
		if (is_resource($this->connection) == FALSE)
		{
			return (FALSE);
		}
		else
		{
			//$code = $this->code($this->parse(1));
			switch ($code = $this->code($this->parse(1)))
			{
				case 200:
					$this->messages[] = "200: OK, Read/Write Allowed.";
					return ($this->hello());
					break;
				case 201:
					$this->messages[] = "200: OK, Read Only.";
					return ($this->hello());
					break;
				case 432:
					$this->messages[] = "432: No Connections Allowed: Permission Denied.";
					return (FALSE);
				case 433:
					$this->messages[] = "433: No Connections Allowed: X Users Allowed, Y Currently Active.";
					return (FALSE);
				case 434:
					$this->messages[] = "434: No Connections Allowed: System Load Too High.";
					return (FALSE);
				default:
					return ($this->errors($code));
			}
		}
	}

	function discid($tracks, $offsets, $time)
	{
		$this->send("discid " . $tracks . " " . implode(" ", $offsets) . " " . $time);
		$header = $this->parse(1);
		switch ($code = $this->code($header))
		{
			case 200:
				$this->messages[] = "200: Calculated Disc ID Properly.";
				preg_match("/.+ (S+)^$/", $header, $matches);
				return ($matches[1]);
			default:
				return ($this->errors($code));
		}
	}

	function disconnect()
	{
		// Disconnect from server.
		$this->send("quit");
		switch ($code = $this->code($this->parse(1)))
		{
			case 230:
				$this->messages[] = "230: OK, Goodbye.";
				fclose($this->connection);
				return (TRUE);
			default:
				return ($this->errors($code));
		}
	}

	function errors($code)
	{
		// Handle general error status codes from other functions.
		switch ($code)
		{
			case 402:
				$this->messages[] = "402: Server Error.";
				return (FALSE);
			case 408:
				$this->messages[] = "408: CGI Environment Error.";
				return (FALSE);
			case 500:
				$this->messages[] = "500: Command Syntax Error.";
				return (FALSE);
			case 530:
				$this->messages[] = "503: Server Error, Server Timeout.";
				return (FALSE);
			default:
				$this->messages[] = "Received Unrecognised Code (" . $code . ")";
				return (FALSE);
		}
	}

	function genres()
	{
		// Return a list of muscial genres.
		$this->send("cddb lscat");;
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: OK, Category List Follows.";
				return ($this->parse(0));
			default:
				return ($this->errors($code));
		}
	}

	function hello()
	{
		// Handshake
		$sys = posix_uname();
		$usr = posix_getpwuid(posix_geteuid());

		// Send 'hello' command to the conneccted server.
		$this->send("cddb hello " . $usr['name'] . " " . $sys['nodename'] . " PHPlibcddb 0.01");
		switch ($code = $this->code($this->parse(1)))
		{
			case 200:
				$this->messages[] = "200: OK, Handshake Successful.";
				// Handshake was completed successfully.
				return (TRUE);
			case 402:
				// Not *really* a problem, but it is important to know if this is occurring frequently.
				$this->messages[] = "402: Already Shook Hands.";
				return (TRUE);
			case 431:
				//
				$this->messages[] = "431: Handshake Not Successful. Closing Connection.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function help($topic)
	{
		// Request help from server
		$this->send("help " . $topic);

		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: OK, Help Information Follows.";
				return ($this->parse(0));
			case 401:
				$this->messages[] = "401: No Help Information Available.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function import()
	{
		// Grab the CD information from the local machine.
		// This will only work on Unix-based machines with cd-discid.
		$data = explode(" ", `cd-discid /dev/cdrom`);
		$info['discid'] = array_shift($data);
		$info['tracks'] = array_shift($data);
		$info['length'] = array_pop($data);
		$info['timing'] = $data;
		return ($info);
	}

	function log()
	{
		// Request usagee log from the server.
		$this->send("log");
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: OK, Log Summary Follows.";
				return ($this->parse(0));
			case 211:
				$this->messages[] = "211: OK Log Follows.";
				return ($this->parse(0));
			case 401:
				$this->messages[] = "401: Permission Denied.";
				return (FALSE);
			case 402:
				$this->messages[] = "402: No Log Information Available.";
				return (FALSE);
			case 501:
				$this->messages[] = "501: Invalid Start/End Date.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function motd()
	{
		// Request the Message of the Day
		$this->send("motd");
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: Last Modified MM/DD/YYYY HH:MM:SS. MOTD Follows.";
				return ($this->parse(0));
			case 401:
				$this->messages[] = "401: No Message of the Day Available.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function parse($lines)
	{
		// Check if read is for multiple (0) or single (1) lines.
		if ($lines == 1)
		{
			$response = "";
			// Loop through the response
			while (feof($this->connection) == FALSE)
			{
				// Read a character from the response.
				$character = fgetc($this->connection);
				// Continue until we get a carriage return.
				if ($character == chr(13))
				{
					break;
				}
				// Add current character to response string.
				if (empty($response))
				{
					$response = $character;
				}
				else
				{
					$response = $response . $character;
				}
			}
			return (trim($response));
		}
		else
		{
			$offset = 0;
			$response = array();
			while (feof($this->connection) == FALSE)
			{
				// Prevent PHP throwing an error.
				if (array_key_exists($offset, $response) == FALSE)
				{
					// Populate current array offset
					$response[$offset] = "";
					// Remove the NL character from the response.
					fgetc($this->connection);
				}
				// Read a character from the response.
				$character = fgetc($this->connection);
				// Check for an ASCII CR character.
				if ($character == chr(13))
				{
					// Add a new line to the array
					$response[$offset] = trim($response[$offset]);
					$offset++;
					continue;
				}
				// If the character is a fullstop (.) and is at the beginning of the line, delete the row, and exit.
				// Reasoning:  a fullstop (.) is the terminating character for multiple line responses.
				if ($character == chr(46)  && strlen($response[$offset]) == 0)
				{
					unset($response[$offset]);
					// Grab what *SHOULD* be the last character (CR) from the response.
					fgetc($this->connection);
					break;
				}
				else
				{
					// Add the character to the current row in the reponse array.
					if (empty($response[$offset]))
					{
						$response[$offset] = $character;
					}
					else
					{
						$response[$offset] = $response[$offset] . $character;
					}
				}
			}
			return ($response);
		}
	}

	function protocol($protocol)
	{
		// Change the current CDCDP level.
		$this->send("proto " . $protocol);
		switch ($code = $this->code($this->parse(1)))
		{
			case 200:
				$this->messages[] = "200: CDDB Protocol Level, Current Level, Current Supported Level.";
				return (TRUE);
			case 201:
				$this->messages[] = "201: OK, Protocol Level Now Current Level.";
				return (TRUE);
			case 501:
				$this->messages[] = "501: Illegal Protocol Level.";
				return (FALSE);
			case 502:
				$this->messages[] = "502: Protocol Level Already Current Level.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function query($discid, $tracks, $offset, $length)
	{
		// Find matches for CD data.
		$this->send("cddb query " . $discid . " " . $tracks . " " . implode(" ", $offset) . " " . $length);
		$header = $this->parse(1);
		switch($code = $this->code($header))
		{
			case 200:
				$this->messages[] = "200: Found Exact Match.";
				preg_match("/^d{3} (S+) (S+) (.+/.+)$/", $header, $matches);
				$results[0]['category'] = $matches[1];
				$results[0]['discid'] = $matches[2];
				$results[0]['dtitle'] = $matches[3];
				return ($results);
			case 202:
				$this->messages[] = "202: No Match Found.";
				return (FALSE);
			case 210:
				$this->messages[] = "210: Found Exact Matches. List Follows.";
				foreach ($this->parse(0) as $offset => $data)
				{
					// Extract CD information from response.
					preg_match("/^(S+) (S+) (.+/.+)$/", $data, $matches);
					$results[$offset]['category'] = $matches[1];
					$results[$offset]['discid'] = $matches[2];
					$results[$offset]['dtitle'] = $matches[3];
				}
				return ($results);
			case 211:
				$this->messages[] = "211: Found Inexact Matches. List Follows.";
				foreach ($this->parse(0) as $offset => $data)
				{
					// Extract CD information from response.
					preg_match("/^(S+) (S+) (.+/.+)$/", $data, $matches);
					$results[$offset]['category'] = $matches[1];
					$results[$offset]['discid'] = $matches[2];
					$results[$offset]['dtitle'] = $matches[3];
				}
				return ($results);
			case 403:
				$this->messages[] = "403: Database Entry is Corrupt.";
				return (FALSE);
			case 409:
				$this->messages[] = "409: No Handshake.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function read($category, $discid)
	{
		// Fetch the track data corresponding to the passed CD data.
		$this->send("cddb read " . $category . " " . $discid);
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "200: OK, CDDB Database Entry Follows.";
				return ($this->parse(0));
			case 401:
				$this->messages[] = "401: Specified CDDB Entry Not Found.";
				return (FALSE);
			case 402:
				$this->messages[] = "402: Server Error.";
				return (FALSE);
			case 403:
				$this->messages[] = "403: Database Entry is Corrupt.";
				return (FALSE);
			case 409:
				$this->messages[] = "409: No Handshake.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function send($request)
	{
		// Wrapper for sending request.
		fputs($this->connection, $request . "n");
	}

	function sites()
	{
		// List all the alternative servers.
		$this->send("sites");
		// Check the status code.
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: OK, Site Information Follows.";
				// Read in all sites.
				$sites = $this->parse(0);
				// Loop through server list and extract different parts.
				foreach ($sites as $offset => $data)
				{
					preg_match("/^(.+) (.+) (d+) (.+) ([N|S]d{3}.d{2}) ([E|W]d{3}.d{2}) (.+)/", $data, $matches);
					$servers[$offset]['hostname'] = $matches[1];
					$servers[$offset]['protocol'] = $matches[2];
					$servers[$offset]['portno'] = $matches[3];
					$servers[$offset]['url'] = $matches[4];
					$servers[$offset]['latitude'] = $matches[5];
					$servers[$offset]['longitude'] = $matches[6];
					$servers[$offset]['name'] = $matches[7];
				}
				return ($servers);
			case 401:
				$this->messages[] = "401: No Site Information Available.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function status()
	{
		// Extract server status information
		$this->send("stat");
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: OK, Status Information Follows.";
				return ($this->parse(0));
			default:
				return ($this->errors($code));
		}
	}

	function update()
	{
		// Ask server to update the database.
		$this->send("update");
		switch ($code = $this->code($this->parse(1)))
		{
			case 200:
				$this->messages[] = "200: Updating the Database.";
				return (TRUE);
			case 401:
				$this->messages[] = "401: Permission Denied.";
				return (FALSE);
			case 402:
				$this->messages[] = "402: Unable to Update the Database.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function users()
	{
		// List all the current users attached to the server.
		$this->send("whom");
		switch ($code = $this->code($this->parse(1)))
		{
			case 210:
				$this->messages[] = "210: OK, User List Follows.";
				return ($this->parse(0));
			case 401:
				$this->messages[] = "401: No User Information Available.";
				return (FALSE);
			default:
				return ($this->errors($code));
		}
	}

	function version()
	{
		// Request server software version information.
		$this->send("ver");
		$header = $this->parse(1);
		switch ($code = $this->code($header))
		{
			case 200:
				$this->messages[] = "200: Version Information.";
				$version[0] = $header;
				return ($version);
			case 201:
				$this->messages[] = "201: Version Information Follows.";
				$version = $this->parse(0);
				return ($version);
			default:
				return ($this->errors($code));
		}
	}
}

?>