<?php defined('SYSPATH') or die('No direct access allowed.');

/**
 * This class is for access to SSL information
 *
 * @package    SSL
 * @category   Helpers
 * @author     Deon George
 * @copyright  (c) 2009-2013 Open Source Billing
 * @license    http://dev.osbill.net/license.html
 */
class SSL {
	private $cert = '';
	private $_details = array();

	public function __construct($cert) {
		$this->cert = $cert;
	}

	public static function instance($cert) {
		return new SSL($cert);
	}

	/**
	 * This function will convert a large decimal number into hex
	 * @param $number Large decimal number
	 */
	private static function _dec_to_hex($number) {
		$hex = array();

		if ($number == 0)
			return '00';

		while ($number > 0) {
			if ($number == 0) {
				array_push($hex, '0');

			} else {
				$x = (int) ($number/16);
				array_push($hex,strtoupper(dechex((int)($number-($x*16)))));
				$number = $x;
			}
		}

		return preg_replace('/^:/','',preg_replace('/(..)/',":$1",implode(array_reverse($hex))));
	}

	/**
	 * Parse our AuthorityKeyIndentifier Extension to extract information
	 * @param $key Return just that index
	 */
	private function _aki($key=NULL) {
		$result = array();

		$aki = $this->_extensions('authorityKeyIdentifier');
		if (! $aki)
			return '';

		foreach (explode("\n",preg_replace("/\n$/",'',$aki)) as $x) {
			if (! $x)
				continue;

			if (strstr($x,':')) {
				list($a,$b) = explode(':',$x,2);
				$result[strtolower($a)] = $b;
			}
		}

		return is_null($key) ? $result : (isset($result[$key]) ? $result[$key] : '');
	}

	private function _bc() {
		return $this->_extensions('basicConstraints');
	}

	/**
	 * Parse our Sign Certifcate to extract information
	 * @param $key Return just that index
	 */
	private function _details($key=NULL) {
		if (! $this->cert)
			return array();

		if (! $this->_details)
			$this->_details = openssl_x509_parse($this->cert);

		return is_null($key) ? $this->_details : (isset($this->_details[$key]) ? $this->_details[$key] : array());
	}

	/**
	 * Parse our Sign Certifcate Extensions to extract information
	 * @param $key Return just that index
	 */
	private function _extensions($key=NULL) {
		$result = $this->_details('extensions');

		return is_null($key) ? $result : (isset($result[$key]) ? $result[$key] : '');
	}

	/**
	 * Render a DN array as a string
	 */
	private function _dn(array $array) {
		$result = '';
		$i = 0;

		foreach ($array as $k=>$v) {
			if ($i++)
				$result .= ',';

			$result .= sprintf('%s=%s',$k,$v);
		}

		return $result;
	}

	public function get_aki_dirname() {
		return $this->_aki('dirname');
	}

	public function get_aki_keyid() {
		return $this->_aki('keyid');
	}

	public function get_aki_serial() {
		return $this->_aki('serial');
	}

	public function get_algorithm() {
		$e = '';
		openssl_x509_export(openssl_x509_read($this->cert),$e,FALSE);

		// @todo There must be a nice way to get this?
		return (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match)) ? $match[1] : _('Unknown');
	}

	public function get_ca_path_len() {
		$m = array();
		$x = preg_match('/.*pathlen:\s*([0-9]+).*$/',$this->_bc(),$m);

		return isset($m[1]) ? (int)$m[1] : 0;
	}

	public function get_dn() {
		return $this->_dn($this->_details('subject'));
	}

	public function get_hash() {
		return $this->_details('hash');
	}

	public function get_isCA() {
		return preg_match('/CA:TRUE/',$this->_bc()) ? TRUE : FALSE;
	}

	public function get_isCert() {
		return is_array($this->_details()) ? TRUE : FALSE;
	}

	public function get_isRoot() {
		return $this->get_aki_keyid() == $this->get_ski();
	}

	public function get_issuer() {
		$k = $this->_details('issuer');

		return isset($k['CN']) ? $k['CN'] : '';
	}

	public function get_issuerdn() {
		return $this->_dn($this->_details('issuer'));
	}

	public function get_serial() {
		return $this->_dec_to_hex($this->_details('serialNumber'));
	}

	public function get_subject() {
		$k = $this->_details('subject');

		return isset($k['CN']) ? $k['CN'] : '';
	}

	public function get_ski() {
		return $this->_extensions('subjectKeyIdentifier');
	}

	public function get_valid_to($format=FALSE) {
		$k = $this->_details('validTo_time_t');

		return $format ? Config::date($k) : $k;
	}

	public function get_valid_from($format=FALSE) {
		$k = $this->_details('validFrom_time_t');

		return $format ? Config::date($k) : $k;
	}

	public function get_version() {
		return $this->_details('version');
	}

	public static function subject($cert) {
		return self::instance($cert)->get_subject();
	}

	public static function csrsubject($csr) {
		$c = openssl_csr_get_subject($csr);

		return $c['CN'];
	}
}
?>
