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

/**
 * This class supports Services
 *
 * @package    SSL
 * @category   Models
 * @author     Deon George
 * @copyright  (c) 2009-2013 Open Source Billing
 * @license    http://dev.osbill.net/license.html
 */
class Model_Service_Plugin_Ssl extends Model_Service_Plugin {
	protected $_table_name = 'service__ssl';
	protected $_updated_column = FALSE;

	// Relationships
	protected $_belongs_to = array(
		'service'=>array(),
	);
	protected $_has_one = array(
		'ca'=>array('model'=>'SSL_CA','far_key'=>'ssl_ca_id','foreign_key'=>'id'),
	);

	protected $_display_filters = array(
		'csr'=>array(
			array('SSL::csrsubject',array(':value')),
		),
		'cert'=>array(
			array('SSL::subject',array(':value')),
		),
	);

	protected $_save_message = TRUE;

	// Required abstract functions

	public function expire($format=FALSE) {
		return $this->_so->get_valid_to($format);
	}

	public function name() {
		return ($this->cert AND $this->ca->loaded()) ? sprintf('%s:%s',$this->ca->subject(),$this->display('cert')) : $this->display('csr');
	}

	public function password() {} // Not used

	public function username() {} // Not used

	// Local functions

	private $_so = NULL;

	/**
	 * Resolve any queries to certificate details
	 */
	public function __call($name,$args) {
		$m = 'get_'.$name;

		if ($this->isCSR() AND method_exists($this,$m))
			return $this->{$m}($args);

		elseif (method_exists($this->_so,$m))
			return $this->_so->{$m}($args);

		else
			throw new Kohana_Exception('Unknown method :method',array(':method'=>$name));
	}

	// We want to inject the SSL object into this Model
	protected function _load_values(array $values) {
		parent::_load_values($values);

		if ($this->cert)
			$this->_so = SSL::instance($this->cert);

		return $this;
	}

	/**
	 * Is this just a CSR?
	 */
	public function isCSR() {
		return ! $this->cert AND $this->csr;
	}

	/**
	 * Return all our CA Certs for this certificate
	 */
	public function cacerts() {
		$result = array();

		$x = $this->ssl_ca_id;
		while ($x) {
			$sco = ORM::factory('SSL_CA',$x);
			array_push($result,$sco->sign_cert);
			$x = $sco->parent_ssl_ca_id;
		}

		return $result;
	}

	public function download_button() {
		if (! $this->service->status OR ! preg_match('/client/',$this->service->product->plugin()->extensions) OR $this->valid_to() < time())
			return '';

		$output = Form::open(URL::link('user','ssl/download'),array('class'=>'form-inline'));
		$output .= Form::hidden('sid',$this->service->id);
		$output .= '<div class="input-append">';
		$output .= Form::password('passwd','',array('placeholder'=>_('Choose a password'),'required','nocg'=>TRUE,'pattern'=>'.{6,}','title'=>'Minimum 6 chars'));
		$output .= Form::button('download','Download',array('class'=>'btn btn-default','nocg'=>TRUE));
		$output .= '</div>';
		$output .= Form::close();

		return $output;
	}

	public function get_dn() {
		return SSL::csrsubject($this->csr);
	}

	public function get_valid_to() {
		// @todo
		return 'Unknown';
	}

	/**
	 * Renew an SSL Certificate
	 */
	// @todo: Renew renews the expiry date as 1 day too early.
	public function renew($force=FALSE) {
		$ssl_conf = Kohana::$config->load('ssl');

		if ($this->cert) {
			$sslo = SSL::instance($this->cert);

			// If our certificate is not old enough skip
			if ($sslo->get_valid_to() > time()+$ssl_conf['min_renew_days']*86400 AND ! $force)
				return FALSE;

		} elseif (! $this->csr) {
			throw new Kohana_Exception('Unable to renew, no CERT or CSR');
		}

		$today = mktime(0,0,0,date('n'),date('j'),date('Y'));
		$days = (int)(($this->service->invoiced_to()-$today)/86400);

		$res = openssl_csr_sign($this->csr,$this->ca->sign_cert,$this->ca->sign_pk,$days,array(
			'config'=>$ssl_conf['config'],
			'x509_extensions'=>$this->service->product->plugin()->extensions,
			'digest_alg'=>'sha1',
		),time());

		if ($res AND openssl_x509_export($res,$cert)) {
			$this->cert = $cert;
			$this->save();

			return TRUE;

		} else {
			print_r(array(
				'csr'=>$this->csr,
				'ca'=>$this->ca->sign_cert,
				'capk'=>$this->ca->sign_pk,
				'days'=>$this->service->product->plugin()->days,
				'ssl'=>$ssl_conf,
				'x509e'=>$this->service->product->plugin()->extensions
			));

			throw new Kohana_Exception('Error Creating SSL Certificate :error',array(':error'=>openssl_error_string()));
		}
	}

	public function validCA() {
		return $this->ca->validParent();
	}

	// If we change the SSL certificate, we need to reload our SSL object
	public function values(array $values, array $expected = NULL) {
		parent::values($values,$expected);

		if (array_key_exists('cert',$values))
			$this->_so = SSL::instance($this->cert);

		return $this;
	}

	/**
	 * Get specific service details for use in other modules
	 * For Example: Invoice
	 *
	 * @todo Make the rendered items configurable
	 * @todo Change this method name, now that it is public
	 */
	// @todo This needs to be validated for this model
	public function _details($type) {
		switch ($type) {
			case 'invoice_detail_items':
				return array();
				break;
			default:
				return parent::$_details($type);
		}
	}
}
?>