#!/usr/bin/php
<?php

include("PEAR/PackageFile.php");
include("PEAR/ChannelFile.php");

function usage() {
	echo "Usage: ".$_SERVER["argv"][0]." [options] package|channel|summary|description|maintainers dir\n";
	echo "       ".$_SERVER["argv"][0]." [options] date|version|license|changelog|all|hasphpscript dir\n";
	echo "       ".$_SERVER["argv"][0]." [options] debian_deps dir\n";
	echo "       ".$_SERVER["argv"][0]." [options] debian_pkgname channel_url pear_name\n";
	echo "       ".$_SERVER["argv"][0]." [options] debian_version version\n";
	echo "       ".$_SERVER["argv"][0]." [options] channel_name|channel_summary|channel_alias channel.xml\n";
	echo "\ncommands:\n";
	echo "  package         - Name of package\n";
	echo "  channel         - PEAR channel URL\n";
	echo "  summary         - Short summary of package\n";
	echo "  description     - Long description of package\n";
	echo "  maintainers     - Comma separated list of maintainers\n";
	echo "  date            - Date of release\n";
	echo "  version         - Version of package\n";
	echo "  license         - License of package\n";
	echo "  changelog       - Full changelog of package\n";
	echo "  all             - print_r() of complete package file\n";
	echo "  hasphpscript    - Check if package contains files of type 'script'\n";
	echo "  debian_deps     - Print dependencies as Debian control fields\n";
	echo "  debian_pkgname  - Translate a PEAR package name to a Debian package name\n";
	echo "  debian_version  - Translate a PEAR version to a Debian version\n";
	echo "  channel_name    - Get channel name\n";
	echo "  channel_summary - Get channel summary\n";
	echo "  channel_alias   - Get channel alias\n";
	echo "\narguments:\n";
	echo "  dir             - Directory containing package.xml or package2.xml file\n";
	echo "\noptions:\n";
	echo "  -d              - print debug to STDERR\n";
}

function printLog($message) {
	global $debug;
	if ($debug) {
		if ($handle = fopen("php://stderr", "a")) {
			if (!is_object($message)) {
				$message = PEAR::raiseError($message);
			}
			fwrite($handle, $message->toString()."\n");
			//var_dump($message->getUserInfo());
			fclose($handle);
		}
	}
}

function checkForPhpScript($pf, $content = NULL) {
	if(get_class($pf) == "PEAR_PackageFile_v1") {
		$filelist = $pf->getFilelist();
		foreach($filelist as $item) {
			if($item['role'] == 'script') {
				return 1;
			}
		}
		return 0;
	} elseif(get_class($pf) == "PEAR_PackageFile_v2") {
		if ($content === NULL) {
			$content = $pf->getContents();
		}
		foreach($content as $name=>$item) {
			if($name == 'dir') {
				return checkForPhpScript($pf, $item);
			} elseif($name == 'file') {
				foreach($item as $file) {
					if($file['attribs']['role'] == 'script') {
						return 1;
					}
				}
			}
		}
		return 0;
	} else {
		printLog("Unknown PEAR_PackageFile version");
		exit(1);
	}

}

function pearPackageNameToDebianPackageNameOverridesFile($overrides_file, $channel_url, $package_name) {
	if (file_exists($overrides_file)) {
		$fh = fopen($overrides_file, 'r');
		if ($fh === false) {
			printLog("Unable to open '$overrides_file'");
			exit(1);
		}
		while (($line = fgets($fh)) !== false) {
			$fields = preg_split("/[\s]+/", $line);
			if (count($fields) < 3) {
				printLog("Ignoring line, too short: '$line'");
				continue;
			}
			if (($fields[0] === $channel_url) && ($fields[1] === $package_name)) {
				printLog("Overriding:'{$fields[0]}' '{$fields[1]}' -> '{$fields[2]}'");
				return $fields[2];
			}
		}
		if (!feof($fh)) {
			printLog("Unable to read '$overrides_file'");
			exit(1);
		}
		fclose($fh);
	}
	return false;
}

function pearPackageNameToDebianPackageNameOverrides($channel_url, $package_name) {
	$overrides = pearPackageNameToDebianPackageNameOverridesFile('debian/pkg-php-tools-overrides', $channel_url, $package_name);
	if ($overrides) {
		return $overrides;
	}
	$overrides_dir = '/usr/share/pkg-php-tools/overrides/';
	if (is_dir($overrides_dir)) {
		if ($dh = opendir($overrides_dir)) {
			while (($file = readdir($dh)) !== false) {
				if (($file == '.') || ($file == '..')) {
					continue;
				}
				$overrides = pearPackageNameToDebianPackageNameOverridesFile($overrides_dir.$file, $channel_url, $package_name);
				if ($overrides) {
					return $overrides;
				}
			}
			closedir($dh);
		} else {
			printLog("Unable to open '$overrides_dir'");
			exit(1);
		}
	}
	return false;
}

function pearPackageNameToDebianPackageName($channel_url, $package_name, $prefix = NULL, $shift_from_url = Array("pear", "pecl", "www")) {
	global $builtin_extensions;
	$overriden = pearPackageNameToDebianPackageNameOverrides($channel_url, $package_name);
	if ($overriden) {
		return $overriden;
	}
	//put everything lowercase
	$package_name = strtolower($package_name);
	$channel_url = strtolower($channel_url);

	// split channel url by dots:
	$urlc = explode(".", $channel_url);
	// split package name by underscores:
	$packagec = explode("_", $package_name);
	// drop last part of url (TLD):
	array_pop($urlc);
	// drop first part of url if equal to pear or www
	if (isset($urlc[0]) && in_array($urlc[0], $shift_from_url)) {
		if (($urlc[0] == "pecl") && ($prefix === NULL)) {
			if (in_array(strtolower($package_name), $builtin_extensions)) {
				return 'builtin';
			}
			$prefix = "php5-"; // Wild guess
		}
		array_shift($urlc);
	}
	// drop first part of url if equal to php
	if (isset($urlc[0]) && ($urlc[0] == "php")) {
		array_shift($urlc);
	}
	// drop first part of package if equal to last part of url (should be done several times?)
	if (end($urlc) == $packagec[0]) {
		array_shift($packagec);
	}
	//merge the result
	return (($prefix === NULL) ? "php-" : $prefix) .implode('-', array_merge($urlc, $packagec));
}
function pearExtensionNameToDebianPackageName($extension_name, $prefix = "php5-") {
	global $builtin_extensions;
	if (in_array(strtolower($extension_name), $builtin_extensions)) {
		return 'builtin';
	}
	return $prefix.strtolower($extension_name);
}

function pearVersionToDebianVersion($version) {
	$debian_version = "";
	$tmp = explode(".",strtolower($version));
	foreach($tmp as $component) {
		if (preg_match('/^\d+$/', $component)) {
			$debian_version .= "$component.";
		} elseif (preg_match('/^(\d+)(alpha\d*|a\d*|beta\d*|b\d*|rc\d*)$/', $component, $matches)) {
			$debian_version .= "$matches[1]~$matches[2].";
		} else {
			$debian_version .= "?not supported($component)?."; # TODO
		}
	}
	return substr($debian_version, 0, -1);
}

function loadPackageFile($packagefilename) {
	$config = new PEAR_Config();
	if ($config instanceof PEAR_Error) {
		printLog($config);
		exit(1);
	}
	$pkg = new PEAR_PackageFile($config);
	if ($pkg instanceof PEAR_Error) {
		printLog($pkg);
		exit(1);
	}
	// Check first for package.xml or package2.xml in subdirs, to have correct relative dirs
	if (is_dir($packagefilename)) {
		$dir_name = realpath($packagefilename);
		$d = dir($dir_name);
		while (false !== ($entry = $d->read())) {
			if ($entry != "." && $entry != ".." && is_dir("$dir_name/$entry")) {
				if (is_file("$dir_name/$entry/package2.xml")) {
					$packagefilename = "$dir_name/$entry/package2.xml";
				} elseif (is_file("$dir_name/$entry/package.xml")) {
					$packagefilename = "$dir_name/$entry/package.xml";
				}
			}
		}
		$d->close();
	}
	$pf = $pkg->fromAnyFile($packagefilename, 0);
	if ($pf instanceof PEAR_Error) {
		printLog($pf);
		exit(1);
	}

	return $pf;
}

function loadChannelFile($channelfilename) {
	$config = new PEAR_Config();
	if ($config instanceof PEAR_Error) {
		printLog($config);
		exit(1);
	}
	$chan = new PEAR_ChannelFile($config);
	if ($chan instanceof PEAR_Error) {
		printLog($chan);
		exit(1);
	}
	PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
	$result = $chan->fromXmlFile($channelfilename);
	PEAR::staticPopErrorHandling();
	if (!$result) {
		$exit = false;
		if (count($errors = $chan->getErrors(true))) {
			foreach ($errors as $error) {
				printLog(ucfirst($error['level'] . ': ' . $error['message']));
				if (!$exit) {
					$exit = $error['level'] == 'error' ? true : false;
				}
			}
			if ($exit) {
				printLog('Exiting: invalid channel.xml file');
				exit(1);
			}
		}
	}

	return $chan;
}
$debug = false;
$builtin_extensions = Array(
	// Compiled in extensions (static)
	'bcmath', 'bz2', 'calendar', 'core', 'ctype', 'date',
	'dba', 'dom', 'ereg', 'exif', 'fileinfo', 'filter', 'ftp', 'gettext', 'hash', 'iconv', 'json', 'libxml',
	'mbstring', 'mhash', 'openssl', 'pcre', 'phar', 'posix', 'reflection', 'session', 'shmop', 'simplexml',
	'soap', 'sockets', 'spl', 'standard', 'sysvmsg', 'sysvsem', 'sysvshm', 'tokenizer', 'wddx', 'xml',
	'xmlreader', 'xmlwriter', 'zip', 'zlib',
	// Other compiled extensions (dynamic)
	'pdo',
	// (extensions are lower-cased, see #696743)
	);
$args = $_SERVER["argv"];
array_shift($args); // script name

if (!empty($args[0]) && ($args[0] == "-d")) {
	$debug = true;
	array_shift($args);
}

if(count($args) < 2) {
	usage();
	exit;
}
$command = array_shift($args);

switch($command) {
	case "package":
		$pf = loadPackageFile($args[0]);
		echo $pf->getPackage();
		break;
	case "channel":
		$pf = loadPackageFile($args[0]);
		echo $pf->getChannel();
		break;
	case "summary":
		$pf = loadPackageFile($args[0]);
		echo $pf->getSummary();
		break;
	case "description":
		$pf = loadPackageFile($args[0]);
		echo $pf->getDescription();
		break;
	case "maintainers":
		$pf = loadPackageFile($args[0]);
		$tmp = Array();
		foreach($pf->getMaintainers() as $maintainer) {
			if (in_array($maintainer["role"], Array("lead", "developer"))) {
				$tmp[] = $maintainer["name"];
			}
		}
		print implode(", ", $tmp);
		break;
	case "date":
		$pf = loadPackageFile($args[0]);
		echo $pf->getDate();
		break;
	case "version":
		$pf = loadPackageFile($args[0]);
		echo $pf->getVersion();
		break;
	case "license":
		$pf = loadPackageFile($args[0]);
		echo $pf->getLicense();
		break;
	case "changelog":
		$pf = loadPackageFile($args[0]);
		echo "Version ".$pf->getVersion()." - ".$pf->getDate()." (".$pf->getState().")\n";
		echo "----------------------------------------\n";
		echo "Notes:\n";
		echo "  ".str_replace("\n", "\n  ", wordwrap(ereg_replace("[[:space:]]+", " ", $pf->getNotes())))."\n\n";
		if(get_class($pf) == "PEAR_PackageFile_v1") {
			foreach($pf->_packageInfo["changelog"] as $changelog) {
				echo "Version ".$changelog["version"]." - ".$changelog["release_date"]." (".$changelog["release_state"].")\n";
				echo "----------------------------------------\n";
				echo "Notes:\n";
				echo "  ".str_replace("\n", "\n  ", wordwrap(ereg_replace("[[:space:]]+", " ", $changelog["release_notes"])))."\n\n";
			}

		} elseif( get_class($pf) == "PEAR_PackageFile_v2") {
			if(isset($pf->_packageInfo["changelog"])){
				$tmparr = $pf->_packageInfo["changelog"]["release"];
				if(array_key_exists("version", $tmparr)) {
					$tmparr = Array($tmparr);
				}
				$tmparr = array_reverse($tmparr);
				foreach($tmparr as $changelog) {
					echo "Version ".$changelog["version"]["release"]." - ".$changelog["date"]." (".$changelog["stability"]["release"].")\n";
					echo "----------------------------------------\n";
					echo "Notes:\n";
					echo "  ".str_replace("\n", "\n  ", wordwrap(ereg_replace("[[:space:]]+", " ", $changelog["notes"])))."\n\n";
				}
			}
		} else {
			printLog("Unknown PEAR_PackageFile version");
			exit(1);
		}
		break;
	case "all":
		$pf = loadPackageFile($args[0]);
		print_r($pf->_packageInfo);
		break;
	case "hasphpscript":
		$pf = loadPackageFile($args[0]);
		echo checkForPhpScript($pf);
		break;
	case "debian_deps":
		$pf = loadPackageFile($args[0]);
		$deps = Array();
		if(get_class($pf) == "PEAR_PackageFile_v1") {
			if ($pf->hasDeps()) {
				printLog("debian_deps is not supported for package.xml version 1.0");
			}
		} elseif( get_class($pf) == "PEAR_PackageFile_v2") {
			$dependencies = &$pf->getDependencies();
			foreach($dependencies as $level => $dep) {
				foreach($dep as $type => $tmp) {
					if (($level == "required") or ($level == "optional")) {
						if (key($tmp) !== 0) {
							$tmp = Array($tmp);
						}
						foreach($tmp as $infos) {
							if (!array_key_exists("exclude", $infos)) {
								$infos["exclude"] = Array();
							} elseif (!is_array($infos["exclude"])) {
								$infos["exclude"] = Array($infos["exclude"]);
							}
							switch($type) {
								case "pearinstaller":
									$package = "php-pear";
									break;
								case "php":
									if (checkForPhpScript($pf)) {
										$package = "php5-cli";
									} else {
										$package = "php5";
									}
									break;
								case "package":
									$package = pearPackageNameToDebianPackageName($infos["channel"], $infos["name"]);
									break;
								case "extension":
									$package = pearExtensionNameToDebianPackageName($infos["name"]);
									break;
								case "subpackage":
								case "os":
								case "arch":
								default:
									printLog("Tag '$type' not supported in $level $type: ".print_r($infos, true));
									continue 2;
							}
							if (($package === 'builtin') || ($package === 'none')) {
								continue;
							}
							if (array_key_exists("min", $infos) || array_key_exists("max", $infos)) {
								$tmp2 = $package;
								if (array_key_exists("min", $infos)) {
									if (in_array($infos["min"], $infos["exclude"])) {
										$operator = ">>";
										$infos["exclude"] = array_diff($infos["exclude"], Array($infos["min"]));
									} else {
										$operator = ">=";
									}
									$package = "$tmp2 ($operator ".
										pearVersionToDebianVersion($infos["min"]).
										")";
								}
								if (array_key_exists("max", $infos)) {
									if (!empty($package)) {
										$package = "$package, ";
									}
									if (in_array($infos["max"], $infos["exclude"])) {
										$operator = "<<";
										$infos["exclude"] = array_diff($infos["exclude"], Array($infos["max"]));
									} else {
										$operator = "<=";
									}
									$package = "$package$tmp2 ($operator ".
										pearVersionToDebianVersion($infos["max"]).
										")";
								}
							}
							# TODO: recommended
							if (!empty($infos["recommended"])) {
								printLog("Tag 'recommended' not supported in $level $type $package: ".print_r($infos["recommended"], true));
							}
							# TODO: exclude
							if (!empty($infos["exclude"])) {
								printLog("Tag 'exclude' not supported in $level $type $package: ".print_r($infos["exclude"], true));
							}
							if (array_key_exists("conflicts", $infos)) {
								$deps["conflicts"][] = $package;
							} else {
								$deps[$level][] = $package;
							}
						}
					}
				}
			}
		} else {
			printLog("Unknown PEAR_PackageFile version");
			exit(1);
		}
		if (array_key_exists("required", $deps)) {
			print "Depends: ".implode(", ", $deps["required"])."\n";
		}
		if (array_key_exists("optional", $deps)) {
			print "Recommends: ".implode(", ", $deps["optional"])."\n";
		}
		if (array_key_exists("conflicts", $deps)) {
			print "Breaks: ".implode(", ", $deps["conflicts"])."\n";
		}
	break;
	case "debian_pkgname":
		if(count($args) != 2) {
			usage();
			exit;
		}
		if ($args[0] === 'extension') {
			$pkg = pearExtensionNameToDebianPackageName($args[1]);
		} else {
			$pkg = pearPackageNameToDebianPackageName($args[0], $args[1]);
		}
		if (($pkg === 'builtin') || ($pkg === 'none')) {
			$pkg = '';
		}
		echo $pkg;
		exit;
		break;
	case "debian_version":
		if(count($args) != 1) {
			usage();
			exit;
		}
		echo pearVersionToDebianVersion($args[0]);
		exit;
		break;
	case "channel_name":
		if(count($args) != 1) {
			usage();
			exit;
		}
		$cf = loadChannelFile($args[0]);
		echo $cf->getName();
		exit;
		break;
	case "channel_summary":
		if(count($args) != 1) {
			usage();
			exit;
		}
		$cf = loadChannelFile($args[0]);
		echo $cf->getSummary();
		exit;
		break;
	case "channel_alias":
		if(count($args) != 1) {
			usage();
			exit;
		}
		$cf = loadChannelFile($args[0]);
		echo $cf->getAlias();
		exit;
		break;
	default:
		printLog("Unknown command $command");
		exit(1);

}

?>
